|
|
@@ -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
|