diff --git a/publish/changeLog.md b/publish/changeLog.md index 91dd24ad..9aa02a57 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -17,6 +17,7 @@ - 点击打开歌单弹窗背景将不再自动关闭弹窗,防止选择输入框里的内容时意外关闭弹窗 - 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序 - 优化桌面歌词在开启 缩放当前播放的歌词 并关闭 延迟歌词滚动 时的歌词滚动位置计算问题,现在歌词滚动应该可以正确滚动到目标位置了 +- 优化歌词在短时间内快速播放时的滚动效果,现在遇到这种情况时滚动将更平滑 ### 修复 diff --git a/src/common/utils/renderer.ts b/src/common/utils/renderer.ts index 6a205cce..a94c58ce 100644 --- a/src/common/utils/renderer.ts +++ b/src/common/utils/renderer.ts @@ -8,15 +8,31 @@ const easeInOutQuad = (t: number, b: number, c: number, d: number): number => { type Noop = () => void const noop: Noop = () => {} +type ScrollElement = { + lx_scrollLockKey?: number + lx_scrollNextParams?: [ScrollElement, number, number, Noop] + lx_scrollTimeout?: number + lx_scrollDelayTimeout?: number +} & T -const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = noop): Noop => { +const handleScrollY = (element: ScrollElement, to: number, duration = 300, fn = noop): Noop => { if (!element) { fn() return noop } + const clean = () => { + element.lx_scrollLockKey = undefined + element.lx_scrollNextParams = undefined + if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout) + element.lx_scrollTimeout = undefined + } + if (element.lx_scrollLockKey) { + element.lx_scrollNextParams = [element, to, duration, fn] + element.lx_scrollLockKey = -1 + return clean + } // @ts-expect-error const start = element.scrollTop ?? element.scrollY ?? 0 - let cancel = false if (to > start) { let maxScrollTop = element.scrollHeight - element.clientHeight if (to > maxScrollTop) to = maxScrollTop @@ -34,9 +50,19 @@ const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = no } let currentTime = 0 - let val + let val: number + let key = Math.random() const animateScroll = () => { + element.lx_scrollTimeout = undefined + // if (element.lx_scrollLockKey != key) { + if (element.lx_scrollNextParams && currentTime > duration * 0.75) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + return + } + currentTime += increment val = Math.trunc(easeInOutQuad(currentTime, start, change, duration)) if (element.scrollTo) { @@ -45,19 +71,23 @@ const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = no element.scrollTop = val } if (currentTime < duration) { - if (cancel) { - fn() - return - } - window.setTimeout(animateScroll, increment) + element.lx_scrollTimeout = window.setTimeout(animateScroll, increment) } else { - fn() + if (element.lx_scrollNextParams) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + } else { + clean() + fn() + } } } + + element.lx_scrollLockKey = key animateScroll() - return () => { - cancel = true - } + + return clean } /** * 设置滚动条位置 @@ -67,16 +97,24 @@ const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = no * @param {*} fn 滚动完成后的回调 * @param {*} delay 延迟执行时间 */ -export const scrollTo = (element: HTMLElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { +export const scrollTo = (element: ScrollElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { let cancelFn: () => void - let timeout: number | null + if (element.lx_scrollDelayTimeout != null) { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } if (delay) { let scrollCancelFn: Noop cancelFn = () => { - timeout == null ? scrollCancelFn?.() : clearTimeout(timeout) + if (element.lx_scrollDelayTimeout == null) { + scrollCancelFn?.() + } else { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } } - timeout = window.setTimeout(() => { - timeout = null + element.lx_scrollDelayTimeout = window.setTimeout(() => { + element.lx_scrollDelayTimeout = undefined scrollCancelFn = handleScrollY(element, to, duration, fn) }, delay) } else { @@ -84,14 +122,24 @@ export const scrollTo = (element: HTMLElement, to: number, duration = 300, fn = } return cancelFn } -const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = () => {}): () => void => { +const handleScrollX = (element: ScrollElement, to: number, duration = 300, fn = () => {}): () => void => { if (!element) { fn() return noop } + const clean = () => { + element.lx_scrollLockKey = undefined + element.lx_scrollNextParams = undefined + if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout) + element.lx_scrollTimeout = undefined + } + if (element.lx_scrollLockKey) { + element.lx_scrollNextParams = [element, to, duration, fn] + element.lx_scrollLockKey = -1 + return clean + } // @ts-expect-error const start = element.scrollLeft || element.scrollX || 0 - let cancel = false if (to > start) { let maxScrollLeft = element.scrollWidth - element.clientWidth if (to > maxScrollLeft) to = maxScrollLeft @@ -109,9 +157,18 @@ const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = () } let currentTime = 0 - let val + let val: number + let key = Math.random() const animateScroll = () => { + element.lx_scrollTimeout = undefined + if (element.lx_scrollNextParams && currentTime > duration * 0.75) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + return + } + currentTime += increment val = Math.trunc(easeInOutQuad(currentTime, start, change, duration)) if (element.scrollTo) { @@ -120,19 +177,21 @@ const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = () element.scrollLeft = val } if (currentTime < duration) { - if (cancel) { - fn() - return - } - window.setTimeout(animateScroll, increment) + element.lx_scrollTimeout = window.setTimeout(animateScroll, increment) } else { - fn() + if (element.lx_scrollNextParams) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + } else { + clean() + fn() + } } } + element.lx_scrollLockKey = key animateScroll() - return () => { - cancel = true - } + return clean } /** * 设置滚动条位置 @@ -142,16 +201,24 @@ const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = () * @param {*} fn 滚动完成后的回调 * @param {*} delay 延迟执行时间 */ -export const scrollXTo = (element: HTMLElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { +export const scrollXTo = (element: ScrollElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { let cancelFn: Noop - let timeout: number | null + if (element.lx_scrollDelayTimeout != null) { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } if (delay) { let scrollCancelFn: Noop cancelFn = () => { - timeout == null ? scrollCancelFn?.() : clearTimeout(timeout) + if (element.lx_scrollDelayTimeout == null) { + scrollCancelFn?.() + } else { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } } - timeout = window.setTimeout(() => { - timeout = null + element.lx_scrollDelayTimeout = window.setTimeout(() => { + element.lx_scrollDelayTimeout = undefined scrollCancelFn = handleScrollX(element, to, duration, fn) }, delay) } else { @@ -160,14 +227,24 @@ export const scrollXTo = (element: HTMLElement, to: number, duration = 300, fn = return cancelFn } -const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = () => {}): () => void => { +const handleScrollXR = (element: ScrollElement, to: number, duration = 300, fn = () => {}): () => void => { if (!element) { fn() return noop } + const clean = () => { + element.lx_scrollLockKey = undefined + element.lx_scrollNextParams = undefined + if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout) + element.lx_scrollTimeout = undefined + } + if (element.lx_scrollLockKey) { + element.lx_scrollNextParams = [element, to, duration, fn] + element.lx_scrollLockKey = -1 + return clean + } // @ts-expect-error const start = element.scrollLeft || element.scrollX as number || 0 - let cancel = false if (to < start) { let maxScrollLeft = -element.scrollWidth + element.clientWidth if (to < maxScrollLeft) to = maxScrollLeft @@ -186,9 +263,18 @@ const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = ( } let currentTime = 0 - let val + let val: number + let key = Math.random() const animateScroll = () => { + element.lx_scrollTimeout = undefined + if (element.lx_scrollNextParams && currentTime > duration * 0.75) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + return + } + currentTime += increment val = Math.trunc(easeInOutQuad(currentTime, start, change, duration)) @@ -198,19 +284,23 @@ const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = ( element.scrollLeft = val } if (currentTime < duration) { - if (cancel) { - fn() - return - } - window.setTimeout(animateScroll, increment) + element.lx_scrollTimeout = window.setTimeout(animateScroll, increment) } else { - fn() + if (element.lx_scrollNextParams) { + const [_element, to, duration, fn] = element.lx_scrollNextParams + clean() + handleScrollY(_element, to, duration, fn) + } else { + clean() + fn() + } } } + + element.lx_scrollLockKey = key animateScroll() - return () => { - cancel = true - } + + return clean } /** * 设置滚动条位置 (writing-mode: vertical-rl 专用) @@ -220,16 +310,24 @@ const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = ( * @param fn 滚动完成后的回调 * @param delay 延迟执行时间 */ -export const scrollXRTo = (element: HTMLElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { +export const scrollXRTo = (element: ScrollElement, to: number, duration = 300, fn = () => {}, delay = 0): () => void => { let cancelFn: Noop - let timeout: number | null + if (element.lx_scrollDelayTimeout != null) { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } if (delay) { let scrollCancelFn: Noop cancelFn = () => { - timeout == null ? scrollCancelFn?.() : clearTimeout(timeout) + if (element.lx_scrollDelayTimeout == null) { + scrollCancelFn?.() + } else { + window.clearTimeout(element.lx_scrollDelayTimeout) + element.lx_scrollDelayTimeout = undefined + } } - timeout = window.setTimeout(() => { - timeout = null + element.lx_scrollDelayTimeout = window.setTimeout(() => { + element.lx_scrollDelayTimeout = undefined scrollCancelFn = handleScrollXR(element, to, duration, fn) }, delay) } else { diff --git a/src/renderer-lyric/components/layout/LyricHorizontal/useLyric.js b/src/renderer-lyric/components/layout/LyricHorizontal/useLyric.js index 9ba94b1d..2213b34a 100644 --- a/src/renderer-lyric/components/layout/LyricHorizontal/useLyric.js +++ b/src/renderer-lyric/components/layout/LyricHorizontal/useLyric.js @@ -35,10 +35,6 @@ export default (isComputeHeight) => { const handleScrollLrc = (duration = 300) => { if (!dom_lines?.length || !dom_lyric.value) return - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } if (isStopScroll) return let dom_p = dom_lines[lyric.line] @@ -161,10 +157,6 @@ export default (isComputeHeight) => { if (lines.length) { setLyric(lines) } else { - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } cancelScrollFn = scrollTo(dom_lyric.value, 0, 300, () => { if (lyric.lines !== lines) return setLyric(lines) diff --git a/src/renderer-lyric/components/layout/LyricVertical/useLyric.js b/src/renderer-lyric/components/layout/LyricVertical/useLyric.js index ea15dfde..6fb712df 100644 --- a/src/renderer-lyric/components/layout/LyricVertical/useLyric.js +++ b/src/renderer-lyric/components/layout/LyricVertical/useLyric.js @@ -35,10 +35,6 @@ export default (isComputeWidth) => { const handleScrollLrc = (duration = 300) => { if (!dom_lines?.length || !dom_lyric.value) return - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } if (isStopScroll) return let dom_p = dom_lines[lyric.line] @@ -161,10 +157,6 @@ export default (isComputeWidth) => { if (lines.length) { setLyric(lines) } else { - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } cancelScrollFn = scrollXRTo(dom_lyric.value, 0, 300, () => { if (lyric.lines !== lines) return setLyric(lines) diff --git a/src/renderer/utils/compositions/useLyric.js b/src/renderer/utils/compositions/useLyric.js index 87de19d3..321dfd8c 100644 --- a/src/renderer/utils/compositions/useLyric.js +++ b/src/renderer/utils/compositions/useLyric.js @@ -80,10 +80,6 @@ export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offse const handleScrollLrc = (duration = 300) => { if (!dom_lines?.length || !dom_lyric.value) return - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } if (isSkipMouseEnter) return if (isStopScroll.value) return let dom_p = dom_lines[lyric.line] @@ -179,10 +175,6 @@ export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offse if (lines.length) { setLyric(lines) } else { - if (cancelScrollFn) { - cancelScrollFn() - cancelScrollFn = null - } cancelScrollFn = scrollTo(dom_lyric.value, 0, 300, () => { if (lyric.lines !== lines) return setLyric(lines)