354 lines
9.6 KiB
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>
|