Prechádzať zdrojové kódy

fix: 音符播放时抖动bug修复

tianyong 1 týždeň pred
rodič
commit
54e6d5a18e
2 zmenil súbory, kde vykonal 56 pridanie a 7 odobranie
  1. 1 1
      osmd-extended
  2. 55 6
      src/state.ts

+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit 036466bd82deff8603e510b95888d89010b38aa0
+Subproject commit bae380f80800211a0585d82b88faaf34d145836e

+ 55 - 6
src/state.ts

@@ -1129,6 +1129,7 @@ export const hanldeDirectSelection = (list: any[]) => {
   }, 0);
 };
 let offsetTop = 0, musicScrollTop = 0;
+let lastLineTop = 0; // 记录上一次的行位置
 /**
  * 窗口内滚动到音符的区域
  * @param isScroll 可选: 强制滚动到顶部, 默认: false
@@ -1142,6 +1143,7 @@ export const scrollViewNote = (resetTop?: boolean) => {
   }
   if (state.activeNoteIndex <= 1 || resetTop) {
     offsetTop = 0;
+    lastLineTop = 0; // 重置行位置记录
   }
   const domId = "vf" + noteId;
   // 合并休止小节没有音符,取小节的位置,否则取音符指针位置
@@ -1157,16 +1159,63 @@ export const scrollViewNote = (resetTop?: boolean) => {
   if (!cursorElement || !musicAndSelection) {
     return
   }
-  // offsetTop = musicAndSelection.scrollTop || offsetTop;
-  const noteCenterOffsetTop = cursorElement ? cursorElement?.offsetTop + (cursorElement?.offsetHeight/2) : 0;
-  // console.log('滑动',offsetTop, noteCenterOffsetTop)
+  
+  // 获取当前音符的位置信息
+  const rect = cursorElement.getBoundingClientRect();
+  const rawOffsetTop = cursorElement.offsetTop;
+  const currentNote = state.times[state.activeNoteIndex];
+  const scrollTarget = state.musicRenderType === EnumMusicRenderType.staff ? rawOffsetTop : rect.height/2 + rawOffsetTop;
+  
+  // 同一行内的音符,offsetTop 可能因为音高不同而有 60-80px 的差异
+  // 但真正换行时,位置变化通常会超过 100px(向上翻页)或 150px(向下换行)
+  // 所以直接使用 rawOffsetTop 的实际差异来判断,而不是用固定行高分组
+  
+  let currentTop = rawOffsetTop;
+  let isLineChanged = false;
+  
+  // 计算与上次位置的差异
+  const positionDiff = currentTop - lastLineTop;
+  
+  // 换行判断阈值:
+  // - 向下换行(新行在下方):位置增加超过 100px
+  // - 向上翻页(新行在上方):位置减少超过 100px
+  // - 同一行内移动:位置变化在 -100px 到 +100px 之间
+  const lineChangeThreshold = 100; // 真正换行的阈值
+  
+  if (Math.abs(positionDiff) > lineChangeThreshold) {
+    // 位置变化超过阈值,认为是换行
+    isLineChanged = true;
+  } else {
+    // 位置变化较小,认为是同一行内移动
+    isLineChanged = false;
+    // 同一行内,使用上次的 lastLineTop 作为基准,避免累积误差
+    currentTop = lastLineTop;
+  }
+  
+  // console.log('滚动检测', { 
+  //   currentTop, 
+  //   lastLineTop, 
+  //   isLineChanged, 
+  //   positionDiff,
+  //   absDiff: Math.abs(positionDiff),
+  //   noteIndex: state.activeNoteIndex,
+  //   rawOffsetTop,
+  //   scrollTarget,
+  //   MeasureNumberXML: currentNote?.MeasureNumberXML
+  // })
+
   if (Math.abs(musicAndSelection?.scrollTop - musicScrollTop) > 30) {
     // 手动滑动谱面,重新播放需要滚动到对应位置
+    lastLineTop = currentTop; // 更新行位置记录
   } else {
-    if (offsetTop === cursorElement.offsetTop || Math.abs(offsetTop - cursorElement.offsetTop) < 30) return;
+    // 如果没有换行,则不执行滚动,直接返回
+    if (!isLineChanged) {
+      return;
+    }
   }
-  // offsetTop = noteCenterOffsetTop;
-  offsetTop = state.musicRenderType === EnumMusicRenderType.staff ? cursorElement.offsetTop : cursorElement.getBoundingClientRect().height/2 + cursorElement.offsetTop;
+  // 换行了,更新 offsetTop 和行位置记录
+  lastLineTop = currentTop;
+  offsetTop = scrollTarget; // 使用计算好的滚动目标值
   const animateType = browser().android ? "instant" : "smooth"
   if (offsetTop > (state.headTopHeight + 30)) {
     musicScrollTop = (offsetTop - state.headTopHeight - 30) * state.musicZoom