Kaynağa Gözat

feat: 增加一行谱模式

TIANYONG 1 yıl önce
ebeveyn
işleme
3350f033ac

+ 32 - 1
src/helpers/formateMusic.ts

@@ -822,6 +822,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	// 是否是变速的曲子
 	const hasVaryingSpeed = _notes.some((item: any) => item.measuresTempoInBPM !== _notes[0].measuresTempoInBPM)
 	console.log('变速曲子',hasVaryingSpeed)
+	let voicesBBox: any = null;
 	for (let { note, iterator, currentTime, isDouble, isMutileSubject } of _notes) {
 		if (note) {
 			if (preMeasureNumber != note?.sourceMeasure?.MeasureNumberXML) {
@@ -1028,6 +1029,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				} else {
 					if (difftime > 0) {
 						fixtime += difftime;
+						state.fixtime = fixtime;
 					}
 				}
 				// 管乐迷 diff获取不准确时, 弱起补齐
@@ -1052,6 +1054,33 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// console.log(note.tie)
 			// console.log('👀看看endtime', duration, relaEndtime, fixtime, i)
 			// console.log('频率',note?.pitch?.frequency,i)
+			
+			/**
+			 * 兼容合并休止小节没有音符的情况,将合并的小节宽度等分,音符位置就在等分的位置
+			 */
+			let bbox: any = null;
+			if (svgElement?.attrs.id) {
+				bbox = document.getElementById(`vf-${svgElement?.attrs?.id}`)?.getBoundingClientRect();
+			} else {
+				// @ts-ignore
+				let currentVoicesBBox: any = document.getElementById(`${stave?.attrs?.id}`)?.nextSibling?.getBoundingClientRect();
+				if (!currentVoicesBBox && multipleRestMeasures <= totalMultipleRestMeasures) {
+					currentVoicesBBox = voicesBBox;
+				}
+				const ratioWidth = currentVoicesBBox?.width / (totalMultipleRestMeasures + 1) || 0;
+				bbox = currentVoicesBBox ? {
+					bottom: currentVoicesBBox.bottom,
+					height: 30,
+					left: currentVoicesBBox.left + ratioWidth * multipleRestMeasures,
+					right: currentVoicesBBox.right,
+					top: currentVoicesBBox.top,
+					width: 1,
+					x: currentVoicesBBox.x + ratioWidth * multipleRestMeasures,
+					y: currentVoicesBBox.y
+				} : null;
+				voicesBBox = currentVoicesBBox;
+			}
+
 			const nodeDetail = {
 				isStaccato: note.voiceEntry.isStaccato(),
 				isRestFlag: note.isRestFlag,
@@ -1094,9 +1123,11 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				noteLength: 1,
 				osdmContext: osmd,
 				speedbeatUnit: beatUnit,
-				multipleRestMeasures: multipleRestMeasures,
+				multipleRestMeasures: multipleRestMeasures, // 当前合并小节的索引,从1开始到当前的totalMultipleRestMeasures结束,
+				totalMultipleRestMeasures, // 当前小节总的合并小节数
 				measureSpeed,  // 小节速度
 				maxNoteNum: note.maxNoteNum, // 当前小节音符最多的分轨的音符数量
+				bbox,
 			};
 			nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
 			nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;

+ 3 - 0
src/page-instrument/view-detail/index.module.less

@@ -171,6 +171,9 @@
         #cursorImg-0 {
             display: none;
         }
+        .staveBox {
+            display: none !important;
+        }
     }
 
 }

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

@@ -2,7 +2,7 @@ import { Popup, Skeleton } from "vant";
 import { computed, defineComponent, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, Transition, watch, watchEffect, defineAsyncComponent } from "vue";
 import { formateTimes } from "../../helpers/formateMusic";
 import Metronome, { metronomeData } from "../../helpers/metronome";
-import state, { EnumMusicRenderType, evaluatCreateMusicPlayer, handleSetSpeed, IAudioState, IPlatform, isRhythmicExercises, resetPlaybackToStart, togglePlay, getMusicDetail } from "/src/state";
+import state, { EnumMusicRenderType, evaluatCreateMusicPlayer, handleSetSpeed, IAudioState, IPlatform, isRhythmicExercises, resetPlaybackToStart, togglePlay, getMusicDetail, calculateDistance } from "/src/state";
 import { browser, setGlobalData } from "../../utils";
 import AudioList from "../../view/audio-list";
 import MusicScore, { resetMusicScore } from "../../view/music-score";
@@ -162,6 +162,7 @@ export default defineComponent({
       setCustomGradual();
 			setCustomNoteRealValue();
       state.times = formateTimes(osmd);
+      calculateDistance();
       // state.times = resetFrequency(state.times);
       state.times = setNoteHalfTone(state.times);
       console.log("🚀 ~ state.times:", state.times, state.subjectId, state);

+ 73 - 8
src/state.ts

@@ -451,6 +451,8 @@ const state = reactive({
   fistNoteLeft: 0,
   /** 是否为单行谱渲染模式 */
   isSingleLine: false,
+  /** 首尾音符的间距 */
+  noteDistance: 0,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -530,7 +532,7 @@ const handlePlaying = () => {
   const duration = getAudioDuration();
   state.playProgress = (currentTime / duration) * 100;
   let item = getNote(currentTime);
-  // console.log(11111,currentTime,duration,state.playSource, item.i)
+  // console.log(11111,currentTime,duration,state.playSource, item)
   // console.log(item.i,item.noteId,item.measureSpeed)
   // 练习模式下,实时刷新小节速度
   if (item && state.modeType === "practise" && state.playState === "play" && item.measureSpeed && item.measureSpeed !== state.playIngSpeed) {
@@ -597,6 +599,11 @@ const handlePlaying = () => {
   //   metronomeData.metro?.sound(currentTime);
   // }
   metronomeData.metro?.sound(currentTime);
+  // 一行谱,需要滚动小节
+  if (state.isSingleLine) {
+    smoothMoveSvgDom();
+  }
+  
 };
 /** 跳转到指定音符开始播放 */
 export const skipNotePlay = async (itemIndex: number, isStart = false) => {
@@ -776,7 +783,7 @@ export const gotoNext = (note: any) => {
   }
   // 一行谱,需要滚动小节
   if (state.isSingleLine) {
-    moveSvgDom(50);
+    moveSvgDom();
   }
   scrollViewNote();
 };
@@ -1217,18 +1224,76 @@ export const followBeatPaly = () => {
   });
 };
 
-let moveX = 0;
-/** 移动svgdom */
-export const moveSvgDom = (offset: number) => {
-  moveX += offset;
-  // state.osmdSvgDom.style.transform = `translateX(${moveX}px)`;
+/** 计算首尾音符的间距 */
+export const calculateDistance = () => {
+  const firstNoteRect = document.getElementById(`vf-${state.times[0]?.svgElement?.attrs?.id}`)?.getBoundingClientRect();
+  const lastNoteRect = document.getElementById(`vf-${state.times.last()?.svgElement?.attrs?.id}`)?.getBoundingClientRect();
+  if (firstNoteRect && lastNoteRect) {
+    const noteDistance = lastNoteRect.x - firstNoteRect.x + lastNoteRect.width / 3;
+    console.log('首尾间距', noteDistance)
+    state.noteDistance = noteDistance || 0;
+  }
+}
+
+/** 跳动svgdom */
+export const moveSvgDom = () => {
   const cursorLeft = state.cursorDom?.getBoundingClientRect()?.left || 0;
-  const leftValue = parseFloat(state.cursorDom.style.left) - 47 - 20
+  const leftValue = parseFloat(state.cursorDom.style.left) - 47 - 20;
   // console.log(cursorLeft,leftValue,'光标位置')
   // state.osmdSvgDom.style.transform = `translateX(${-leftValue}px)`;
   // state.osdmScrollDom.scrollLeft = leftValue
+  state.times.forEach((item: any, idx: number) => {
+    const svgEl = document.getElementById(`vf-${state.times[idx]?.svgElement?.attrs?.id}`)
+    const stemEl = document.getElementById(`vf-${state.times[idx]?.svgElement?.attrs?.id}-stem`)
+    if (item.i === state.activeNoteIndex && item.svgElement) {
+      svgEl?.classList.add('noteActive')
+      stemEl?.classList.add('noteActive')
+    } else {
+      svgEl?.classList.remove('noteActive')
+      stemEl?.classList.remove('noteActive')
+    }
+  })
+
   state.osdmScrollDom.scrollTo({
     left: leftValue,
     behavior: "smooth",
   });
+}
+
+/** 平滑移动svgdom */
+export const smoothMoveSvgDom = () => {
+  const currentTime = getAudioCurrentTime();
+  const matchNoteIdx = state.times.findIndex((item: any) => {
+    Math.abs(item.time - currentTime) * 1000 < 100
+  })
+  
+  if (currentTime <= state.fixtime) return;
+  if (currentTime > state.times.last()?.time) return; 
+  // console.log('跳转音符',currentTime)
+  const currentBBox = state.times[state.activeNoteIndex]?.bbox;
+  let nextIndex = state.activeNoteIndex + 1;
+  let nextBBox = state.times[nextIndex]?.bbox;
+  while (!nextBBox && nextIndex < state.times.length) {
+    nextIndex += 1;
+    nextBBox = state.times[nextIndex]?.bbox;
+  }
+  // 下一个音符和第一个音符之间的间距
+  const noteDistance = nextBBox?.x - state.times[state.activeNoteIndex].bbox?.x + nextBBox?.width / 4 - state.times[state.activeNoteIndex].bbox?.width / 4 || 0
+  if (noteDistance) {
+    // 当前的音符和下一个音符之间的时值
+    const noteDuration = state.times[nextIndex].time - state.times[state.activeNoteIndex]?.time;
+    // 当前时值在该区间的占比
+    const playProgress = (currentTime - state.times[state.activeNoteIndex]?.time) / noteDuration;
+    const distance = noteDistance * playProgress;
+    
+    // 上一个音符和第一个音符的间距
+    let preDistance = state.times[state.activeNoteIndex].bbox?.x - state.times[0].bbox?.x + state.times[state.activeNoteIndex].bbox?.width / 4;
+    // console.log('滑动', distance, preDistance,  state.osdmScrollDom.scrollLeft, noteDistance, state.activeNoteIndex, nextIndex, currentTime )
+    //console.log('滑动','音符:',state.activeNoteIndex,'小节:', state.activeMeasureIndex)
+    state.osdmScrollDom.scrollLeft = distance + preDistance;
+  } else {
+    const playProgress = (currentTime - state.times[0]?.time) / state.times.last()?.time
+    const distance = state.noteDistance * playProgress;
+    state.osdmScrollDom.scrollLeft = distance;
+  }
 }

+ 10 - 1
src/view/music-score/index.module.less

@@ -29,6 +29,14 @@
             }
         }
     }
+    .noteActive {
+        path {
+            fill: #FF2B29;
+            stroke: #FF2B29;
+            // transform: scale(1.2);
+            // transition: all 0.3s;
+        }
+    }
 }
 .inGradualRange{
    :global{
@@ -36,4 +44,5 @@
             // opacity: 0 !important;
         }
    } 
-}
+}
+

+ 1 - 1
src/view/music-score/index.tsx

@@ -104,7 +104,7 @@ export default defineComponent({
 				copyCursor.setAttribute('id','cursor-copy');
 				copyCursor.style.position = 'sticky';
 				copyCursor.style.zIndex = '2';
-				copyCursor.style.background = 'red';
+				// copyCursor.style.background = 'red';
 				copyCursor && scrollDom?.appendChild(copyCursor);
 			}
 		}

+ 1 - 1
src/view/selection/index.tsx

@@ -151,7 +151,7 @@ const calcNoteData = () => {
 			}
 		}
 	}
-	console.log("🚀 ~ selectData.notes:", selectData.notes, selectData.staves);
+	// console.log("🚀 ~ selectData.notes:", selectData.notes, selectData.staves);
 };
 
 /** 重新计算 */