import { computed, defineComponent, onMounted, reactive, Transition, nextTick, watch } from "vue"; import state, { EnumMusicRenderType, handleSelection, skipNotePlay, IPlatform, resetBaseRate } from "/src/state"; import styles from "./index.module.less"; import { metronomeData } from "/src/helpers/metronome"; import { evaluatingData } from "../evaluating"; import { leveByScoreMeasureIcons } from "../evaluating/evaluatResult"; import { Icon, showToast } from "vant"; import MoveMusicScore, { moveData, renderForMoveData } from "../plugins/move-music-score"; import { useRoute } from "vue-router"; import { getQuery } from "/src/utils/queryString"; import IntonationDown from "./imgs/pitchLow.png" import IntonationUp from "./imgs/pitchHigh.png" import MultipleRestMeasures from "./multipleRestMeasures" import { browser } from "../../utils"; import { transform } from "lodash"; export default defineComponent({ name: "selection", setup() { const browsInfo = browser(); const isPad = navigator?.userAgent?.includes("UAWEIVRD-W09") || browsInfo?.iPad || browsInfo.isTablet; const route = useRoute(); const query: any = { ...getQuery(), ...route.query, }; const selectData = reactive({ notes: [] as any[], staves: [] as any[], measureHeight: 0 as number, // 小节高度 }); /** 计算点击层数据 */ const calcNoteData = () => { const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || { x: 0, y: 0, }; const parentLeft = musicContainer.x || 0; const parentTop = musicContainer.y || 0; const notes = state.times; const notesList: string[] = []; const MeasureNumberXMLList: number[] = []; let minMeasureHeigt: number = 0; for (let i = 0; i < notes.length; i++) { const item = notes[i]; // console.log("🚀 ~ item:", item) const noteItem = { ...item, index: item.i, bbox: null as any, staveBox: null as any, }; if (!notesList.includes(item.noteId)) { let staveBbox: any = {}, customBgBox: any = {}; if (item.stave?.attrs?.id) { const staveEle = document.querySelector(`#${item.stave.attrs.id}`); staveBbox = staveEle?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0, width: 0, }; customBgBox = staveEle?.querySelector('.vf-custom-bg')?.getBoundingClientRect() || { y: 0, height: 0 } // console.log("🚀 ~ staveBbox:", staveBbox.height) } if (item.svgElement) { const noteEle = document.querySelector(`#vf-${item.svgElement?.attrs?.id}`); if (noteEle) { const noteBbox = noteEle.getBoundingClientRect?.() || { x: 0, width: 0 }; if (state.musicRenderType !== EnumMusicRenderType.staff) { noteItem.bbox = { left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px", top: noteBbox.y - parentTop - noteBbox.height + "px", width: noteBbox.width * 1.5 + "px", height: noteBbox.height * 3 + "px", x: item.bbox?.x, y: item.bbox?.y, originWidth: item.bbox?.width }; const noteHead = noteEle.querySelector(".vf-numbered-note-head"); const noteHeadBbox = noteHead?.getBoundingClientRect?.(); if (noteHeadBbox) { item.bbox = { left: noteHeadBbox.x - parentLeft - noteHeadBbox.width / 4, width: noteHeadBbox.width * 1.5, x: item.bbox?.x, y: item.bbox?.y, originWidth: item.bbox?.width } } } else { const needTransY = -(staveBbox.height - customBgBox.height) / 2 + "px"; noteItem.bbox = { left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px", top: customBgBox.y ? customBgBox.y - parentTop + "px" : staveBbox.y - parentTop + "px", width: noteBbox.width * 1.5 + "px", height: staveBbox.height + "px", x: item.bbox?.x, y: item.bbox?.y, originWidth: item.bbox?.width, transform: `translateY(${needTransY})` }; } } if (selectData.notes.find((item:any) => item.id === noteItem.id)) { // } else { selectData.notes.push(noteItem); } // selectData.notes.push(noteItem); notesList.push(item.noteId); } } if (!MeasureNumberXMLList.includes(item.MeasureNumberXML)) { if (item.stave) { if (item.stave?.attrs?.id) { const staveEle = document.querySelector(`#${item.stave.attrs.id}`); const list = [ Array.from(staveEle?.querySelectorAll(".vf-clef") || []), Array.from(staveEle?.querySelectorAll(".vf-keysignature") || []), Array.from(staveEle?.getElementsByTagName("text") || []), ].flat(); try { if (list.length) { // console.log("🚀 ~ list:", list) list.forEach((_el: any) => { _el?.style?.setProperty("display", "none"); }); } } catch (error) {} const staveBbox = staveEle?.getBoundingClientRect?.() || { x: 0, width: 0, y: 0, height: 0 }; if (i === 0) { minMeasureHeigt = staveBbox.height } try { if (list.length) { list.forEach((_el: any) => { _el?.style?.removeProperty("display"); }); } } catch (error) {} // console.log("🚀 ~ staveEle:", staveBbox.height) selectData.measureHeight = staveBbox.height let compareVal = staveBbox.height - minMeasureHeigt compareVal = compareVal > 0 ? compareVal : 0 noteItem.staveBox = { left: staveBbox.x - parentLeft + "px", // top: ((item.stave.y || 0) - 5) * state.zoom + "px", top: staveBbox.y - parentTop + compareVal + "px", width: staveBbox.width + "px", height: staveBbox.height - compareVal + "px", // background: 'rgba(0,0,0,.2)' }; selectData.staves.push(noteItem); } MeasureNumberXMLList.push(item.MeasureNumberXML); } else { if (item.multipleRestMeasures) { if (state.isCombineRender) { let currentItem = null; for (let index = 0; index < state.vfmeasures.length; index++) { const element = state.vfmeasures[index]; const measureNum = element.getAttribute('data-num') ? Number(element.getAttribute('data-num')) : -1; const nextMeasureNum = state.vfmeasures[index+1]?.getAttribute('data-num') ? Number(state.vfmeasures[index+1]?.getAttribute('data-num')) : -1; if (measureNum === item.MeasureNumberXML || item.MeasureNumberXML < nextMeasureNum || nextMeasureNum == -1) { currentItem = element break; } } const staveBbox = currentItem?.querySelector('.vf-stave')?.getBoundingClientRect() || { x: 0, width: 0, y: 0, height: 0 }; if (currentItem) { noteItem.staveBox = { left: staveBbox.x - parentLeft + "px", // top: ((item.stave.y || 0) - 5) * state.zoom + "px", top: staveBbox.y - parentTop + "px", width: staveBbox.width + "px", height: staveBbox.height + "px", // height: preItem.staveBox.height, }; selectData.staves.push(noteItem); MeasureNumberXMLList.push(item.MeasureNumberXML); } } else { const preItem = selectData.staves.find( (n: any) => n.MeasureNumberXML === item.MeasureNumberXML - 1 ); if (preItem?.staveBox) { noteItem.staveBox = { left: preItem.staveBox.left, top: preItem.staveBox.top, width: preItem.staveBox.width, // height: preItem.staveBox.height, }; selectData.staves.push(noteItem); MeasureNumberXMLList.push(item.MeasureNumberXML); } } } } } } // 部分浏览器渲染的第一小节的位置信息会包含拍号、调号,需要处理一下,剔除掉拍号、调号的位置 if (selectData.staves[0]?.staveBox?.top !== selectData.staves[1]?.staveBox?.top) { selectData.staves[0].staveBox.top = selectData.staves[1]?.staveBox?.top || selectData.staves[0]?.staveBox?.top } console.log("🚀 ~ selectData.notes:", selectData.notes, selectData.staves); }; /** 是否可以点击音符 */ const disableClickNote = computed(() => { return (state.sectionStatus && state.section.length != 2) || (state.modeType === "evaluating"); }); // 选段符号 const sectionPosData = computed(() => { if(state.sectionStatus) { return state.section.map(((item,index) => { if(index === 0){ const currItem = selectData.staves.find(stave => { return stave.MeasureNumberXML === item.MeasureNumberXML }) // 获取stave里面vf-custom-bg的位置坐标,才是准确的坐标 const currBgX = document.getElementById(currItem.stave.attrs.id)?.querySelector('.vf-custom-bg')?.getBoundingClientRect()?.x || 0; return currItem && { left: currBgX ? currBgX + 'px' : currItem.staveBox.left, top: currItem.staveBox.top, height: selectData.measureHeight + 'px' // 小节的高度 } } else { // 实际的结束位置 const actualEndIndex = state.userChooseEndIndex > item.MeasureNumberXML ? state.userChooseEndIndex : item.MeasureNumberXML const currItem = selectData.staves.find(stave => { return stave.MeasureNumberXML === actualEndIndex }) return currItem && { left: parseFloat(currItem.staveBox.left)+parseFloat(currItem.staveBox.width)-2 +"px", top: currItem.staveBox.top, height: selectData.measureHeight + 'px' } } })) } return [] }) onMounted(() => { selectData.notes = []; selectData.staves = []; calcNoteData(); const img: HTMLElement = document.querySelector('#cursorImg-0')! if (metronomeData.cursorMode === 2){ img.classList.add('lineHide') } else { img.classList.remove('lineHide') } // 初始化谱面可移动的元素位置 try { moveData.partIndex = state.partIndex + "" // 速度标记元素和谱面并非同时渲染,初始化可移动元素的时候,需要加个延迟 setTimeout(() => { renderForMoveData() }, 0); } catch (error) {} }); return () => ( <>
e.stopPropagation()} > {selectData.staves.map((item: any, index) => { // 评测得分 const scoreItem = item.id && evaluatingData.evaluatings[item.measureListIndex]; // for(let idx in evaluatingData.evaluatings) { // const { show, measureIndex } = evaluatingData.evaluatings[idx] // if (show && measureIndex !== item.measureListIndex) { // evaluatingData.evaluatings[idx].show = false // } // } // 高级模式下,显示节拍线 // 不是报告模式 // 不是多小节休止符 // 节拍线开关 // 当前小节 // 当前小节 /* 节拍指针,现在没有节拍器指针了,但是以后要加上的话,这里需要性能优化。现在这样每次节拍指针更新都会刷新这里的虚拟dom */ const lineShow = !state.isReport && metronomeData.cursorMode === 2 && item.MeasureNumberXML === metronomeData.activeMetro?.measureNumberXML && state.times[state.activeNoteIndex].MeasureNumberXML === item.MeasureNumberXML; //console.log('显示节拍指针',lineShow,state.times[state.activeNoteIndex].MeasureNumberXML,item.MeasureNumberXML,metronomeData.activeMetro?.measureNumberXML) return ( <> {item.staveBox && (
0.8) ? styles.linePC : '', ]} style={item.staveBox} onClick={() => handleSelection(item)} > {lineShow && (
)} {!state.isReport && !!item.multipleRestMeasures && } { scoreItem.show = false; }} > {scoreItem?.show && (
{scoreItem.score}
)}
)} ); })} {selectData.notes.map((item: any) => { return (
skipNotePlay(item.index, false, 'manual')} > {/*
*/}
{/* */}
{/* 音准高了 */}
{/* 音准低了 */}
); })} {/* 选段 */} { sectionPosData.value.map((item,index) =>{ return ( item &&
0&&styles.selectHandleRight,(state.playState==="play" || state.isHomeWork)&&styles.playIng]} onClick={()=>{ // 如果选择了2个 删除左边的时候 if (state.section.length===1&&index === 0) { // #bug:11552 resetBaseRate(state.activeNoteIndex); } if(state.section.length===2&&index === 0){ state.section = [] // 重置速度和播放倍率 resetBaseRate(state.activeNoteIndex); showToast({ message: "请选择开始小节", duration: 0, position: "top", className: "selectionToast", }); }else{ state.section.splice(index,1) state.section = [...state.section] // 触发 watch showToast({ message: state.section.length?"请选择结束小节":"请选择开始小节", duration: 0, position: "top", className: "selectionToast", }); } }}>
) }) } {/* 移动模块 */} {query.isMove == "1" && }
); }, });