新增通过播放详情页歌词调整播放进度
parent
34febf4949
commit
cfee4a0b24
|
@ -1,6 +1,7 @@
|
|||
### 新增
|
||||
|
||||
- 新增对播放详情页歌词大小、是否缩放、对齐方式的设置,可以去设置-播放详情页设置查看
|
||||
- 新增通过播放详情页歌词调整播放进度
|
||||
|
||||
### 优化
|
||||
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
<template>
|
||||
<div :class="['right', $style.right]">
|
||||
<div :class="['right', $style.right]" :style="lrcFontSize">
|
||||
<div :class="['lyric', $style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }]" :style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric">
|
||||
<div :class="$style.lyricSpace"></div>
|
||||
<div :class="['pre', $style.lyricSpace]"></div>
|
||||
<div ref="dom_lyric_text"></div>
|
||||
<div :class="$style.lyricSpace"></div>
|
||||
</div>
|
||||
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
|
||||
<div :class="$style.skip" v-show="isStopScroll">
|
||||
<div :class="$style.line" ref="dom_skip_line"></div>
|
||||
<span :class="$style.label">{{timeStr}}</span>
|
||||
<base-btn :class="$style.skipBtn" @mouseenter="handleSkipMouseEnter" @mouseleave="handleSkipMouseLeave" @click="handleSkipPlay">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="50%" viewBox="0 0 170 170" space="preserve">
|
||||
<use xlink:href="#icon-play"></use>
|
||||
</svg>
|
||||
</base-btn>
|
||||
</div>
|
||||
</transition>
|
||||
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
|
||||
<div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
|
||||
<div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]">
|
||||
|
@ -31,9 +42,15 @@ export default {
|
|||
const {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
dom_skip_line,
|
||||
isMsDown,
|
||||
isStopScroll,
|
||||
timeStr,
|
||||
handleLyricMouseDown,
|
||||
handleWheel,
|
||||
handleSkipPlay,
|
||||
handleSkipMouseEnter,
|
||||
handleSkipMouseLeave,
|
||||
} = useLyric({ isPlay, lyric })
|
||||
|
||||
const fontSizeUp = () => {
|
||||
|
@ -48,6 +65,10 @@ export default {
|
|||
const lrcStyles = computed(() => {
|
||||
return {
|
||||
textAlign: setting.value.playDetail.style.align,
|
||||
}
|
||||
})
|
||||
const lrcFontSize = computed(() => {
|
||||
return {
|
||||
'--playDetail-lrc-font-size': setting.value.playDetail.style.fontSize / 100 + 'rem',
|
||||
}
|
||||
})
|
||||
|
@ -69,13 +90,20 @@ export default {
|
|||
return {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
dom_skip_line,
|
||||
isMsDown,
|
||||
timeStr,
|
||||
handleLyricMouseDown,
|
||||
handleWheel,
|
||||
handleSkipPlay,
|
||||
handleSkipMouseEnter,
|
||||
handleSkipMouseLeave,
|
||||
lyric,
|
||||
isShowLrcSelectContent,
|
||||
lrcStyles,
|
||||
lrcFontSize,
|
||||
isZoomActiveLrc,
|
||||
isStopScroll,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -133,11 +161,11 @@ export default {
|
|||
:global {
|
||||
.lrc-content {
|
||||
line-height: 1.2;
|
||||
margin: var(--playDetail-lrc-font-size, 16px) 0;
|
||||
padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 0;
|
||||
overflow-wrap: break-word;
|
||||
color: @color-player-detail-lyric;
|
||||
transition: @transition-theme;
|
||||
transition-property: margin;
|
||||
transition-property: padding;
|
||||
|
||||
.translation {
|
||||
transition: @transition-theme !important;
|
||||
|
@ -203,6 +231,50 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skip {
|
||||
position: absolute;
|
||||
top: calc(38% + var(--playDetail-lrc-font-size, 16px) + 4px);
|
||||
left: 0;
|
||||
// height: 6px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
// opacity: .5;
|
||||
.line {
|
||||
border-top: 1px dashed @color-player-detail-lyric-active;
|
||||
opacity: .15;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.label {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: -14px;
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
color: @color-player-detail-lyric-active;
|
||||
opacity: .7;
|
||||
}
|
||||
.skipBtn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none !important;
|
||||
pointer-events: initial;
|
||||
transition: @transition-theme;
|
||||
transition-property: opacity;
|
||||
opacity: .8;
|
||||
&:hover {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lyricSelectContent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
@ -267,6 +339,14 @@ each(@themes, {
|
|||
// .lrc-active {
|
||||
// color: ~'@{color-@{value}-theme}';
|
||||
// }
|
||||
.skip {
|
||||
.line {
|
||||
border-top-color: ~'@{color-@{value}-player-detail-lyric-active}';
|
||||
}
|
||||
.label {
|
||||
color:~'@{color-@{value}-player-detail-lyric-active}';
|
||||
}
|
||||
}
|
||||
.lyricSelectContent {
|
||||
background-color: ~'@{color-@{value}-theme_2-background_1}';
|
||||
color: ~'@{color-@{value}-player-detail-lyric}';
|
||||
|
|
|
@ -1,18 +1,74 @@
|
|||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from '@renderer/utils/vueTools'
|
||||
import { scrollTo } from '@renderer/utils'
|
||||
import { scrollTo, throttle, formatPlayTime2 } from '@renderer/utils'
|
||||
import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
|
||||
export default ({ isPlay, lyric }) => {
|
||||
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 isStopScroll = false
|
||||
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.eventHub.emit(eventPlayerNames.setProgress, time)
|
||||
if (!isPlay.value) window.eventHub.emit(eventPlayerNames.setPlay)
|
||||
}
|
||||
const handleSkipMouseEnter = () => {
|
||||
isSkipMouseEnter = true
|
||||
clearLyricScrollTimeout()
|
||||
}
|
||||
const handleSkipMouseLeave = () => {
|
||||
isSkipMouseEnter = false
|
||||
startLyricScrollTimeout()
|
||||
}
|
||||
|
||||
const setTime = throttle(() => {
|
||||
if (point.x == null) {
|
||||
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('font')) {
|
||||
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
|
||||
if (time) time = time / 1000
|
||||
timeStr.value = formatPlayTime2(time)
|
||||
} else {
|
||||
time = -1
|
||||
timeStr.value = '--:--'
|
||||
}
|
||||
} else {
|
||||
time = dom.time
|
||||
if (time) time = time / 1000
|
||||
timeStr.value = formatPlayTime2(time)
|
||||
}
|
||||
dom_pre_line = dom
|
||||
})
|
||||
|
||||
const handleScrollLrc = (duration = 300) => {
|
||||
if (!dom_lines?.length || !dom_lyric.value) return
|
||||
|
@ -20,7 +76,8 @@ export default ({ isPlay, lyric }) => {
|
|||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
if (isStopScroll) return
|
||||
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)
|
||||
}
|
||||
|
@ -31,9 +88,10 @@ export default ({ isPlay, lyric }) => {
|
|||
}
|
||||
const startLyricScrollTimeout = () => {
|
||||
clearLyricScrollTimeout()
|
||||
if (isSkipMouseEnter) return
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
isStopScroll = false
|
||||
isStopScroll.value = false
|
||||
if (!isPlay.value) return
|
||||
handleScrollLrc()
|
||||
}, 3000)
|
||||
|
@ -53,25 +111,27 @@ export default ({ isPlay, lyric }) => {
|
|||
}
|
||||
const handleMouseMsMove = event => {
|
||||
if (isMsDown.value) {
|
||||
if (!isStopScroll) isStopScroll = true
|
||||
if (!isStopScroll.value) isStopScroll.value = true
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollTop = msDownScrollY + msDownY - event.clientY
|
||||
startLyricScrollTimeout()
|
||||
setTime()
|
||||
}
|
||||
}
|
||||
|
||||
const handleWheel = (event) => {
|
||||
console.log(event.deltaY)
|
||||
if (!isStopScroll) isStopScroll = true
|
||||
if (!isStopScroll.value) isStopScroll.value = true
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollTop = dom_lyric.value.scrollTop + event.deltaY
|
||||
startLyricScrollTimeout()
|
||||
setTime()
|
||||
}
|
||||
|
||||
const setLyric = (lines) => {
|
||||
|
@ -139,8 +199,14 @@ export default ({ isPlay, lyric }) => {
|
|||
return {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
dom_skip_line,
|
||||
isStopScroll,
|
||||
isMsDown,
|
||||
timeStr,
|
||||
handleLyricMouseDown,
|
||||
handleWheel,
|
||||
handleSkipPlay,
|
||||
handleSkipMouseEnter,
|
||||
handleSkipMouseLeave,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ const createAnimation = (dom, duration) => new window.Animation(new window.Keyfr
|
|||
// https://jsfiddle.net/ceqpnbky/1/
|
||||
|
||||
module.exports = class FontPlayer {
|
||||
constructor({ lyric = '', translationLyric = '', lineClassName = '', fontClassName = '', translationClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) {
|
||||
constructor({ time = 0, lyric = '', translationLyric = '', lineClassName = '', fontClassName = '', translationClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) {
|
||||
this.time = time
|
||||
this.lyric = lyric
|
||||
this.translationLyric = translationLyric
|
||||
|
||||
|
@ -51,6 +52,7 @@ module.exports = class FontPlayer {
|
|||
this.isLineMode = false
|
||||
|
||||
this.lineContent = document.createElement('div')
|
||||
this.lineContent.time = this.time
|
||||
if (this.lineClassName) this.lineContent.classList.add(this.lineClassName)
|
||||
this.fontContent = document.createElement('div')
|
||||
this.fontContent.style = 'position:relative;display:inline-block;'
|
||||
|
|
|
@ -106,6 +106,7 @@ module.exports = class Lyric {
|
|||
if (this.isLineMode) {
|
||||
this._lines = lyricLines.map(line => {
|
||||
const fontPlayer = new FontPlayer({
|
||||
time: line.time,
|
||||
lyric: line.text,
|
||||
translationLyric: line.translation,
|
||||
lineClassName: this.lineClassName,
|
||||
|
@ -127,6 +128,7 @@ module.exports = class Lyric {
|
|||
} else {
|
||||
this._lines = lyricLines.map(line => {
|
||||
const fontPlayer = new FontPlayer({
|
||||
time: line.time,
|
||||
lyric: line.text,
|
||||
translationLyric: line.translation,
|
||||
lineClassName: this.lineClassName,
|
||||
|
|
Loading…
Reference in New Issue