Skip to content

Commit

Permalink
优化歌词在短时间内快速播放时的滚动效果
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Sep 2, 2023
1 parent ed858ba commit 5e7ef69
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 72 deletions.
1 change: 1 addition & 0 deletions publish/changeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- 点击打开歌单弹窗背景将不再自动关闭弹窗,防止选择输入框里的内容时意外关闭弹窗
- 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序
- 优化桌面歌词在开启 缩放当前播放的歌词 并关闭 延迟歌词滚动 时的歌词滚动位置计算问题,现在歌词滚动应该可以正确滚动到目标位置了
- 优化歌词在短时间内快速播放时的滚动效果,现在遇到这种情况时滚动将更平滑

### 修复

Expand Down
194 changes: 146 additions & 48 deletions src/common/utils/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,31 @@ const easeInOutQuad = (t: number, b: number, c: number, d: number): number => {

type Noop = () => void
const noop: Noop = () => {}
type ScrollElement<T> = {
lx_scrollLockKey?: number
lx_scrollNextParams?: [ScrollElement<HTMLElement>, 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<HTMLElement>, 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
Expand All @@ -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) {
Expand All @@ -45,19 +71,23 @@ const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = no
element.scrollTop = val
}
if (currentTime < duration) {
if (cancel) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
}
}

element.lx_scrollLockKey = key
animateScroll()
return () => {
cancel = true
}

return clean
}
/**
* 设置滚动条位置
Expand All @@ -67,31 +97,49 @@ 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<HTMLElement>, 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 {
cancelFn = handleScrollY(element, to, duration, fn) ?? noop
}
return cancelFn
}
const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = () => {}): () => void => {
const handleScrollX = (element: ScrollElement<HTMLElement>, 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
Expand All @@ -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) {
Expand All @@ -120,19 +177,21 @@ const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = ()
element.scrollLeft = val
}
if (currentTime < duration) {
if (cancel) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
}
}
element.lx_scrollLockKey = key
animateScroll()
return () => {
cancel = true
}
return clean
}
/**
* 设置滚动条位置
Expand All @@ -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<HTMLElement>, 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 {
Expand All @@ -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<HTMLElement>, 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
Expand All @@ -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))

Expand All @@ -198,19 +284,23 @@ const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = (
element.scrollLeft = val
}
if (currentTime < duration) {
if (cancel) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
}
}

element.lx_scrollLockKey = key
animateScroll()
return () => {
cancel = true
}

return clean
}
/**
* 设置滚动条位置 (writing-mode: vertical-rl 专用)
Expand All @@ -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<HTMLElement>, 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 5e7ef69

Please sign in to comment.