lx-music-desktop/src/renderer/utils/compositions/useLyric.js

247 lines
6.8 KiB
JavaScript

import { ref, onMounted, onBeforeUnmount, watch, nextTick } from '@common/utils/vueTools'
import { throttle, formatPlayTime2 } from '@common/utils/common'
import { scrollTo } from '@common/utils/renderer'
import { play } from '@renderer/core/player/action'
// import { player as eventPlayerNames } from '@renderer/event/names'
export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offset }) => {
const dom_lyric = ref(null)
const dom_lyric_text = ref(null)
const dom_skip_line = ref(null)
const isMsDown = ref(false)
const isStopScroll = ref(false)
const timeStr = ref('--/--')
let msDownY = 0
let msDownScrollY = 0
let timeout = null
let cancelScrollFn
let dom_lines
let isSetedLines = false
let point = {
x: null,
y: null,
}
let time = -1
let dom_pre_line = null
let isSkipMouseEnter = false
const handleSkipPlay = () => {
if (time == -1) return
handleSkipMouseLeave()
isStopScroll.value = false
window.app_event.setProgress(time)
if (!isPlay.value) play()
}
const handleSkipMouseEnter = () => {
isSkipMouseEnter = true
clearLyricScrollTimeout()
}
const handleSkipMouseLeave = () => {
isSkipMouseEnter = false
startLyricScrollTimeout()
}
const throttleSetTime = throttle(() => {
if (!dom_skip_line.value) return
const rect = dom_skip_line.value.getBoundingClientRect()
point.x = rect.x
point.y = rect.y
let dom = document.elementFromPoint(point.x, point.y)
if (dom_pre_line === dom) return
if (dom.tagName == 'SPAN') {
dom = dom.parentNode.parentNode
} else if (dom.classList.contains('line')) {
dom = dom.parentNode
}
if (dom.time == null) {
if (lyric.lines.length) {
time = dom.classList.contains('pre') ? 0 : lyric.lines[lyric.lines.length - 1].time ?? 0
time = Math.max(time - lyric.offset - lyric.tempOffset, 0)
time /= 1000
if (time > playProgress.maxPlayTime) time = playProgress.maxPlayTime
timeStr.value = formatPlayTime2(time)
} else {
time = -1
timeStr.value = '--:--'
}
} else {
time = dom.time
time = Math.max(time - lyric.offset - lyric.tempOffset, 0)
time /= 1000
if (time > playProgress.maxPlayTime) time = playProgress.maxPlayTime
timeStr.value = formatPlayTime2(time)
}
dom_pre_line = dom
})
const setTime = () => {
if (isShowLyricProgressSetting.value) throttleSetTime()
}
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]
cancelScrollFn = scrollTo(dom_lyric.value, dom_p ? (dom_p.offsetTop - dom_lyric.value.clientHeight * 0.38) : 0, duration)
}
const clearLyricScrollTimeout = () => {
if (!timeout) return
clearTimeout(timeout)
timeout = null
}
const startLyricScrollTimeout = () => {
clearLyricScrollTimeout()
if (isSkipMouseEnter) return
timeout = setTimeout(() => {
timeout = null
isStopScroll.value = false
if (!isPlay.value) return
handleScrollLrc()
}, 3000)
}
const handleLyricDown = (y) => {
// console.log(event)
if (delayScrollTimeout) {
clearTimeout(delayScrollTimeout)
delayScrollTimeout = null
}
isMsDown.value = true
msDownY = y
msDownScrollY = dom_lyric.value.scrollTop
}
const handleLyricMouseDown = event => {
handleLyricDown(event.clientY)
}
const handleLyricTouchStart = event => {
if (event.changedTouches.length) {
const touch = event.changedTouches[0]
handleLyricDown(touch.clientY)
}
}
const handleMouseMsUp = event => {
isMsDown.value = false
}
const handleMove = (y) => {
if (isMsDown.value) {
isStopScroll.value ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
dom_lyric.value.scrollTop = msDownScrollY + msDownY - y
startLyricScrollTimeout()
setTime()
}
}
const handleMouseMsMove = event => {
handleMove(event.clientY)
}
const handleTouchMove = (e) => {
if (e.changedTouches.length) {
const touch = e.changedTouches[0]
handleMove(touch.clientY)
}
}
const handleWheel = (event) => {
console.log(event.deltaY)
isStopScroll.value ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
dom_lyric.value.scrollTop = dom_lyric.value.scrollTop + event.deltaY
startLyricScrollTimeout()
setTime()
}
const setLyric = (lines) => {
const dom_line_content = document.createDocumentFragment()
for (const line of lines) {
dom_line_content.appendChild(line.dom_line)
}
dom_lyric_text.value.textContent = ''
dom_lyric_text.value.appendChild(dom_line_content)
nextTick(() => {
dom_lines = dom_lyric.value.querySelectorAll('.line-content')
handleScrollLrc()
})
}
const initLrc = (lines, oLines) => {
isSetedLines = true
if (oLines) {
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)
}, 50)
}
} else {
setLyric(lines)
}
}
let delayScrollTimeout
const scrollLine = (line, oldLine) => {
if (line < 0) return
if (line == 0 && isSetedLines) return isSetedLines = false
isSetedLines &&= false
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
delayScrollTimeout = setTimeout(() => {
delayScrollTimeout = null
handleScrollLrc(600)
}, 600)
}
watch(() => lyric.lines, initLrc)
watch(() => lyric.line, scrollLine)
onMounted(() => {
document.addEventListener('mousemove', handleMouseMsMove)
document.addEventListener('mouseup', handleMouseMsUp)
document.addEventListener('touchmove', handleTouchMove)
document.addEventListener('touchend', handleMouseMsUp)
initLrc(lyric.lines, null)
nextTick(() => {
scrollLine(lyric.line)
})
})
onBeforeUnmount(() => {
document.removeEventListener('mousemove', handleMouseMsMove)
document.removeEventListener('mouseup', handleMouseMsUp)
document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleMouseMsUp)
})
return {
dom_lyric,
dom_lyric_text,
dom_skip_line,
isStopScroll,
isMsDown,
timeStr,
handleLyricMouseDown,
handleLyricTouchStart,
handleWheel,
handleSkipPlay,
handleSkipMouseEnter,
handleSkipMouseLeave,
handleScrollLrc,
}
}