lx-music-desktop/src/renderer-lyric/components/core/Lyric.vue

466 lines
13 KiB
Vue

<template lang="pug">
div(:class="[$style.lyric, lyricEvent.isMsDown ? $style.draging : null]" :style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric")
div(:class="$style.lyricSpace")
div(:class="[$style.lyricText]" ref="dom_lyric_text")
//- div(v-for="(info, index) in lyricLines" :key="index" :class="[$style.lineContent, lyric.line == index ? (lrcConfig.style.isZoomActiveLrc ? $style.lrcActiveZoom : $style.lrcActive) : null]")
p(:class="$style.lrcLine") {{info.text}}
div(:class="$style.lyricSpace")
</template>
<script>
import { rendererOn, rendererSend, NAMES } from '../../../common/ipc'
import { scrollTo } from '../../../renderer/utils'
import Lyric from '@renderer/utils/lyric-font-player'
let cancelScrollFn = null
export default {
props: {
lrcConfig: {
type: Object,
default() {
return {
style: {
fontSize: 125,
opacity: 80,
isZoomActiveLrc: true,
},
}
},
},
isPlayLxlrc: {
type: Boolean,
default: true,
},
isShowLyricTransition: {
type: Boolean,
default: true,
},
},
data() {
return {
musicInfo: {
songmid: null,
name: '^',
singer: '^',
album: null,
},
lyric: {
line: 0,
lines: [],
},
dom_lines: [],
clickTime: 0,
lyricEvent: {
isMsDown: false,
msDownY: 0,
msDownScrollY: 0,
isStopScroll: false,
timeout: null,
},
winEvent: {
isMsDown: false,
msDownX: 0,
msDownY: 0,
},
_lyricLines: [],
lyricLines: [],
isSetedLines: false,
isPlay: false,
lyrics: {
lyric: '',
tlyric: '',
lxlyric: '',
},
}
},
computed: {
lrcStyles() {
return {
fontSize: this.lrcConfig.style.fontSize / 100 + 'rem',
opacity: this.lrcConfig.style.opacity / 100,
}
},
},
watch: {
'lyric.lines': {
handler(n, o) {
this.isSetedLines = true
if (o) {
this._lyricLines = n
if (n.length) {
this.lyricLines = n
this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc()
})
} else {
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
cancelScrollFn = scrollTo(this.$refs.dom_lyric, 0, 300, () => {
if (this.lyricLines === this._lyricLines && this._lyricLines.length) return
this.lyricLines = this._lyricLines
this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc()
})
}, 50)
}
} else {
this.lyricLines = n
this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc()
})
}
},
immediate: true,
},
'lyric.line': {
handler(n) {
if (n < 0) return
if (n == 0 && this.isSetedLines) return this.isSetedLines = false
this.handleScrollLrc()
},
immediate: true,
},
isShowLyricTransition() {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
},
isPlayLxlrc() {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
},
},
created() {
rendererOn(NAMES.winLyric.set_lyric_info, (event, data) => this.handleSetInfo(data))
window.lrc = new Lyric({
lineClassName: 'lrc-content',
fontClassName: 'font',
shadowClassName: 'shadow',
shadowContent: true,
activeLineClassName: 'active',
onPlay: (line, text) => {
this.lyric.text = text
this.lyric.line = line
// console.log(line, text)
},
onSetLyric: lines => { // listening lyrics seting event
// console.log(lines) // lines is array of all lyric text
this.$refs.dom_lyric_text.textContent = ''
const dom_lines = document.createDocumentFragment()
for (const line of lines) {
dom_lines.appendChild(line.dom_line)
}
this.$refs.dom_lyric_text.appendChild(dom_lines)
this.lyric.lines = lines
this.lyric.line = 0
},
offset: 100,
})
},
mounted() {
document.addEventListener('mousemove', this.handleMouseMsMove)
document.addEventListener('mouseup', this.handleMouseMsUp)
rendererSend(NAMES.winLyric.get_lyric_info, 'info')
},
beforeDestroy() {
this.clearLyricScrollTimeout()
document.removeEventListener('mousemove', this.handleMouseMsMove)
document.removeEventListener('mouseup', this.handleMouseMsUp)
},
methods: {
handleSetInfo({ type, data }) {
// console.log(type, data)
switch (type) {
case 'lyric':
this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc
this.lyrics.lxlyric = data.lxlrc
this.setLyric()
break
case 'play':
this.isPlay = true
window.lrc.play(data)
break
case 'pause':
this.isPlay = false
window.lrc.pause()
break
case 'info':
// console.log('info', data)
this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc
this.lyrics.lxlyric = data.lxlrc
this.setLyric()
this.$nextTick(() => {
this.lyric.line = data.line
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
})
case 'music_info':
this.musicInfo.name = data.name
this.musicInfo.songmid = data.songmid
this.musicInfo.singer = data.singer
this.musicInfo.album = data.album
break
case 'status':
// console.log('status', data)
this.isPlay = data.isPlay
this.lyric.line = data.line
if (data.isPlay) window.lrc.play(data.played_time)
break
default:
break
}
// console.log(data)
},
handleResize() {
this.setProgressWidth()
},
handleScrollLrc() {
if (!this.dom_lines.length) return
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
if (this.lyricEvent.isStopScroll) return
let dom_p = this.dom_lines[this.lyric.line]
cancelScrollFn = scrollTo(this.$refs.dom_lyric, dom_p ? (dom_p.offsetTop - this.$refs.dom_lyric.clientHeight * 0.5 + dom_p.clientHeight / 2) : 0)
},
handleLyricMouseDown(e) {
if (e.target.classList.contains('font') ||
e.target.parentNode.classList.contains('font') ||
e.target.classList.contains('translation') ||
e.target.parentNode.classList.contains('translation')) {
this.lyricEvent.isMsDown = true
this.lyricEvent.msDownY = e.clientY
this.lyricEvent.msDownScrollY = this.$refs.dom_lyric.scrollTop
} else {
this.winEvent.isMsDown = true
this.winEvent.msDownX = e.clientX
this.winEvent.msDownY = e.clientY
}
},
handleMouseMsUp(e) {
this.lyricEvent.isMsDown = false
this.winEvent.isMsDown = false
},
handleMouseMsMove(e) {
if (this.lyricEvent.isMsDown) {
if (!this.lyricEvent.isStopScroll) this.lyricEvent.isStopScroll = true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
this.$refs.dom_lyric.scrollTop = this.lyricEvent.msDownScrollY + this.lyricEvent.msDownY - e.clientY
this.startLyricScrollTimeout()
} else if (this.winEvent.isMsDown) {
rendererSend(NAMES.winLyric.set_win_bounds, {
x: e.clientX - this.winEvent.msDownX,
y: e.clientY - this.winEvent.msDownY,
w: window.innerWidth,
h: window.innerHeight,
})
}
// if (this.volumeEvent.isMsDown) {
// let val = this.volumeEvent.msDownValue + (e.clientX - this.volumeEvent.msDownX) / 70
// this.volume = val < 0 ? 0 : val > 1 ? 1 : val
// if (this.audio) this.audio.volume = this.volume
// }
// console.log(val)
},
startLyricScrollTimeout() {
this.clearLyricScrollTimeout()
this.lyricEvent.timeout = setTimeout(() => {
this.lyricEvent.timeout = null
this.lyricEvent.isStopScroll = false
if (!this.isPlay) return
this.handleScrollLrc()
}, 3000)
},
handleWheel(event) {
// console.log(event.deltaY)
if (!this.lyricEvent.isStopScroll) this.lyricEvent.isStopScroll = true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
}
this.$refs.dom_lyric.scrollTop = this.$refs.dom_lyric.scrollTop + event.deltaY
this.startLyricScrollTimeout()
},
clearLyricScrollTimeout() {
if (!this.lyricEvent.timeout) return
clearTimeout(this.lyricEvent.timeout)
this.lyricEvent.timeout = null
},
close() {
rendererSend(NAMES.winLyric.close)
},
setLyric() {
window.lrc.setLyric(
this.isPlayLxlrc && this.lyrics.lxlyric ? this.lyrics.lxlyric : this.lyrics.lyric,
this.isShowLyricTransition && this.lyrics.tlyric ? this.lyrics.tlyric : '',
// (this.isShowLyricTransition && this.lyrics.tlyric ? (this.lyrics.tlyric + '\n') : '') + (this.lyrics.lyric || ''),
)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.lyric {
text-align: center;
height: 100%;
overflow: hidden;
font-size: 16px;
cursor: grab;
color: @color-theme-lyric;
&.draging {
cursor: grabbing;
}
:global {
.lrc-content {
line-height: 1.2;
margin: 16px 0;
overflow-wrap: break-word;
.font {
display: inline-block;
}
.translation {
transition: @transition-theme !important;
transition-property: font-size, color;
font-size: 0.8em;
margin-top: 5px;
}
.line {
transition-property: font-size, color !important;
background: none !important;
-webkit-text-fill-color: unset;
// -webkit-text-fill-color: none !important;
}
&.active {
.line {
color: @color-theme;
}
.translation {
font-size: 1em;
color: @color-theme;
}
span {
// color: @color-theme;
font-size: 1.2em;
}
}
span {
transition: @transition-theme !important;
transition-property: font-size !important;
font-size: 1em;
background-repeat: no-repeat;
background-color: @color-theme-lyric;
background-image: -webkit-linear-gradient(top, @color-theme, @color-theme);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-size: 0 100%;
}
}
.shadow {
text-shadow: 1px 1px 2px rgb(32, 32, 32);
}
}
// p {
// padding: 8px 0;
// line-height: 1.2;
// overflow-wrap: break-word;
// transition: @transition-theme !important;
// transition-property: color, font-size;
// }
}
// .lrc-line {
// display: inline-block;
// padding: 8px 0;
// line-height: 1.2;
// overflow-wrap: break-word;
// transition: @transition-theme;
// transition-property: color, font-size, text-shadow;
// cursor: grab;
// // font-weight: bold;
// // background-clip: text;
// color: @color-theme-lyric;
// text-shadow: 1px 1px 2px #000;
// // background: linear-gradient(@color-theme-lyric, @color-theme-lyric);
// // background-clip: text;
// // -webkit-background-clip: text;
// // -webkit-text-fill-color: #fff;
// // -webkit-text-stroke: thin #124628;
// }
.lyric-space {
height: 70%;
}
.lrc-active {
.lrc-line {
color: @color-theme-lyric_2;
// background: linear-gradient(@color-theme-lyric, @color-theme-lyric_2);
// background-clip: text;
// -webkit-background-clip: text;
// -webkit-text-fill-color: @color-theme-lyric_2;
// -webkit-text-stroke: thin #124628;
}
}
.lrc-active-zoom {
.lrc-active;
font-size: 1.2em;
}
.footer {
flex: 0 0 100px;
overflow: hidden;
display: flex;
align-items: center;
}
each(@themes, {
:global(#container.@{value}) {
// .lrc-line {
// color: ~'@{color-@{value}-theme-lyric}';
// }
.lrc-active, .lrc-active-zoom {
.lrc-line {
color: ~'@{color-@{value}-theme-lyric_2}';
}
}
.lyric {
color: ~'@{color-@{value}-theme-lyric}';
:global {
.lrc-content {
&.active {
.line {
color: ~'@{color-@{value}-theme}';
}
}
span {
// background-color: ~'@{color-@{value}-theme_2-font}';
background-image: -webkit-linear-gradient(top, ~'@{color-@{value}-theme}', ~'@{color-@{value}-theme}');
}
}
}
}
}
})
</style>