新增通过播放详情页歌词调整播放进度
parent
34febf4949
commit
cfee4a0b24
|
@ -1,6 +1,7 @@
|
||||||
### 新增
|
### 新增
|
||||||
|
|
||||||
- 新增对播放详情页歌词大小、是否缩放、对齐方式的设置,可以去设置-播放详情页设置查看
|
- 新增对播放详情页歌词大小、是否缩放、对齐方式的设置,可以去设置-播放详情页设置查看
|
||||||
|
- 新增通过播放详情页歌词调整播放进度
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
<template>
|
<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="['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 ref="dom_lyric_text"></div>
|
||||||
<div :class="$style.lyricSpace"></div>
|
<div :class="$style.lyricSpace"></div>
|
||||||
</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">
|
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
|
||||||
<div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
|
<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 }]">
|
<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 {
|
const {
|
||||||
dom_lyric,
|
dom_lyric,
|
||||||
dom_lyric_text,
|
dom_lyric_text,
|
||||||
|
dom_skip_line,
|
||||||
isMsDown,
|
isMsDown,
|
||||||
|
isStopScroll,
|
||||||
|
timeStr,
|
||||||
handleLyricMouseDown,
|
handleLyricMouseDown,
|
||||||
handleWheel,
|
handleWheel,
|
||||||
|
handleSkipPlay,
|
||||||
|
handleSkipMouseEnter,
|
||||||
|
handleSkipMouseLeave,
|
||||||
} = useLyric({ isPlay, lyric })
|
} = useLyric({ isPlay, lyric })
|
||||||
|
|
||||||
const fontSizeUp = () => {
|
const fontSizeUp = () => {
|
||||||
|
@ -48,6 +65,10 @@ export default {
|
||||||
const lrcStyles = computed(() => {
|
const lrcStyles = computed(() => {
|
||||||
return {
|
return {
|
||||||
textAlign: setting.value.playDetail.style.align,
|
textAlign: setting.value.playDetail.style.align,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const lrcFontSize = computed(() => {
|
||||||
|
return {
|
||||||
'--playDetail-lrc-font-size': setting.value.playDetail.style.fontSize / 100 + 'rem',
|
'--playDetail-lrc-font-size': setting.value.playDetail.style.fontSize / 100 + 'rem',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -69,13 +90,20 @@ export default {
|
||||||
return {
|
return {
|
||||||
dom_lyric,
|
dom_lyric,
|
||||||
dom_lyric_text,
|
dom_lyric_text,
|
||||||
|
dom_skip_line,
|
||||||
isMsDown,
|
isMsDown,
|
||||||
|
timeStr,
|
||||||
handleLyricMouseDown,
|
handleLyricMouseDown,
|
||||||
handleWheel,
|
handleWheel,
|
||||||
|
handleSkipPlay,
|
||||||
|
handleSkipMouseEnter,
|
||||||
|
handleSkipMouseLeave,
|
||||||
lyric,
|
lyric,
|
||||||
isShowLrcSelectContent,
|
isShowLrcSelectContent,
|
||||||
lrcStyles,
|
lrcStyles,
|
||||||
|
lrcFontSize,
|
||||||
isZoomActiveLrc,
|
isZoomActiveLrc,
|
||||||
|
isStopScroll,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -133,11 +161,11 @@ export default {
|
||||||
:global {
|
:global {
|
||||||
.lrc-content {
|
.lrc-content {
|
||||||
line-height: 1.2;
|
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;
|
overflow-wrap: break-word;
|
||||||
color: @color-player-detail-lyric;
|
color: @color-player-detail-lyric;
|
||||||
transition: @transition-theme;
|
transition: @transition-theme;
|
||||||
transition-property: margin;
|
transition-property: padding;
|
||||||
|
|
||||||
.translation {
|
.translation {
|
||||||
transition: @transition-theme !important;
|
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 {
|
.lyricSelectContent {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -267,6 +339,14 @@ each(@themes, {
|
||||||
// .lrc-active {
|
// .lrc-active {
|
||||||
// color: ~'@{color-@{value}-theme}';
|
// color: ~'@{color-@{value}-theme}';
|
||||||
// }
|
// }
|
||||||
|
.skip {
|
||||||
|
.line {
|
||||||
|
border-top-color: ~'@{color-@{value}-player-detail-lyric-active}';
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
color:~'@{color-@{value}-player-detail-lyric-active}';
|
||||||
|
}
|
||||||
|
}
|
||||||
.lyricSelectContent {
|
.lyricSelectContent {
|
||||||
background-color: ~'@{color-@{value}-theme_2-background_1}';
|
background-color: ~'@{color-@{value}-theme_2-background_1}';
|
||||||
color: ~'@{color-@{value}-player-detail-lyric}';
|
color: ~'@{color-@{value}-player-detail-lyric}';
|
||||||
|
|
|
@ -1,18 +1,74 @@
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from '@renderer/utils/vueTools'
|
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 }) => {
|
export default ({ isPlay, lyric }) => {
|
||||||
const dom_lyric = ref(null)
|
const dom_lyric = ref(null)
|
||||||
const dom_lyric_text = ref(null)
|
const dom_lyric_text = ref(null)
|
||||||
|
const dom_skip_line = ref(null)
|
||||||
const isMsDown = ref(false)
|
const isMsDown = ref(false)
|
||||||
|
const isStopScroll = ref(false)
|
||||||
|
const timeStr = ref('--/--')
|
||||||
|
|
||||||
let msDownY = 0
|
let msDownY = 0
|
||||||
let msDownScrollY = 0
|
let msDownScrollY = 0
|
||||||
let isStopScroll = false
|
|
||||||
let timeout = null
|
let timeout = null
|
||||||
let cancelScrollFn
|
let cancelScrollFn
|
||||||
let dom_lines
|
let dom_lines
|
||||||
let isSetedLines = false
|
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) => {
|
const handleScrollLrc = (duration = 300) => {
|
||||||
if (!dom_lines?.length || !dom_lyric.value) return
|
if (!dom_lines?.length || !dom_lyric.value) return
|
||||||
|
@ -20,7 +76,8 @@ export default ({ isPlay, lyric }) => {
|
||||||
cancelScrollFn()
|
cancelScrollFn()
|
||||||
cancelScrollFn = null
|
cancelScrollFn = null
|
||||||
}
|
}
|
||||||
if (isStopScroll) return
|
if (isSkipMouseEnter) return
|
||||||
|
if (isStopScroll.value) return
|
||||||
let dom_p = dom_lines[lyric.line]
|
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)
|
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 = () => {
|
const startLyricScrollTimeout = () => {
|
||||||
clearLyricScrollTimeout()
|
clearLyricScrollTimeout()
|
||||||
|
if (isSkipMouseEnter) return
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
timeout = null
|
timeout = null
|
||||||
isStopScroll = false
|
isStopScroll.value = false
|
||||||
if (!isPlay.value) return
|
if (!isPlay.value) return
|
||||||
handleScrollLrc()
|
handleScrollLrc()
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
@ -53,25 +111,27 @@ export default ({ isPlay, lyric }) => {
|
||||||
}
|
}
|
||||||
const handleMouseMsMove = event => {
|
const handleMouseMsMove = event => {
|
||||||
if (isMsDown.value) {
|
if (isMsDown.value) {
|
||||||
if (!isStopScroll) isStopScroll = true
|
if (!isStopScroll.value) isStopScroll.value = true
|
||||||
if (cancelScrollFn) {
|
if (cancelScrollFn) {
|
||||||
cancelScrollFn()
|
cancelScrollFn()
|
||||||
cancelScrollFn = null
|
cancelScrollFn = null
|
||||||
}
|
}
|
||||||
dom_lyric.value.scrollTop = msDownScrollY + msDownY - event.clientY
|
dom_lyric.value.scrollTop = msDownScrollY + msDownY - event.clientY
|
||||||
startLyricScrollTimeout()
|
startLyricScrollTimeout()
|
||||||
|
setTime()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleWheel = (event) => {
|
const handleWheel = (event) => {
|
||||||
console.log(event.deltaY)
|
console.log(event.deltaY)
|
||||||
if (!isStopScroll) isStopScroll = true
|
if (!isStopScroll.value) isStopScroll.value = true
|
||||||
if (cancelScrollFn) {
|
if (cancelScrollFn) {
|
||||||
cancelScrollFn()
|
cancelScrollFn()
|
||||||
cancelScrollFn = null
|
cancelScrollFn = null
|
||||||
}
|
}
|
||||||
dom_lyric.value.scrollTop = dom_lyric.value.scrollTop + event.deltaY
|
dom_lyric.value.scrollTop = dom_lyric.value.scrollTop + event.deltaY
|
||||||
startLyricScrollTimeout()
|
startLyricScrollTimeout()
|
||||||
|
setTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
const setLyric = (lines) => {
|
const setLyric = (lines) => {
|
||||||
|
@ -139,8 +199,14 @@ export default ({ isPlay, lyric }) => {
|
||||||
return {
|
return {
|
||||||
dom_lyric,
|
dom_lyric,
|
||||||
dom_lyric_text,
|
dom_lyric_text,
|
||||||
|
dom_skip_line,
|
||||||
|
isStopScroll,
|
||||||
isMsDown,
|
isMsDown,
|
||||||
|
timeStr,
|
||||||
handleLyricMouseDown,
|
handleLyricMouseDown,
|
||||||
handleWheel,
|
handleWheel,
|
||||||
|
handleSkipPlay,
|
||||||
|
handleSkipMouseEnter,
|
||||||
|
handleSkipMouseLeave,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ const createAnimation = (dom, duration) => new window.Animation(new window.Keyfr
|
||||||
// https://jsfiddle.net/ceqpnbky/1/
|
// https://jsfiddle.net/ceqpnbky/1/
|
||||||
|
|
||||||
module.exports = class FontPlayer {
|
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.lyric = lyric
|
||||||
this.translationLyric = translationLyric
|
this.translationLyric = translationLyric
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ module.exports = class FontPlayer {
|
||||||
this.isLineMode = false
|
this.isLineMode = false
|
||||||
|
|
||||||
this.lineContent = document.createElement('div')
|
this.lineContent = document.createElement('div')
|
||||||
|
this.lineContent.time = this.time
|
||||||
if (this.lineClassName) this.lineContent.classList.add(this.lineClassName)
|
if (this.lineClassName) this.lineContent.classList.add(this.lineClassName)
|
||||||
this.fontContent = document.createElement('div')
|
this.fontContent = document.createElement('div')
|
||||||
this.fontContent.style = 'position:relative;display:inline-block;'
|
this.fontContent.style = 'position:relative;display:inline-block;'
|
||||||
|
|
|
@ -106,6 +106,7 @@ module.exports = class Lyric {
|
||||||
if (this.isLineMode) {
|
if (this.isLineMode) {
|
||||||
this._lines = lyricLines.map(line => {
|
this._lines = lyricLines.map(line => {
|
||||||
const fontPlayer = new FontPlayer({
|
const fontPlayer = new FontPlayer({
|
||||||
|
time: line.time,
|
||||||
lyric: line.text,
|
lyric: line.text,
|
||||||
translationLyric: line.translation,
|
translationLyric: line.translation,
|
||||||
lineClassName: this.lineClassName,
|
lineClassName: this.lineClassName,
|
||||||
|
@ -127,6 +128,7 @@ module.exports = class Lyric {
|
||||||
} else {
|
} else {
|
||||||
this._lines = lyricLines.map(line => {
|
this._lines = lyricLines.map(line => {
|
||||||
const fontPlayer = new FontPlayer({
|
const fontPlayer = new FontPlayer({
|
||||||
|
time: line.time,
|
||||||
lyric: line.text,
|
lyric: line.text,
|
||||||
translationLyric: line.translation,
|
translationLyric: line.translation,
|
||||||
lineClassName: this.lineClassName,
|
lineClassName: this.lineClassName,
|
||||||
|
|
Loading…
Reference in New Issue