浏览代码

Merge remote-tracking branch 'origin/hqyDev' into feature-tianyong

TIANYONG 5 月之前
父节点
当前提交
10e8adfde4

+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit e77aa077f43f575035a4b3821baab0d1e91534c6
+Subproject commit 3689e3c4aac7182ec7cc8ed24b0953dd37c41227

+ 53 - 22
src/helpers/formateMusic.ts

@@ -1215,6 +1215,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// console.log('si',si,i)
 			// console.log(note.sourceMeasure.MeasureNumberXML,note,svgElement, NoteRealValue, measureLength)
 			if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
+				i++
 				continue;
 			}
 			// console.log(iterator.currentMeasure)
@@ -1285,39 +1286,55 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			/**
 			 * evxml的曲子,如果曲谱xml中带有times信息,则音符时值优先取times中的值
 			 * 曲子:1795013295024062466(春暖花开),如果音符有times信息,休止符没有times信息,此种规则是认为休止符不参与时值计算的,需要过滤掉该休止符
-			 * TODO:需要考虑唱名怎么处理,唱名是xml有多少个音符,就需要唱多少个,不能剔除
 			 */
-			if (state.isEvxml && note.isRestFlag && note?.noteTimeInfo?.length === 0 && state.xmlHasTimes ) {
-				const idx = _notes.findIndex(item=>item.note === note);
-				let nextNoteTimes = _notes[idx+1]?.note?.noteTimeInfo?.[0]?.begin*1000 
-				let preNoteTImes = _notes[idx-1]?.note?.noteTimeInfo?.[0]?.end*1000
-				// 当下一个音符也没有时间的时候,再往下一个找
-				if(!nextNoteTimes && nextNoteTimes!==0){
-					let nextIndex = idx + 2
-					while(!nextNoteTimes && nextIndex<_notes.length){
-						nextNoteTimes = _notes[nextIndex]?.note?.noteTimeInfo?.[0]?.begin*1000
-						nextIndex ++
-					}
-					// 当最后音符就是没有打时间的休止小节,可能nextNoteTimes时间找不到,目前没有处理
+			let evNoteStartTime = 0, evNoteEndTime = 0;
+			if (state.isEvxml && note?.noteTimeInfo?.length === 0 && state.xmlHasTimes ) {
+				// 找出这个音符前面音符的结束时间
+				let preNoteTImes = allNotes[allNotes.length - 1]?.endtime*1000
+				if(!preNoteTImes){
+					preNoteTImes = Math.max(fixtime - noteLength, 0)*1000 //如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,所以往前奏里面找补
 				}
-				if(!preNoteTImes && preNoteTImes!==0){
-					let preIndex = idx - 2
-					while(!preNoteTImes && preIndex>-1){
-						preNoteTImes = _notes[preIndex]?.note?.noteTimeInfo?.[0]?.end*1000
-						preIndex --
+				// 找出这个音符后面音符的开始时间
+				let nextI = i
+				let nextNoteTimes
+				// 多个连续的没有打时间的音符 需要平分时值
+				const notesRatio = []
+				while (!nextNoteTimes && nextI<_notes.length) {
+					notesRatio.push(_notes[nextI].note.length.realValue)
+					nextI++
+					if(_notes[nextI]?.note){ // 有可能_notes里面没有这个音符
+						nextNoteTimes = fliterNotesTime(_notes[nextI].note, preNoteTImes)
 					}
-					// 当没有找到preNoteTImes的时候 赋值为0 (当第一个音符就是没有打时间的休止小节会出现这种情况)
-					preNoteTImes || (preNoteTImes = 0)
 				}
+				// 当最后音符就是没有打时间的音符,可能nextNoteTimes时间找不到时候取上个音符的结束时间加上这个音符的时间
+				if(!nextNoteTimes){
+					nextNoteTimes = preNoteTImes + noteLength*1000
+				}
+				// 判断有没有首位连续 首位连续的时候删掉这个音符
 				const allowRange = Math.abs(nextNoteTimes - preNoteTImes)< 10;
 				if (allowRange) {
 					note.maxNoteNum = note.maxNoteNum - 1;
 					// 唱名时间补齐,当删除这个音符的时候,上个音符的持续时间要加上这个音符的时间
 					allNotes[allNotes.length - 1].noteLengthTime += noteLength
+					i++
 					continue;
+				}else{
+					// 当多个连续的休止符没有打时间的时候 根据音符平均分配
+					if(notesRatio.length > 1){
+						const sum = notesRatio.reduce((acc:number, curr:number) => acc + curr, 0)
+						nextNoteTimes = (nextNoteTimes - preNoteTImes) * notesRatio[0] / sum + preNoteTImes
+					}
+					evNoteEndTime = nextNoteTimes/1000
+					evNoteStartTime = preNoteTImes/1000
+					// 当这个音符计算出来的时值大于本身这个音符的时值时候,取这个音符的长度,防止有前奏和间奏的时候,计算音符持续时长过长
+					if(evNoteEndTime - evNoteStartTime > noteLength){
+						evNoteEndTime = evNoteStartTime + noteLength
+					}
+					if (evNoteStartTime) {
+						relativeTime = evNoteStartTime - fixtime
+					}
 				}
 			}
-			let evNoteStartTime = 0, evNoteEndTime = 0;
 			if (state.isEvxml && note?.noteTimeInfo?.length ) {
 				let idx = noteIds.filter((item: any) => item === svgElement?.attrs.id)?.length || 0;
 				// 如果是合并的小节的休止符
@@ -1387,7 +1404,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				stave,
 				firstVerticalMeasure: activeVerticalMeasureList[0],
 				noteLength: 1,
-				osdmContext: osmd,
+				//osdmContext: osmd,
 				speedbeatUnit: beatUnit,
 				multipleRestMeasures: multipleRestMeasures, // 当前合并小节的索引,从1开始到当前的totalMultipleRestMeasures结束,
 				totalMultipleRestMeasures, // 当前小节总的合并小节数
@@ -1744,4 +1761,18 @@ export const compatibleXmlPitchVoice = (xmlParse: any) => {
 		}
 		(window as any).xmlNeedAdjustVoice = xmlNeedAdjustVoice
 	}
+}
+
+// 筛选出这个音符中的值(比前一个音符时值大的值)
+function fliterNotesTime(note:any, preTime:number):undefined|number {
+	// 音符可能涉及多遍歌词,所以这里要找到对应的times
+	if(note?.noteTimeInfo?.length){
+		const timeObj = note?.noteTimeInfo.find((value:any) => {
+			const beginTime = value?.begin*1000 || 0
+			return beginTime > preTime || Math.abs(beginTime - preTime)< 10  //差值在10毫秒之内
+		})
+		return timeObj?.begin*1000
+	}else{
+		return undefined
+	}
 }

+ 39 - 4
src/helpers/metronome.ts

@@ -313,27 +313,62 @@ class Metronome {
 		// 1.统计有多少小节
 		const measures: any[] = [];
 		let xmlNumber = -1;
+		let isDiff = false //弱起时间不够补的时候
 		for (let i = 0; i < times.length; i++) {
 			const note = times[i];
-			const measureNumberXML = note?.noteElement?.sourceMeasure?.MeasureNumberXML;
+			const measureNumberXML = note.MeasureNumberXML;
 			// console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure)
 			// console.log("🚀 ~ measureNumberXML", measureNumberXML, note)
 			// console.log("🚀 ~ measureNumberXML", note)
-			const measureListIndex = note?.noteElement?.sourceMeasure?.measureListIndex;
+			const measureListIndex = measureNumberXML - 1;
 			if (measureNumberXML > -1) {
 				if (measureNumberXML != xmlNumber) {
 					// 弱起的时候 根据音符结尾时间减去音符开头时间,得到的不是正常小节的时间,然后平均分配节拍之后,当前节拍间隔会非常短 这里弱起取整个小节的时间
+					// 当小节时间 减去音符时间,前奏没有预留时间时候,从歌词开始唱的那里开始响节拍器
 					let startTime = note.measures[0].time
 					if(i === 0 && note.measures[0].difftime>0){
 						startTime = note.measures[note.measures.length - 1].endtime - note.measures[0].measureLength
+						if(startTime < 0){
+							isDiff = true
+						}
+					}
+					if(isDiff) {
+						// 当前小节有歌词,开放弱起节拍器
+						let isLyric = false
+						let noteIndex = 0
+						while(!isLyric && noteIndex<note.measures.length){
+							isLyric = !!note.measures[noteIndex]?.formatLyricsEntries?.length
+							noteIndex ++
+						}
+						isDiff = !isLyric
+					}
+					if(isDiff){
+						xmlNumber = measureNumberXML;
+						continue
+					}
+					// 最后一小节的时间 有可能和下一小节开始时间接不上
+					const { time, endtime, noteLengthTime } = note.measures[note.measures.length - 1]
+					let nextNoteStartTime = times[note.measures[note.measures.length - 1].i + 1]?.time
+					let noteEndTime = 0
+					if(!nextNoteStartTime){
+						// 当不够的时候补上时值
+						noteEndTime = time + noteLengthTime > endtime ? time + noteLengthTime : endtime
+					}else{
+						if(Math.abs(nextNoteStartTime - endtime)*1000< 10){
+							// 当首位本来就是相连的
+							noteEndTime = endtime
+						}else{
+							// 当首位不相连,差值大于这个音符的持续时间的时候取这个音符的时间(防止有间奏的连起来时间太长),否则直接取后一个音符的开始时间
+							noteEndTime = nextNoteStartTime - time > noteLengthTime ? time + noteLengthTime : nextNoteStartTime
+						}	
 					}
 					const m = {
 						measureNumberXML: measureNumberXML,
 						measureNumberIndex: measureListIndex,
 						numerator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.numerator || 0,
 						start: startTime,
-						end: note.measures[note.measures.length - 1].endtime,
-						time: note.measures[note.measures.length - 1].endtime - startTime,
+						end: noteEndTime,
+						time: noteEndTime - startTime,
 						stave_x: note?.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.x || 0,
 						end_x: note?.stave?.end_x || 0 || 0,
 						stepList: [] as number[],

+ 6 - 0
src/page-instrument/header-top/index.tsx

@@ -498,6 +498,12 @@ export default defineComponent({
       HANDLE_WORK_ADD();
       // 不在APP中,
       if (!storeData.isApp) {
+        window.parent.postMessage(
+          {
+            api: "back",
+          },
+          "*"
+        );
         window.close();
         return;
       }

+ 1 - 1
src/page-instrument/simple-detail/index.tsx

@@ -167,7 +167,7 @@ export default defineComponent({
 			state.activeNoteIndex = item?.i || 0
 			// 一行谱,需要滚动小节
 			if (state.isSingleLine) {
-				moveSmoothAnimationByPlayTime(currentTime)
+				moveSmoothAnimationByPlayTime(currentTime, true)
 			}
 		
 		};

+ 6 - 0
src/page-instrument/view-detail/emptyMusic/index.tsx

@@ -18,6 +18,12 @@ export default defineComponent({
          HANDLE_WORK_ADD()
          // 不在APP中,
          if (!storeData.isApp) {
+            window.parent.postMessage(
+               {
+                  api: "back",
+               },
+               "*"
+            );
             window.close()
             return
          }

+ 2 - 2
src/page-instrument/view-detail/index.tsx

@@ -26,7 +26,6 @@ import WorkIndex, { resetSection } from "../custom-plugins/work-index";
 import TheMusicList, { isMusicList } from "../component/the-music-list";
 import { storeData } from "/src/store";
 import ViewFigner from "../view-figner";
-import { recalculateNoteData } from "/src/view/selection";
 import ToggleMusicSheet from "/src/view/plugins/toggleMusicSheet";
 import { setCustomGradual, setCustomNoteRealValue } from "/src/helpers/customMusicScore";
 import { usePageVisibility } from "@vant/use";
@@ -39,6 +38,7 @@ import EmptyMusic, { isEmptyMusicShow } from "./emptyMusic";
 import { position } from "html2canvas/dist/types/css/property-descriptors/position";
 import Loading from "./loading"
 import ExerciseStatistics from "../custom-plugins/ExerciseStatistics"
+import { musicData } from "/src/view/music-score"
 // import bgJson from "./images/index.json";
 
 // const DelayCheck = defineAsyncComponent(() =>
@@ -545,7 +545,7 @@ export default defineComponent({
               onRendered={handleOnRendered} 
             > 
               {/* 旋律线关闭时候的 标题和作者 */}
-              <AuthorName></AuthorName>
+              { musicData.showSelection && <AuthorName></AuthorName>}
               {/* 节拍器 */}
               {!detailData.isLoading && !detailData.skeletonLoading && <Tick />}
             </MusicScore>

+ 12 - 4
src/page-instrument/view-detail/smoothAnimation/bird/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent } from "vue"
+import { defineComponent, onBeforeUnmount, ref } from "vue"
 import { Vue3Lottie } from "vue3-lottie";
 import styles from "./index.module.less"
 import state from "/src/state"
@@ -9,11 +9,19 @@ import note from "./imgs/note.json"
 export default defineComponent({
     name: "Bird",
     setup() {
+        const vue3LottieDom = ref()
+        const vue3Lottie1Dom = ref()
+        const vue3Lottie2Dom = ref()
+        onBeforeUnmount(()=>{
+            vue3LottieDom.value?.destroy()
+            vue3Lottie1Dom.value?.destroy()
+            vue3Lottie2Dom.value?.destroy()
+        })
         return () => <>
-                <Vue3Lottie style={ { display : state.playState === "paused" ? "" : "none" } } key={1} class={styles.brid} animationData={stilln} autoPlay={true} loop={true}></Vue3Lottie>
+                <Vue3Lottie ref={vue3LottieDom} style={ { display : state.playState === "paused" ? "" : "none" } } key={1} class={styles.brid} animationData={stilln} autoPlay={true} loop={true}></Vue3Lottie>
                 <>
-                    <Vue3Lottie style={ { display : state.playState === "paused" ? "none" : "" } } key={2} class={styles.brid} animationData={fly} autoPlay={true} loop={true}></Vue3Lottie>
-                    <Vue3Lottie style={ { display : state.playState === "paused" ? "none" : "" } } key={2} class={styles.note} animationData={note} autoPlay={true} loop={true}></Vue3Lottie>
+                    <Vue3Lottie ref={vue3Lottie1Dom} style={ { display : state.playState === "paused" ? "none" : "" } } key={2} class={styles.brid} animationData={fly} autoPlay={true} loop={true}></Vue3Lottie>
+                    <Vue3Lottie ref={vue3Lottie2Dom} style={ { display : state.playState === "paused" ? "none" : "" } } key={2} class={styles.note} animationData={note} autoPlay={true} loop={true}></Vue3Lottie>
                 </>
         </>
     }

+ 22 - 9
src/page-instrument/view-detail/smoothAnimation/index.ts

@@ -26,7 +26,9 @@ type smoothAnimationType = {
    batePos: pointsPosType
    pointsPos: pointsPosType
    translateXNum: number
-   aveSpeed: number
+   aveSpeed: number,
+   pageTurnLock: boolean
+   oldCurrentTime: number
 }
 
 let _numberOfSegments = 56 // 中间切割线的个数
@@ -50,7 +52,9 @@ export const smoothAnimationState = {
    batePos: [], // times 直接转换的数组
    pointsPos: [], // 筛选之后的点坐标数组
    translateXNum: 0, // 当前谱面的translateX的距离   谱面的位置信息 由translateX和scrollLeft的偏移一起决定
-   aveSpeed: 0 // 谱面的一帧的平均速度
+   aveSpeed: 0, // 谱面的一帧的平均速度
+   pageTurnLock: false,
+   oldCurrentTime: 0
 } as smoothAnimationType
 
 // 监听显示与隐藏
@@ -133,6 +137,8 @@ function dataFindIndex(activeIndex: number, progress: number) {
 export function destroySmoothAnimation() {
    smoothAnimationState.isShow.value = false
    window.removeEventListener("resize", calcClientWidth)
+   // 销毁组件 防止内存泄漏
+   smoothAnimationState.smoothBotDom && render(null, smoothAnimationState.smoothBotDom)
    smoothAnimationState.smoothAnimationBoxDom?.remove()
    Object.assign(smoothAnimationState, {
       canvasDom: null,
@@ -151,19 +157,27 @@ export function destroySmoothAnimation() {
       batePos: [],
       pointsPos: [],
       translateXNum: 0,
-      aveSpeed: 0
+      aveSpeed: 0,
+      pageTurnLock: false,
+      oldCurrentTime: 0
    })
 }
 
 /**
  * 根据播放时间进度移动处理
+ * isIgnoreFilter  忽略这种 判断,因为有些只需要谱面移动
  */
-export function moveSmoothAnimationByPlayTime(time?: number) {
+export function moveSmoothAnimationByPlayTime(time?: number, isIgnoreFilter = false) {
    // 暂停之后不进行移动了
    if (state.playState === "paused") {
       return
    }
    const currentTime = time || getAudioCurrentTime()
+   // 某些浏览器 音频暂停后返回的时间会倒退,把这种时间过滤掉
+   if(currentTime < smoothAnimationState.oldCurrentTime && !isIgnoreFilter) {
+      return
+   }
+   smoothAnimationState.oldCurrentTime = currentTime
    if (currentTime <= state.fixtime) return
    if (currentTime > state.times.last()?.endtime) return
    // 当休止小节,可能当前音符在谱面上没有实际的音符(没有bbox),所以往后找谱面上有的音符
@@ -221,9 +235,8 @@ export function moveSmoothAnimation(progress: number, activeIndex: number, isMov
 /**
  * 谱面翻页逻辑
  */
-let pageTurnLock = false
 function pageTurn_osmd(nowPointsPos: pointsPosType[0]) {
-   if (pageTurnLock) return
+   if (smoothAnimationState.pageTurnLock) return
    // 视口宽度
    const clientWidth = smoothAnimationState.osdmScrollDomWith
    let { left, right } = smoothAnimationState.smoothBotDom!.getBoundingClientRect()
@@ -243,7 +256,7 @@ function pageTurn_osmd(nowPointsPos: pointsPosType[0]) {
          if (smoothAnimationState.translateXNum > maxTranslateXNum) {
             smoothAnimationState.translateXNum = maxTranslateXNum
          }
-         pageTurnLock = true
+         smoothAnimationState.pageTurnLock = true
          moveTranslateXNum(smoothAnimationState.translateXNum)
       }
    }
@@ -330,7 +343,7 @@ export function moveTranslateXNum(translateXNum: number) {
       smoothAnimationState.osmdCanvasPageDom && (smoothAnimationState.osmdCanvasPageDom.style.transition = "")
       smoothAnimationState.selectionBoxDom && (smoothAnimationState.selectionBoxDom.style.transition = "")
       smoothAnimationState.selectionBgBoxDom && (smoothAnimationState.selectionBgBoxDom.style.transition = "")
-      pageTurnLock = false
+      smoothAnimationState.pageTurnLock = false
    } else {
       smoothAnimationState.osmdCanvasPageDom && (smoothAnimationState.osmdCanvasPageDom.style.transform = `translateX(-${translateXNum}px)`)
       smoothAnimationState.selectionBoxDom && (smoothAnimationState.selectionBoxDom.style.transform = `translateX(-${translateXNum}px)`)
@@ -357,7 +370,7 @@ function createSmoothAnimation() {
    const osmdCanvasPageDom = document.querySelector("#osmdCanvasPage1") as HTMLElement
    smoothAnimationState.osmdCanvasPageDom = osmdCanvasPageDom
    smoothAnimationState.osmdCanvasPageDom.addEventListener("transitionend", () => {
-      pageTurnLock = false
+      smoothAnimationState.pageTurnLock = false
    })
    // selectionBox
    setTimeout(() => {

+ 2 - 0
src/state.ts

@@ -779,6 +779,8 @@ const handlePlaying = () => {
 export const skipNotePlay = async (itemIndex: number, isStart = false, handType?: string) => {
   // 纯预览模式,练习、评测作业模式,禁止手动点击跳转音符
   if (state.isPreView) return;
+  // 点击或者重播的时候清除一行谱的时间信息
+  state.isSingleLine && (smoothAnimationState.oldCurrentTime = 0)
   if (handType === 'manual' && (query.workRecord || query.evaluatingRecord)) return;
   const item = state.times[itemIndex];
   // 如果是选段状态,可以点击段落范围内的音符,从当前音符开始播放,如果不是段落内的音符,直接return

+ 39 - 50
src/view/music-score/index.tsx

@@ -14,13 +14,9 @@ import HorizontalDragScroll from './HorizontalDragScroll';
 import { getQuery } from "/src/utils/queryString";
 
 export const musicRenderTypeKey = "musicRenderType";
-let osmd: any = null;
-const musicData = reactive({
+export const musicData = reactive({
 	showSelection: false, // 可以加载点击浮层
-	isRenderLoading: true,
-	score: "",
-	containerWidth: 0,
-	isRefreshLoading: false,
+	score: ""
 });
 
 /** 重新渲染曲谱 */
@@ -63,6 +59,7 @@ export default defineComponent({
 	},
 	setup(props, { emit, slots, expose }) {
 		const query: any = getQuery();
+		let osmd: any = null;
 		/** 设置 曲谱模式,五线谱还是简谱 */
 		const setRenderType = () => {
 			const musicRenderType: any = sessionStorage.getItem(props.renderTypeKey || musicRenderTypeKey);
@@ -88,29 +85,33 @@ export default defineComponent({
 			const container = document.getElementById("musicAndSelection");
 			if (!container || !musicData.score) return;
 			setGlobalMusicSheet();
-			osmd = new OpenSheetMusicDisplay(container, {
-				drawTitle: false,
-				drawSubtitle: false,
-				// drawMeasureNumbers: false,
-				autoResize: false,
-				followCursor: false,
+			if(!osmd){
+				osmd = new OpenSheetMusicDisplay(container, {
+					drawTitle: false,
+					drawSubtitle: false,
+					// drawMeasureNumbers: false,
+					autoResize: false,
+					followCursor: false,
+					drawLyricist: false, // 渲染作曲家
+					drawComposer: false, // 渲染作词家
+					// pageBackgroundColor: '#609FCF',
+					// autoGenerateMultipleRestMeasuresFromRestMeasures: state.isSingleLine ? false : true, // 连续休止小节是否合并显示
+					autoGenerateMultipleRestMeasuresFromRestMeasures: true,
+					// darkMode: true, // 暗黑模式
+					// pageFormat: 'A4_P',
+					// autoBeam: true,
+					// drawMetronomeMarks: false,
+					// ...this.opotions,
+					colorStemsLikeNoteheads: true, // 是否将音符柄的颜色设置为与它们的音符头相同,默认false
+					// drawingParameters: "compact" // 使用紧凑布局
+				});
+			}
+			osmd.setOptions({
+				drawLyrics: (((!state.accompany && !state.music ) || state.playType === 'sing' || !state.isEvxml) && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
 				drawPartNames: props.showPartNames, // 是否渲染声轨名称
-				drawLyricist: false, // 渲染作曲家
-				drawComposer: false, // 渲染作词家
 				defaultColorMusic: props.musicColor, // 颜色
-				// pageBackgroundColor: '#609FCF',
-				renderSingleHorizontalStaffline: state.isSingleLine ? true : false,
-				// autoGenerateMultipleRestMeasuresFromRestMeasures: state.isSingleLine ? false : true, // 连续休止小节是否合并显示
-				autoGenerateMultipleRestMeasuresFromRestMeasures: true,
-				drawLyrics: ( ((!state.accompany && !state.music ) || state.playType === 'sing' || !state.isEvxml) && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
-				// darkMode: true, // 暗黑模式
-				// pageFormat: 'A4_P',
-				// autoBeam: true,
-				// drawMetronomeMarks: false,
-				// ...this.opotions,
-				colorStemsLikeNoteheads: true, // 是否将音符柄的颜色设置为与它们的音符头相同,默认false
-				// drawingParameters: "compact" // 使用紧凑布局
-			});
+				renderSingleHorizontalStaffline: state.isSingleLine ? true : false
+			})
 			// osmd.EngravingRules.CompactMode = true // 紧凑模式
 			// osmd.EngravingRules.PageRightMargin = state.isSingleLine ? (window.innerWidth+200)/10 : 2;
 			// osmd.EngravingRules.FixedMeasureWidth = state.isSingleLine ? true : false; // 是否固定小节的宽度(小节同一宽度渲染)
@@ -174,18 +175,11 @@ export default defineComponent({
 			resetGivenFormate();
 			musicData.showSelection = true;
 		};
-		/** 获取渲染容器的宽度 */
-		const getContainerWidth = () => {
-			musicData.containerWidth = document.getElementById("musicAndSelection")?.offsetWidth || 625;
-			// console.log(musicData.containerWidth)
-		};
 		let horizontalDragScroll:HorizontalDragScroll | null
 		onMounted(async () => {
-			getContainerWidth();
 			//setRenderType();
 			await getXML();
 			await init();
-			musicData.isRenderLoading = false;
 			// pc 端支持 拖动滚动
 			if(state.platform === "PC" || query.isCbs){
 				const container = document.querySelector('#musicAndSelection') as HTMLElement;
@@ -209,34 +203,29 @@ export default defineComponent({
 			}
 			return result;
 		});
-
 		/** 刷新曲谱 */
 		const refreshMusicScore = () => {
-			const container = document.getElementById('musicAndSelection'), svgDom = document.getElementById('osmdCanvasPage1'), selectionBox = document.getElementById('selectionBox'), selectionBgBox = document.getElementById('selectionBgBox');
+			state.loadingText = "正在加载中,请稍等..."
+			state.isLoading = true
+			state.evXmlBeginArr = [];
+			state.vfmeasures = [];
+			musicData.showSelection = false;
+			state.osmd.clear();
+			const container = document.getElementById('musicAndSelection'), svgDom = document.getElementById('osmdCanvasPage1');
 			if (container) {
+				//这里需要删除osmd,不然多行谱和一行谱切换 滚动高度会出问题
 				svgDom && container?.removeChild(svgDom)
-				selectionBox && container?.removeChild(selectionBox)
-				selectionBgBox && container?.removeChild(selectionBgBox)
 			}
 			// 有可能会有 其他地方的js执行 阻塞 这里确保加载条出来
-			state.loadingText = "正在加载中,请稍等..."
-			state.isLoading = true
 			setTimeout(async () => {
-				state.evXmlBeginArr = [];
-				state.vfmeasures = [];
-				musicData.showSelection = false;
-				state.osmd.clear();
-				musicData.isRenderLoading = true;
-				musicData.isRefreshLoading = true;
-				getContainerWidth();
+				// 在滚动过程中(雄鹰高飞这种marginbottom比较大的) 多行谱和一行谱切换 滚动高度会出问题
+				container && (container.scrollTop = 0)
 				//setRenderType();
 				await getXML();
 				await init();
-				musicData.isRenderLoading = false;
-				musicData.isRefreshLoading = false;
 				musicData.showSelection = true;
 				state.isLoading = false
-			}, 120);
+			}, 60);
 		}
 		expose({
 			refreshMusicScore,

+ 0 - 7
src/view/music-score/testCheck.tsx

@@ -14,7 +14,6 @@ export const musicRenderTypeKey = "musicRenderType";
 let osmd: any = null;
 const musicData = reactive({
 	showSelection: false, // 可以加载点击浮层
-	isRenderLoading: true,
 	score: "",
 	containerWidth: 0,
 });
@@ -131,13 +130,7 @@ export default defineComponent({
             container.innerHTML = "";
 			
 		};
-		/** 获取渲染容器的宽度 */
-		const getContainerWidth = () => {
-			musicData.containerWidth = 625;
-			// console.log(musicData.containerWidth)
-		};
 		onMounted(async () => {
-			getContainerWidth();
             state.isEvxml = true
             // checkRender(xmlList2);
 		});

+ 172 - 180
src/view/selection/index.tsx

@@ -13,207 +13,199 @@ import IntonationUp from "./imgs/pitchHigh.png"
 import MultipleRestMeasures from "./multipleRestMeasures"
 import { browser } from "../../utils";
 
-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,
+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,
 		};
-		if (!notesList.includes(item.noteId)) {
-			let staveBbox: any = {};
-			if (item.stave?.attrs?.id) {
-				const staveEle = document.querySelector(`#${item.stave.attrs.id}`);
-				staveBbox = staveEle?.parentElement?.parentElement?.getBoundingClientRect?.() || {
-					x: 0,
-					width: 0,
+		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,
 				};
-				// 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
+				if (!notesList.includes(item.noteId)) {
+					let staveBbox: any = {};
+					if (item.stave?.attrs?.id) {
+						const staveEle = document.querySelector(`#${item.stave.attrs.id}`);
+						staveBbox = staveEle?.parentElement?.parentElement?.getBoundingClientRect?.() || {
+							x: 0,
+							width: 0,
 						};
-						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
+						// 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 {
+								noteItem.bbox = {
+									left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px",
+									top: 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
+								};
 							}
+							
 						}
-						
-					} else {
-						noteItem.bbox = {
-							left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px",
-							top: 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
-						};
-					}
-					
-				}
-				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");
-							});
+						if (selectData.notes.find((item:any) => item.id === noteItem.id)) {
+							// 
+						} else {
+							selectData.notes.push(noteItem);
 						}
-					} catch (error) {}
-
-					const staveBbox = staveEle?.getBoundingClientRect?.() || { x: 0, width: 0, y: 0, height: 0 };
-					if (i === 0) {
-						minMeasureHeigt = staveBbox.height
+						// selectData.notes.push(noteItem);
+						notesList.push(item.noteId);
 					}
-					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;
+		
+				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
 							}
-						}
-						const staveBbox = currentItem?.querySelector('.vf-stave')?.getBoundingClientRect() || { x: 0, width: 0, y: 0, height: 0 };
-						if (currentItem) {
+							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  + "px",
+								top: staveBbox.y - parentTop + compareVal + "px",
 								width: staveBbox.width + "px",
-								height: staveBbox.height + "px",
-								// height: preItem.staveBox.height,
+								height: staveBbox.height - compareVal + "px",
+								// background: 'rgba(0,0,0,.2)'
 							};
 							selectData.staves.push(noteItem);
-							MeasureNumberXMLList.push(item.MeasureNumberXML);
 						}
+						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 (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);
-};
-
-/** 重新计算 */
-export const recalculateNoteData = () => {
-	selectData.notes = [];
-	selectData.staves = [];
-	calcNoteData();
-};
-
-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,
+			// 部分浏览器渲染的第一小节的位置信息会包含拍号、调号,需要处理一下,剔除掉拍号、调号的位置
+			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(() => {