lx-music-desktop/src/renderer/components/layout/PlayDetail/LyricPlayer.vue

354 lines
9.6 KiB
Vue

<template>
<div :class="['right', $style.right]" :style="lrcFontSize">
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
<div
v-show="!isShowLrcSelectContent"
ref="dom_lyric"
:class="['lyric', $style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }]" :style="lrcStyles"
@wheel="handleWheel" @mousedown="handleLyricMouseDown"
@contextmenu.stop="handleShowLyricMenu"
>
<div :class="['pre', $style.lyricSpace]" />
<div ref="dom_lyric_text" />
<div :class="$style.lyricSpace" />
</div>
</transition>
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
<div v-if="isShowLyricProgressSetting" v-show="isStopScroll && !isShowLrcSelectContent" :class="$style.skip">
<div ref="dom_skip_line" :class="$style.line" />
<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 1024 1024" space="preserve">
<use xlink:href="#icon-play" />
</svg>
</base-btn>
</div>
</transition>
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
<div v-if="isShowLrcSelectContent" :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" @contextmenu="handleCopySelectText">
<div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]">
<span>{{ info.text }}</span>
<template v-for="(lrc, i) in info.extendedLyrics" :key="i">
<br>
<span :class="$style.lyricSelectlineExtended">{{ lrc }}</span>
</template>
</div>
</div>
</transition>
<LyricMenu v-model="lyricMenuVisible" :xy="lyricMenuXY" :lyric-info="lyricInfo" @update-lyric="handleUpdateLyric" />
</div>
</template>
<script>
import { clipboardWriteText } from '@common/utils/electron'
import { lyric } from '@renderer/store/player/lyric'
import { playProgress } from '@renderer/store/player/playProgress'
import { isFullscreen } from '@renderer/store'
import {
isPlay,
isShowLrcSelectContent,
isShowPlayComment,
musicInfo as playerMusicInfo,
playMusicInfo,
} from '@renderer/store/player/state'
import {
setMusicInfo,
} from '@renderer/store/player/action'
import { onMounted, onBeforeUnmount, computed, reactive, ref, nextTick, watch } from '@common/utils/vueTools'
import useLyric from '@renderer/utils/compositions/useLyric'
import LyricMenu from './components/LyricMenu'
import { appSetting } from '@renderer/store/setting'
import { setLyricOffset } from '@renderer/core/lyric'
export default {
components: {
LyricMenu,
},
setup() {
const isZoomActiveLrc = computed(() => appSetting['playDetail.isZoomActiveLrc'])
const isShowLyricProgressSetting = computed(() => appSetting['playDetail.isShowLyricProgressSetting'])
const {
dom_lyric,
dom_lyric_text,
dom_skip_line,
isMsDown,
isStopScroll,
timeStr,
handleLyricMouseDown,
handleWheel,
handleSkipPlay,
handleSkipMouseEnter,
handleSkipMouseLeave,
handleScrollLrc,
} = useLyric({ isPlay, lyric, playProgress, isShowLyricProgressSetting })
watch([isFullscreen, isShowPlayComment], () => {
setTimeout(handleScrollLrc, 400)
})
const lyricMenuVisible = ref(false)
const lyricMenuXY = reactive({
x: 0,
y: 0,
})
const lyricInfo = reactive({
lyric: '',
tlyric: '',
rlyric: '',
lxlyric: '',
rawlyric: '',
musicInfo: null,
})
const updateMusicInfo = () => {
lyricInfo.lyric = playerMusicInfo.lrc
lyricInfo.tlyric = playerMusicInfo.tlrc
lyricInfo.rlyric = playerMusicInfo.rlrc
lyricInfo.lxlyric = playerMusicInfo.lxlrc
lyricInfo.rawlyric = playerMusicInfo.rawlrc
lyricInfo.musicInfo = playMusicInfo.musicInfo
}
const handleShowLyricMenu = event => {
updateMusicInfo()
lyricMenuXY.x = event.pageX
lyricMenuXY.y = event.pageY
if (lyricMenuVisible.value) return
nextTick(() => {
lyricMenuVisible.value = true
})
}
const handleUpdateLyric = ({ lyric, tlyric, rlyric, lxlyric, offset }) => {
setMusicInfo({
lrc: lyric,
tlrc: tlyric,
rlrc: rlyric,
lxlrc: lxlyric,
})
console.log(offset)
setLyricOffset(offset)
}
const lrcStyles = computed(() => {
return {
textAlign: appSetting['playDetail.style.align'],
}
})
const lrcFontSize = computed(() => {
let size = appSetting['playDetail.style.fontSize'] / 100
if (isFullscreen.value) size = size *= 1.4
return {
'--playDetail-lrc-font-size': (isShowPlayComment.value ? size * 0.82 : size) + 'rem',
}
})
onMounted(() => {
window.app_event.on('lyricUpdated', updateMusicInfo)
})
onBeforeUnmount(() => {
window.app_event.off('lyricUpdated', updateMusicInfo)
})
return {
dom_lyric,
dom_lyric_text,
dom_skip_line,
isMsDown,
timeStr,
handleLyricMouseDown,
handleWheel,
handleSkipPlay,
handleSkipMouseEnter,
handleSkipMouseLeave,
lyric,
lrcStyles,
lrcFontSize,
isShowLrcSelectContent,
isShowLyricProgressSetting,
isZoomActiveLrc,
isStopScroll,
lyricMenuVisible,
lyricMenuXY,
handleShowLyricMenu,
handleUpdateLyric,
lyricInfo,
}
},
methods: {
handleCopySelectText() {
let str = window.getSelection().toString()
str = str.trim()
if (!str.length) return
clipboardWriteText(str)
},
},
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.right {
flex: 0 0 60%;
// padding: 0 30px;
position: relative;
transition: flex-basis @transition-normal;
}
.lyric {
text-align: center;
height: 100%;
overflow: hidden;
font-size: var(--playDetail-lrc-font-size, 16px);
-webkit-mask-image: linear-gradient(transparent 0%, #fff 20%, #fff 80%, transparent 100%);
cursor: grab;
&.draging {
cursor: grabbing;
}
:global {
.font-lrc {
color: var(--color-200);
}
.line-content {
line-height: 1.2;
padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 1px;
overflow-wrap: break-word;
color: var(--color-200);
transition: @transition-normal;
transition-property: padding;
.extended {
font-size: 0.8em;
margin-top: 5px;
}
&.line-mode {
.font-lrc {
transition: @transition-fast;
transition-property: font-size, color;
}
}
&.line-mode.active .font-lrc, &.font-mode.played .font-lrc {
color: var(--color-primary-dark-200);
}
&.font-mode .extended .font-lrc {
transition: @transition-slow;
transition-property: font-size, color;
}
&.font-mode > .line > .font-lrc {
> span {
transition: @transition-normal;
transition-property: font-size;
font-size: 1em;
background-repeat: no-repeat;
background-color: var(--color-200);
background-image: -webkit-linear-gradient(top, var(--color-primary-dark-200), var(--color-primary-dark-200));
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-size: 0 100%;
}
}
}
}
// p {
// padding: 8px 0;
// line-height: 1.2;
// overflow-wrap: break-word;
// transition: @transition-normal !important;
// transition-property: color, font-size;
// }
// .lrc-active {
// color: var(--color-primary);
// font-size: 1.2em;
// }
}
.lrc-active-zoom {
:global {
.line-content {
&.active {
.extended {
font-size: .94em;
}
.line {
font-size: 1.1em;
}
}
}
}
}
.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: 2px dotted var(--color-primary-dark-100);
opacity: .15;
margin-right: 30px;
-webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 15%, #fff 100%);
}
.label {
position: absolute;
right: 30px;
top: -14px;
line-height: 1;
font-size: 12px;
color: var(--color-primary-dark-100);
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-normal;
transition-property: opacity;
opacity: .8;
&:hover {
opacity: .6;
}
}
}
.lyricSelectContent {
position: absolute;
left: 0;
top: 0;
// text-align: center;
height: 100%;
width: 100%;
font-size: 16px;
z-index: 10;
color: var(--color-400);
.lyricSelectline {
padding: 8px 0;
overflow-wrap: break-word;
transition: @transition-normal !important;
transition-property: color, font-size;
line-height: 1.3;
}
.lyricSelectlineExtended {
font-size: 14px;
}
.lrc-active {
color: var(--color-primary);
}
}
.lyric-space {
height: 70%;
}
</style>