v2.0.0
This commit is contained in:
+42
-277
@@ -1,286 +1,51 @@
|
||||
<template lang="pug">
|
||||
#container(:class="[theme, { lock: lrcConfig.isLock }, { hide: isHoverHide && isMouseEnter }]")
|
||||
#main(@mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave" @mousemove="handleMouseMoveMain")
|
||||
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
|
||||
.control-bar(v-show="!lrcConfig.isLock")
|
||||
core-control-bar(:lrcConfig="lrcConfig" :themes="themeList")
|
||||
core-lyric(:lrcConfig="lrcConfig" :isPlayLxlrc="isPlayLxlrc" :isShowLyricTranslation="isShowLyricTranslation" :isShowLyricRoma="isShowLyricRoma")
|
||||
div.resize-left(@mousedown.self="handleMouseDown('left', $event)" @touchstart.self="handleTouchDown('left', $event)")
|
||||
div.resize-top(@mousedown.self="handleMouseDown('top', $event)" @touchstart.self="handleTouchDown('top', $event)")
|
||||
div.resize-right(@mousedown.self="handleMouseDown('right', $event)" @touchstart.self="handleTouchDown('right', $event)")
|
||||
div.resize-bottom(@mousedown.self="handleMouseDown('bottom', $event)" @touchstart.self="handleTouchDown('bottom', $event)")
|
||||
div.resize-top-left(@mousedown.self="handleMouseDown('top-left', $event)" @touchstart.self="handleTouchDown('top-left', $event)")
|
||||
div.resize-top-right(@mousedown.self="handleMouseDown('top-right', $event)" @touchstart.self="handleTouchDown('top-right', $event)")
|
||||
div.resize-bottom-left(@mousedown.self="handleMouseDown('bottom-left', $event)" @touchstart.self="handleTouchDown('bottom-left', $event)")
|
||||
div.resize-bottom-right(@mousedown.self="handleMouseDown('bottom-right', $event)" @touchstart.self="handleTouchDown('bottom-right', $event)")
|
||||
core-icons
|
||||
<template>
|
||||
<div id="container" :class="[{ lock: setting['desktopLyric.isLock'] }, { hide: isHoverHide && isMouseEnter }]">
|
||||
<div id="main" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave" @mousemove="handleMouseMoveMain">
|
||||
<transition enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut">
|
||||
<div v-show="!setting['desktopLyric.isLock']" class="control-bar">
|
||||
<layout-control-bar />
|
||||
</div>
|
||||
</transition>
|
||||
<layout-lyric-vertical v-if="setting['desktopLyric.direction'] == 'vertical'" />
|
||||
<layout-lyric-horizontal v-else />
|
||||
<transition enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut">
|
||||
<common-audio-visualizer v-if="setting['desktopLyric.audioVisualization']" />
|
||||
</transition>
|
||||
</div>
|
||||
<div class="resize-left" @mousedown.self="handleMouseDown('left', $event)" @touchstart.self="handleTouchDown('left', $event)" />
|
||||
<div class="resize-top" @mousedown.self="handleMouseDown('top', $event)" @touchstart.self="handleTouchDown('top', $event)" />
|
||||
<div class="resize-right" @mousedown.self="handleMouseDown('right', $event)" @touchstart.self="handleTouchDown('right', $event)" />
|
||||
<div class="resize-bottom" @mousedown.self="handleMouseDown('bottom', $event)" @touchstart.self="handleTouchDown('bottom', $event)" />
|
||||
<div class="resize-top-left" @mousedown.self="handleMouseDown('top-left', $event)" @touchstart.self="handleTouchDown('top-left', $event)" />
|
||||
<div class="resize-top-right" @mousedown.self="handleMouseDown('top-right', $event)" @touchstart.self="handleTouchDown('top-right', $event)" />
|
||||
<div class="resize-bottom-left" @mousedown.self="handleMouseDown('bottom-left', $event)" @touchstart.self="handleTouchDown('bottom-left', $event)" />
|
||||
<div class="resize-bottom-right" @mousedown.self="handleMouseDown('bottom-right', $event)" @touchstart.self="handleTouchDown('bottom-right', $event)" />
|
||||
<layout-icons />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rendererOn, rendererInvoke, rendererSend, NAMES } from '../common/ipc'
|
||||
<script setup>
|
||||
import useWindowSize from '@lyric/useApp/useWindowSize'
|
||||
import useHoverHide from '@lyric/useApp/useHoverHide'
|
||||
import { onMounted } from '@common/utils/vueTools'
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { sendConnectMainWindowEvent } from '@lyric/utils/ipc'
|
||||
import useLyric from '@lyric/useApp/useLyric'
|
||||
import useTheme from '@lyric/useApp/useTheme'
|
||||
import { init as initLyricPlayer } from '@lyric/core/lyric'
|
||||
|
||||
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
|
||||
|
||||
let mouseCheckTools = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
preX: 0,
|
||||
preY: 0,
|
||||
timeout: null,
|
||||
handleCheck(setShow) {
|
||||
let xDiff = Math.abs(this.x - this.preX)
|
||||
let yDiff = Math.abs(this.y - this.preY)
|
||||
if (xDiff > 8) {
|
||||
if (this.x > this.preX) {
|
||||
if (this.x + xDiff * 1.25 > window.innerWidth - 16) return setShow()
|
||||
} else {
|
||||
if (this.x - xDiff * 1.25 < 8) return setShow()
|
||||
}
|
||||
}
|
||||
if (yDiff > 8) {
|
||||
if (this.y > this.preY) {
|
||||
if (this.y + yDiff * 1.25 > window.innerHeight - 16) return setShow()
|
||||
} else {
|
||||
if (this.y - yDiff * 1.25 < 8) return setShow()
|
||||
}
|
||||
}
|
||||
const { handleMouseDown, handleTouchDown } = useWindowSize()
|
||||
const { handleMouseMoveMain, isHoverHide, isMouseEnter } = useHoverHide()
|
||||
useLyric()
|
||||
useTheme()
|
||||
|
||||
// setShow(false)
|
||||
},
|
||||
handleMove(x, y, setShow) {
|
||||
// console.log(x, y, this.x, this.y)
|
||||
this.preX = this.x
|
||||
this.preY = this.y
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.startTimeout(setShow)
|
||||
},
|
||||
startTimeout(setShow) {
|
||||
this.stopTimeout()
|
||||
this.timeout = setTimeout(this.handleCheck.bind(this), 200, setShow)
|
||||
},
|
||||
stopTimeout() {
|
||||
if (!this.timeout) return
|
||||
clearTimeout(this.timeout)
|
||||
this.timeout = null
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
resize: {
|
||||
origin: null,
|
||||
msDownX: 0,
|
||||
msDownY: 0,
|
||||
},
|
||||
lrcConfig: {
|
||||
enable: false,
|
||||
isLock: true,
|
||||
isAlwaysOnTop: false,
|
||||
isDelayScroll: true,
|
||||
width: 600,
|
||||
height: 700,
|
||||
x: -1,
|
||||
y: -1,
|
||||
theme: 0,
|
||||
isHoverHide: false,
|
||||
style: {
|
||||
font: '',
|
||||
fontSize: 125,
|
||||
opacity: 80,
|
||||
isZoomActiveLrc: true,
|
||||
},
|
||||
},
|
||||
isShowLyricTranslation: false,
|
||||
isShowLyricRoma: false,
|
||||
isPlayLxlrc: true,
|
||||
themeList: [
|
||||
{
|
||||
id: 0,
|
||||
className: 'green',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
className: 'yellow',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
className: 'blue',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
className: 'red',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
className: 'pink',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
className: 'purple',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
className: 'orange',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
className: 'grey',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
className: 'ming',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
className: 'blue2',
|
||||
},
|
||||
],
|
||||
isMouseEnter: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
theme() {
|
||||
let theme = this.themeList.find(t => t.id == this.lrcConfig.theme) || this.themeList[0]
|
||||
return theme.className
|
||||
},
|
||||
isHoverHide() {
|
||||
return this.lrcConfig.isLock && this.lrcConfig.isHoverHide
|
||||
},
|
||||
},
|
||||
created() {
|
||||
rendererOn(NAMES.winLyric.set_lyric_config, (event, config) => this.handleUpdateConfig(config))
|
||||
rendererInvoke(NAMES.winLyric.get_lyric_config).then(config => this.handleUpdateConfig(config))
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('mousemove', this.handleMouseMove)
|
||||
document.addEventListener('mouseup', this.handleMouseUp)
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('mousemove', this.handleMouseMove)
|
||||
document.removeEventListener('mouseup', this.handleMouseUp)
|
||||
},
|
||||
methods: {
|
||||
handleUpdateConfig({ config, languageId, isShowLyricTranslation, isShowLyricRoma, isPlayLxlrc }) {
|
||||
this.lrcConfig = config
|
||||
this.isShowLyricTranslation = isShowLyricTranslation
|
||||
this.isShowLyricRoma = isShowLyricRoma
|
||||
this.isPlayLxlrc = isPlayLxlrc
|
||||
if (this.$i18n.locale !== languageId && languageId != null) this.$i18n.locale = languageId
|
||||
},
|
||||
handleDown(origin, clientX, clientY) {
|
||||
this.handleMouseUp()
|
||||
this.resize.origin = origin
|
||||
this.resize.msDownX = clientX
|
||||
this.resize.msDownY = clientY
|
||||
},
|
||||
handleMouseUp() {
|
||||
this.resize.origin = null
|
||||
},
|
||||
handleMouseDown(origin, event) {
|
||||
this.handleDown(origin, event.clientX, event.clientY)
|
||||
},
|
||||
handleTouchDown(origin, event) {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
this.handleDown(origin, touch.clientX, touch.clientY)
|
||||
}
|
||||
},
|
||||
handleMouseMove(event) {
|
||||
this.handleMove(event.clientX, event.clientY)
|
||||
},
|
||||
handleTouchMove(event) {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
this.handleMove(touch.clientX, touch.clientY)
|
||||
}
|
||||
},
|
||||
handleMove(clientX, clientY) {
|
||||
if (!this.resize.origin || this.lrcConfig.isLock) return
|
||||
// if (!event.target.classList.contains('resize-' + this.resize.origin)) return
|
||||
// console.log(event.target)
|
||||
let bounds = {
|
||||
w: 0,
|
||||
h: 0,
|
||||
}
|
||||
let temp
|
||||
switch (this.resize.origin) {
|
||||
case 'left':
|
||||
temp = clientX - this.resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
break
|
||||
case 'right':
|
||||
bounds.w = clientX - this.resize.msDownX
|
||||
this.resize.msDownX += bounds.w
|
||||
break
|
||||
case 'top':
|
||||
temp = clientY - this.resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
break
|
||||
case 'bottom':
|
||||
bounds.h = clientY - this.resize.msDownY
|
||||
this.resize.msDownY += bounds.h
|
||||
break
|
||||
case 'top-left':
|
||||
temp = clientX - this.resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
temp = clientY - this.resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
break
|
||||
case 'top-right':
|
||||
temp = clientY - this.resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
bounds.w = clientX - this.resize.msDownX
|
||||
this.resize.msDownX += bounds.w
|
||||
break
|
||||
case 'bottom-left':
|
||||
temp = clientX - this.resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
bounds.h = clientY - this.resize.msDownY
|
||||
this.resize.msDownY += bounds.h
|
||||
break
|
||||
case 'bottom-right':
|
||||
bounds.w = clientX - this.resize.msDownX
|
||||
this.resize.msDownX += bounds.w
|
||||
bounds.h = clientY - this.resize.msDownY
|
||||
this.resize.msDownY += bounds.h
|
||||
break
|
||||
}
|
||||
// console.log(bounds)
|
||||
bounds.w = window.innerWidth + bounds.w
|
||||
bounds.h = window.innerHeight + bounds.h
|
||||
rendererSend(NAMES.winLyric.set_win_bounds, bounds)
|
||||
},
|
||||
// handleMouseOver() {
|
||||
// // this.handleMouseUp()
|
||||
// },
|
||||
handleMouseMoveMain(event) {
|
||||
if (!this.isHoverHide) return
|
||||
this.handleMouseEnter()
|
||||
mouseCheckTools.handleMove(event.clientX, event.clientY, () => {
|
||||
this.handleMouseLeave()
|
||||
})
|
||||
},
|
||||
handleMouseEnter() {
|
||||
// console.log('enter - >')
|
||||
if (!this.isHoverHide || this.isMouseEnter) return
|
||||
this.isMouseEnter = true
|
||||
},
|
||||
handleMouseLeave() {
|
||||
// console.log('leave - <')
|
||||
if (!this.isHoverHide) return
|
||||
this.isMouseEnter = false
|
||||
mouseCheckTools.stopTimeout()
|
||||
},
|
||||
},
|
||||
}
|
||||
onMounted(() => {
|
||||
initLyricPlayer()
|
||||
sendConnectMainWindowEvent()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -8,6 +8,13 @@ html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-lyric-unplay: rgba(255, 255, 255, 1);
|
||||
--color-lyric-played: rgba(7, 197, 86, 1);
|
||||
--color-lyric-shadow: rgba(0, 0, 0, 0.14);
|
||||
--color-lyric-shadow-font-mode: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.nobreak {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.mixin-ellipsis() {
|
||||
.mixin-ellipsis(@n: 1) {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
white-space: normal !important;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-line-clamp: @n;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.mixin-ellipsis-2() {
|
||||
@@ -28,3 +28,38 @@
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.stroke(@stroke, @color) {
|
||||
@maxi: @stroke + 1;
|
||||
.i-loop (@i) when (@i > 0) {
|
||||
@maxj: @stroke + 1;
|
||||
.j-loop (@j) when (@j > 0) {
|
||||
text-shadow+: (@i - 1)*(1px) (@j - 1)*(1px) 0 @color;
|
||||
text-shadow+: (@i - 1)*(1px) (@j - 1)*(-1px) 0 @color;
|
||||
text-shadow+: (@i - 1)*(-1px) (@j - 1)*(-1px) 0 @color;
|
||||
text-shadow+: (@i - 1)*(-1px) (@j - 1)*(1px) 0 @color;
|
||||
.j-loop(@j - 1);
|
||||
}
|
||||
.j-loop (0) {}
|
||||
.j-loop(@maxj);
|
||||
.i-loop(@i - 1);
|
||||
}
|
||||
.i-loop (0) {}
|
||||
.i-loop(@maxi);
|
||||
text-shadow+: 0 0 0 @color;
|
||||
}
|
||||
|
||||
.stroke2(@color) {
|
||||
text-shadow: 2px 0 @color, -2px 0 @color, 0 2px @color, 0 -2px @color,
|
||||
1px 1px @color, -1px -1px @color, 1px -1px @color, -1px 1px @color;
|
||||
}
|
||||
// .stroke2(@color) {
|
||||
// text-shadow:
|
||||
// -1px -1px 0px @color,
|
||||
// 0px -1px 0px @color,
|
||||
// 1px -1px 0px @color,
|
||||
// -1px 0px 0px @color,
|
||||
// 1px 0px 0px @color,
|
||||
// -1px 1px 0px @color,
|
||||
// 0px 1px 0px @color,
|
||||
// 1px 1px 0px @color;
|
||||
// }
|
||||
|
||||
@@ -1,66 +1,5 @@
|
||||
@import './colors.less';
|
||||
|
||||
@themes: green, yellow, blue, red, pink, purple, orange, grey, ming, blue2;
|
||||
|
||||
|
||||
// Colors
|
||||
// @color-theme: #03a678;
|
||||
@color-theme: #07c556;
|
||||
@color-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-theme-lyric: @color-theme_2;
|
||||
@color-theme-lyric_2:@color-theme;
|
||||
|
||||
@color-green-theme: #07c556;
|
||||
@color-green-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-green-theme-lyric: @color-green-theme_2;
|
||||
@color-green-theme-lyric_2: @color-green-theme;
|
||||
|
||||
@color-yellow-theme: #fffa12;
|
||||
@color-yellow-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-yellow-theme-lyric: @color-yellow-theme_2;
|
||||
@color-yellow-theme-lyric_2: @color-yellow-theme;
|
||||
|
||||
@color-blue-theme: #19b5fe;
|
||||
@color-blue-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-blue-theme-lyric: @color-blue-theme_2;
|
||||
@color-blue-theme-lyric_2: @color-blue-theme;
|
||||
|
||||
@color-red-theme: #ff1222;
|
||||
@color-red-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-red-theme-lyric: @color-red-theme_2;
|
||||
@color-red-theme-lyric_2: @color-red-theme;
|
||||
|
||||
@color-pink-theme: #f1828d;
|
||||
@color-pink-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-pink-theme-lyric: @color-pink-theme_2;
|
||||
@color-pink-theme-lyric_2: @color-pink-theme;
|
||||
|
||||
@color-purple-theme: #c851d4;
|
||||
@color-purple-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-purple-theme-lyric: @color-purple-theme_2;
|
||||
@color-purple-theme-lyric_2: @color-purple-theme;
|
||||
|
||||
@color-orange-theme: #ffad12;
|
||||
@color-orange-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-orange-theme-lyric: @color-orange-theme_2;
|
||||
@color-orange-theme-lyric_2: @color-orange-theme;
|
||||
|
||||
@color-grey-theme: #bdc3c7;
|
||||
@color-grey-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-grey-theme-lyric: @color-grey-theme_2;
|
||||
@color-grey-theme-lyric_2: @color-grey-theme;
|
||||
|
||||
@color-ming-theme: #336e7b;
|
||||
@color-ming-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-ming-theme-lyric: @color-ming-theme_2;
|
||||
@color-ming-theme-lyric_2: @color-ming-theme;
|
||||
|
||||
@color-blue2-theme: #6b7bd8;
|
||||
@color-blue2-theme_2: rgba(255, 255, 255, 1);
|
||||
@color-blue2-theme-lyric: @color-blue2-theme_2;
|
||||
@color-blue2-theme-lyric_2: @color-blue2-theme;
|
||||
|
||||
|
||||
// Width
|
||||
@width-app-left: 6.6%;
|
||||
|
||||
@@ -78,5 +17,8 @@
|
||||
@radius-border: 4px;
|
||||
|
||||
@transition-theme: .4s ease;
|
||||
@transition-slow: .6s ease;
|
||||
@transition-normal: .4s ease;
|
||||
@transition-fast: .2s ease;
|
||||
|
||||
@form-radius: 3px;
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div :class="$style.content">
|
||||
<canvas ref="dom_canvas" :class="$style.canvas" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onBeforeUnmount, onMounted, watch } from '@common/utils/vueTools'
|
||||
import { useEvent, getAnalyserDataArray } from '@lyric/core/mainWindowChannel'
|
||||
// import { getAnalyser } from '@renderer/plugins/player'
|
||||
import { isPlay, setting } from '@lyric/store/state'
|
||||
|
||||
// const themes = {
|
||||
// green: 'rgba(77,175,124,.16)',
|
||||
// blue: 'rgba(52,152,219,.16)',
|
||||
// yellow: 'rgba(233,212,96,.22)',
|
||||
// orange: 'rgba(245,171,53,.16)',
|
||||
// red: 'rgba(214,69,65,.12)',
|
||||
// pink: 'rgba(241,130,141,.16)',
|
||||
// purple: 'rgba(155,89,182,.14)',
|
||||
// grey: 'rgba(108,122,137,.16)',
|
||||
// ming: 'rgba(51,110,123,.14)',
|
||||
// blue2: 'rgba(79,98,208,.14)',
|
||||
// black: 'rgba(39,39,39,.4)',
|
||||
// mid_autumn: 'rgba(74,55,82,.1)',
|
||||
// naruto: 'rgba(87,144,167,.15)',
|
||||
// happy_new_year: 'rgba(192,57,43,.1)',
|
||||
// }
|
||||
|
||||
const getBarWidth = canvasWidth => {
|
||||
let barWidth = (canvasWidth / 128) * 2.5
|
||||
const width = canvasWidth / 86
|
||||
const diffWidth = barWidth - width
|
||||
// console.log(barWidth - width)
|
||||
// if (barWidth - width > 20) newBarWidth = 20
|
||||
// barWidth = newBarWidth
|
||||
return diffWidth > 32
|
||||
? canvasWidth / 128 // 4k屏、超宽屏直接显示所有频谱条
|
||||
: diffWidth > 12 ? width : barWidth
|
||||
}
|
||||
export default {
|
||||
setup() {
|
||||
const dom_canvas = ref(null)
|
||||
|
||||
let ctx
|
||||
// let bufferLength = 0
|
||||
// let dataArray
|
||||
let WIDTH
|
||||
let HEIGHT
|
||||
let MAX_HEIGHT
|
||||
let barWidth
|
||||
let barHeight
|
||||
let x = 0
|
||||
let isPlaying = false
|
||||
let animationFrameId
|
||||
|
||||
let num
|
||||
let mult
|
||||
const maxNum = 255
|
||||
let frequencyAvg = 0
|
||||
|
||||
// const theme = useRefGetter('theme')
|
||||
// const setting = useRefGetter('setting')
|
||||
// let themeColor = getComputedStyle(document.documentElement).getPropertyValue('--color-primary-light-200-alpha-800')
|
||||
// watch(theme, theme => {
|
||||
let themeColor = 'rgba(255, 255, 255, .12)'
|
||||
// })
|
||||
|
||||
useEvent((event) => {
|
||||
if (event.action == 'send_analyser_data_array') {
|
||||
// console.log(event.action)
|
||||
renderFrame(event.data)
|
||||
}
|
||||
})
|
||||
|
||||
// https://developer.mozilla.org/zh-CN/docs/Web/API/AnalyserNode/smoothingTimeConstant
|
||||
const renderFrame = (dataArray) => {
|
||||
x = 0
|
||||
|
||||
// console.log(dataArray)
|
||||
// analyser.getByteFrequencyData(dataArray)
|
||||
|
||||
ctx.clearRect(0, 0, WIDTH, HEIGHT)
|
||||
// ctx.fillRect(0, 0, WIDTH, HEIGHT)
|
||||
ctx.fillStyle = themeColor
|
||||
|
||||
for (let i = 0; i < dataArray.length; i++) {
|
||||
mult = Math.floor(i / maxNum)
|
||||
num = mult % 2 === 0 ? (i - maxNum * mult) : (maxNum - (i - maxNum * mult))
|
||||
let spectrum = num > 90 ? 0 : dataArray[num + 20]
|
||||
frequencyAvg += spectrum * 1.4
|
||||
}
|
||||
frequencyAvg /= dataArray.length
|
||||
frequencyAvg *= 1.6
|
||||
|
||||
frequencyAvg = frequencyAvg / maxNum
|
||||
// ctx.scale(1, 1 + frequencyAvg)
|
||||
|
||||
for (let i = 0; i < dataArray.length; i++) {
|
||||
if (x > WIDTH) break
|
||||
|
||||
barHeight = dataArray[i]
|
||||
|
||||
// let r = barHeight + (25 * (i / bufferLength))
|
||||
// let g = 250 * (i / bufferLength)
|
||||
// let b = 50
|
||||
|
||||
// ctx.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')'
|
||||
barHeight = (barHeight * frequencyAvg + barHeight * 0.42) * MAX_HEIGHT
|
||||
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight)
|
||||
|
||||
x += barWidth
|
||||
}
|
||||
|
||||
animationFrameId = null
|
||||
if (isPlaying) animationFrameId = window.requestAnimationFrame(getAnalyserDataArray)
|
||||
}
|
||||
|
||||
const handlePlay = () => {
|
||||
isPlaying = true
|
||||
// analyser.fftSize = 256
|
||||
// bufferLength = analyser.frequencyBinCount
|
||||
// console.log(bufferLength)
|
||||
barWidth = getBarWidth(WIDTH)
|
||||
// dataArray = new Uint8Array(bufferLength)
|
||||
// renderFrame()
|
||||
getAnalyserDataArray()
|
||||
}
|
||||
|
||||
|
||||
const handlePause = () => {
|
||||
if (animationFrameId) window.cancelAnimationFrame(animationFrameId)
|
||||
isPlaying = false
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
const canvas = dom_canvas.value
|
||||
canvas.width = canvas.clientWidth
|
||||
canvas.height = canvas.clientHeight
|
||||
WIDTH = canvas.width
|
||||
HEIGHT = canvas.height
|
||||
MAX_HEIGHT = Math.round(HEIGHT * 0.46 / 255 * 10000) / 10000
|
||||
// console.log(MAX_HEIGHT)
|
||||
barWidth = getBarWidth(WIDTH)
|
||||
}
|
||||
|
||||
watch(isPlay, (isPlay) => {
|
||||
if (isPlay) handlePlay()
|
||||
else handlePause()
|
||||
})
|
||||
watch(() => setting['desktopLyric.audioVisualization'], (enable) => {
|
||||
if (!enable) handlePause()
|
||||
})
|
||||
window.addEventListener('resize', handleResize)
|
||||
onBeforeUnmount(() => {
|
||||
handlePause()
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const canvas = dom_canvas.value
|
||||
ctx = canvas.getContext('2d')
|
||||
canvas.width = canvas.clientWidth
|
||||
canvas.height = canvas.clientHeight
|
||||
WIDTH = canvas.width
|
||||
HEIGHT = canvas.height
|
||||
MAX_HEIGHT = Math.round(HEIGHT * 0.46 / 255 * 10000) / 10000
|
||||
|
||||
// console.log(MAX_HEIGHT)
|
||||
if (isPlay.value) handlePlay()
|
||||
})
|
||||
|
||||
return {
|
||||
dom_canvas,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
.content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// opacity: 0.1;
|
||||
}
|
||||
</style>
|
||||
@@ -1,278 +0,0 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.container")
|
||||
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut")
|
||||
div(:class="$style.btns" v-show="!isShowThemeList")
|
||||
button(:class="$style.btn" @click="handleClose" :title="$t('desktop_lyric__close')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-close')
|
||||
button(:class="$style.btn" @click="handleLock" :title="$t('desktop_lyric__' + (lrcConfig.isLock ? 'unlock' : 'lock'))")
|
||||
svg(v-if="config.isLock" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-unlock')
|
||||
svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-lock')
|
||||
button(:class="$style.btn" :title="$t('desktop_lyric__theme')" @click="isShowThemeList = true")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-theme')
|
||||
button(:class="$style.btn" @click="handleFontChange('increase', 10)" @contextmenu="handleFontChange('increase', 1)" :title="$t('desktop_lyric__font_increase')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-font-increase')
|
||||
button(:class="$style.btn" @click="handleFontChange('decrease', 10)" @contextmenu="handleFontChange('decrease', 1)" :title="$t('desktop_lyric__font_decrease')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-font-decrease')
|
||||
button(:class="$style.btn" @click="handleOpactiyChange('increase', 10)" @contextmenu="handleOpactiyChange('increase', 2)" :title="$t('desktop_lyric__opactiy_increase')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-opactiy-increase')
|
||||
button(:class="$style.btn" @click="handleOpactiyChange('decrease', 10)" @contextmenu="handleOpactiyChange('decrease', 2)" :title="$t('desktop_lyric__opactiy_decrease')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-opactiy-decrease')
|
||||
|
||||
//- button(:class="$style.btn" v-text="lrcConfig.style.isZoomActiveLrc ? '取消缩放' : '缩放歌词'" @click="handleZoomLrc")
|
||||
button(:class="$style.btn" @click="handleZoomLrc" :title="$t('desktop_lyric__' + (lrcConfig.style.isZoomActiveLrc ? 'lrc_active_zoom_off' : 'lrc_active_zoom_on'))")
|
||||
svg(v-if="config.style.isZoomActiveLrc" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-vibrate-off')
|
||||
svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-vibrate')
|
||||
|
||||
button(:class="$style.btn" @click="handleAlwaysOnTop" :title="$t('desktop_lyric__' + (lrcConfig.isAlwaysOnTop ? 'win_top_off' : 'win_top_on'))")
|
||||
svg(v-if="config.isAlwaysOnTop" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-top-off')
|
||||
svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-top-on')
|
||||
//- base-quantity(v-model="config.style.fontSize" :min="30" :step="2" :max="1000" @input="sendEvent")
|
||||
//- base-quantity(v-model="config.style.opacity" :min="5" :step="5" :max="100" @input="sendEvent")
|
||||
transition(enter-active-class="animated slideInDown" leave-active-class="animated-fast slideOutUp")
|
||||
div(:class="$style.themes" v-show="isShowThemeList")
|
||||
ul(:class="$style.themeList")
|
||||
li(:class="$style.btnBack")
|
||||
button(:class="$style.btn" @click="isShowThemeList = false" :title="$t('desktop_lyric__back')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 512 512' space='preserve')
|
||||
use(xlink:href='#icon-back')
|
||||
li(:class="[$style.themeItem, theme.className, lrcConfig.theme == theme.id ? $style.active : null]" v-for="theme in themes" @click="handleToggleTheme(theme)")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rendererSend, NAMES } from '../../../common/ipc'
|
||||
import { toRaw } from 'vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
lrcConfig: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
enable: false,
|
||||
isLock: false,
|
||||
isAlwaysOnTop: false,
|
||||
width: 600,
|
||||
height: 700,
|
||||
x: -1,
|
||||
y: -1,
|
||||
theme: '',
|
||||
style: {
|
||||
fontSize: 125,
|
||||
opacity: 80,
|
||||
isZoomActiveLrc: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
themes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
enable: false,
|
||||
isLock: false,
|
||||
isAlwaysOnTop: false,
|
||||
theme: 0,
|
||||
style: {
|
||||
fontSize: 125,
|
||||
opacity: 80,
|
||||
isZoomActiveLrc: true,
|
||||
},
|
||||
},
|
||||
isShowThemeList: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
lrcConfig: {
|
||||
handler(n) {
|
||||
this.config = JSON.parse(JSON.stringify(n))
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.config = JSON.parse(JSON.stringify(this.lrcConfig))
|
||||
},
|
||||
methods: {
|
||||
sendEvent() {
|
||||
rendererSend(NAMES.winLyric.set_lyric_config, toRaw(this.config))
|
||||
},
|
||||
handleClose() {
|
||||
this.config.enable = false
|
||||
this.sendEvent()
|
||||
},
|
||||
handleLock() {
|
||||
this.config.isLock = true
|
||||
this.sendEvent()
|
||||
},
|
||||
handleAlwaysOnTop() {
|
||||
this.config.isAlwaysOnTop = !this.config.isAlwaysOnTop
|
||||
this.sendEvent()
|
||||
},
|
||||
handleZoomLrc() {
|
||||
this.config.style.isZoomActiveLrc = !this.config.style.isZoomActiveLrc
|
||||
this.sendEvent()
|
||||
},
|
||||
handleFontChange(action, step) {
|
||||
let num
|
||||
switch (action) {
|
||||
case 'increase':
|
||||
num = Math.min(this.config.style.fontSize + step, 1000)
|
||||
break
|
||||
case 'decrease':
|
||||
num = Math.max(this.config.style.fontSize - step, 30)
|
||||
break
|
||||
}
|
||||
if (this.config.style.fontSize == num) return
|
||||
this.config.style.fontSize = num
|
||||
this.sendEvent()
|
||||
},
|
||||
handleOpactiyChange(action, step) {
|
||||
let num
|
||||
switch (action) {
|
||||
case 'increase':
|
||||
num = Math.min(this.config.style.opacity + step, 100)
|
||||
break
|
||||
case 'decrease':
|
||||
num = Math.max(this.config.style.opacity - step, 6)
|
||||
break
|
||||
}
|
||||
if (this.config.style.opacity == num) return
|
||||
this.config.style.opacity = num
|
||||
this.sendEvent()
|
||||
},
|
||||
handleToggleTheme(theme) {
|
||||
this.config.theme = theme.id
|
||||
this.sendEvent()
|
||||
},
|
||||
// handleUpdateSetting() {
|
||||
// this.sendEvent()
|
||||
// },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
@bar-height: 38px;
|
||||
@bar-height-padding: 7px;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
// height: 50px;
|
||||
transition: opacity @transition-theme;
|
||||
// opacity: 0;
|
||||
// &:hover {
|
||||
// opacity: 1;
|
||||
// }
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.btn {
|
||||
min-height: @bar-height;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
color: #fff;
|
||||
transition: color @transition-theme;
|
||||
&:hover {
|
||||
color: @color-theme;
|
||||
}
|
||||
}
|
||||
|
||||
.themes {
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
&:global(.slideInDown), &:global(.slideOutUp) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
min-height: @bar-height;
|
||||
padding-top: @bar-height-padding;
|
||||
padding-left: @bar-height-padding;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btnBack {
|
||||
margin-top: -@bar-height-padding;
|
||||
margin-left: -@bar-height-padding;
|
||||
}
|
||||
|
||||
.theme-item {
|
||||
display: block;
|
||||
margin-right: @bar-height-padding;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: @radius-border;
|
||||
border: 2px solid transparent;
|
||||
box-sizing: border-box;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
transition: border-color @transition-theme;
|
||||
&.active {
|
||||
border-color: @color-theme;
|
||||
}
|
||||
&:after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: @radius-border;
|
||||
}
|
||||
each(@themes, {
|
||||
&:global(.@{value}) {
|
||||
&:after {
|
||||
background-color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.btn {
|
||||
&:hover {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
.theme-item {
|
||||
&.active {
|
||||
border-color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
@@ -1,59 +0,0 @@
|
||||
<template lang="pug">
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' style="display: none;")
|
||||
defs
|
||||
g#icon-close(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z')
|
||||
|
||||
g#icon-unlock(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,1 10,15A2,2 0 0,1 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17Z')
|
||||
|
||||
g#icon-lock(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z')
|
||||
|
||||
g#icon-font-decrease(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,17L23,11.93L21.59,10.5L19,13.1V7H17V13.1L14.41,10.5L13,11.93L18,17Z')
|
||||
|
||||
g#icon-font-increase(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,7L13,12.07L14.41,13.5L17,10.9V17H19V10.9L21.59,13.5L23,12.07L18,7Z')
|
||||
|
||||
g#icon-opactiy-increase(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M12,6A6,6 0 0,1 18,12C18,14.22 16.79,16.16 15,17.2V19A1,1 0 0,1 14,20H10A1,1 0 0,1 9,19V17.2C7.21,16.16 6,14.22 6,12A6,6 0 0,1 12,6M14,21V22A1,1 0 0,1 13,23H11A1,1 0 0,1 10,22V21H14M20,11H23V13H20V11M1,11H4V13H1V11M13,1V4H11V1H13M4.92,3.5L7.05,5.64L5.63,7.05L3.5,4.93L4.92,3.5M16.95,5.63L19.07,3.5L20.5,4.93L18.37,7.05L16.95,5.63Z")
|
||||
//- path(d='M17.66,8L12,2.35L6.34,8C4.78,9.56 4,11.64 4,13.64C4,15.64 4.78,17.75 6.34,19.31C7.9,20.87 9.95,21.66 12,21.66C14.05,21.66 16.1,20.87 17.66,19.31C19.22,17.75 20,15.64 20,13.64C20,11.64 19.22,9.56 17.66,8M6,14C6,12 6.62,10.73 7.76,9.6L12,5.27L16.24,9.65C17.38,10.77 18,12 18,14H6Z')
|
||||
|
||||
g#icon-opactiy-decrease(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M20,11H23V13H20V11M1,11H4V13H1V11M13,1V4H11V1H13M4.92,3.5L7.05,5.64L5.63,7.05L3.5,4.93L4.92,3.5M16.95,5.63L19.07,3.5L20.5,4.93L18.37,7.05L16.95,5.63M12,6A6,6 0 0,1 18,12C18,14.22 16.79,16.16 15,17.2V19A1,1 0 0,1 14,20H10A1,1 0 0,1 9,19V17.2C7.21,16.16 6,14.22 6,12A6,6 0 0,1 12,6M14,21V22A1,1 0 0,1 13,23H11A1,1 0 0,1 10,22V21H14M11,18H13V15.87C14.73,15.43 16,13.86 16,12A4,4 0 0,0 12,8A4,4 0 0,0 8,12C8,13.86 9.27,15.43 11,15.87V18Z")
|
||||
|
||||
g#icon-top-on(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z")
|
||||
//- path(d='M17.66,8L12,2.35L6.34,8C4.78,9.56 4,11.64 4,13.64C4,15.64 4.78,17.75 6.34,19.31C7.9,20.87 9.95,21.66 12,21.66C14.05,21.66 16.1,20.87 17.66,19.31C19.22,17.75 20,15.64 20,13.64C20,11.64 19.22,9.56 17.66,8M6,14C6,12 6.62,10.73 7.76,9.6L12,5.27L16.24,9.65C17.38,10.77 18,12 18,14H6Z')
|
||||
|
||||
g#icon-top-off(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M2,5.27L3.28,4L20,20.72L18.73,22L12.8,16.07V22H11.2V16H6V14L8,12V11.27L2,5.27M16,12L18,14V16H17.82L8,6.18V4H7V2H17V4H16V12Z")
|
||||
|
||||
g#icon-theme(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z')
|
||||
|
||||
g#icon-vibrate(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M16,19H8V5H16M16.5,3H7.5A1.5,1.5 0 0,0 6,4.5V19.5A1.5,1.5 0 0,0 7.5,21H16.5A1.5,1.5 0 0,0 18,19.5V4.5A1.5,1.5 0 0,0 16.5,3M19,17H21V7H19M22,9V15H24V9M3,17H5V7H3M0,15H2V9H0V15Z")
|
||||
|
||||
g#icon-vibrate-off(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d="M8.2,5L6.55,3.35C6.81,3.12 7.15,3 7.5,3H16.5A1.5,1.5 0 0,1 18,4.5V14.8L16,12.8V5H8.2M0,15H2V9H0V15M21,17V7H19V15.8L20.2,17H21M3,17H5V7H3V17M18,17.35L22.11,21.46L20.84,22.73L18,19.85C17.83,20.54 17.21,21 16.5,21H7.5A1.5,1.5 0 0,1 6,19.5V7.89L1.11,3L2.39,1.73L6.09,5.44L8,7.34L16,15.34L18,17.34V17.35M16,17.89L8,9.89V19H16V17.89M22,9V15H24V9H22Z")
|
||||
|
||||
|
||||
g#icon-back(fill='currentColor')
|
||||
// 0 0 512 512
|
||||
path(d='M511.563,434.259c-1.728-142.329-124.42-258.242-277.087-263.419V95.999c0-17.645-14.342-31.999-31.974-31.999 c-7.931,0-15.591,3.042-21.524,8.562c0,0-134.828,124.829-173.609,163.755C2.623,241.109,0,248.088,0,255.994 c0,7.906,2.623,14.885,7.369,19.687c38.781,38.915,173.609,163.745,173.609,163.745c5.933,5.521,13.593,8.562,21.524,8.562 c17.631,0,31.974-14.354,31.974-31.999v-74.591c153.479,2.156,255.792,50.603,255.792,95.924c0,5.896,4.767,10.666,10.658,10.666 c0.167,0.021,0.333,0.01,0.416,0c5.891,0,10.658-4.771,10.658-10.666C512,436.259,511.854,435.228,511.563,434.259z')
|
||||
</template>
|
||||
|
||||
@@ -1,542 +0,0 @@
|
||||
<template lang="pug">
|
||||
div(:class="[$style.lyric, { [$style.draging]: lyricEvent.isMsDown }, { [$style.lrcActiveZoom]: lrcConfig.style.isZoomActiveLrc } ]"
|
||||
:style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" @touchstart="handleLyricTouchStart" 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
|
||||
let delayScrollTimeout
|
||||
|
||||
export default {
|
||||
props: {
|
||||
lrcConfig: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
isDelayScroll: true,
|
||||
style: {
|
||||
font: '',
|
||||
fontSize: 125,
|
||||
opacity: 80,
|
||||
isZoomActiveLrc: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
isPlayLxlrc: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isShowLyricTranslation: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isShowLyricRoma: {
|
||||
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: '',
|
||||
rlyric: '',
|
||||
lxlyric: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
lrcStyles() {
|
||||
return {
|
||||
fontFamily: this.lrcConfig.style.font,
|
||||
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, o) {
|
||||
if (n < 0) return
|
||||
if (n == 0 && this.isSetedLines) return this.isSetedLines = false
|
||||
if (o == null || n - o != 1) return this.handleScrollLrc()
|
||||
if (this.lrcConfig.isDelayScroll) {
|
||||
delayScrollTimeout = setTimeout(() => {
|
||||
delayScrollTimeout = null
|
||||
this.handleScrollLrc(600)
|
||||
}, 600)
|
||||
} else {
|
||||
this.handleScrollLrc()
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
isShowLyricTranslation() {
|
||||
this.setLyric()
|
||||
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
|
||||
},
|
||||
isShowLyricRoma() {
|
||||
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
|
||||
},
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('mousemove', this.handleMouseMsMove)
|
||||
document.addEventListener('mouseup', this.handleMouseMsUp)
|
||||
document.addEventListener('touchmove', this.handleTouchMove)
|
||||
document.addEventListener('touchend', this.handleMouseMsUp)
|
||||
rendererSend(NAMES.winLyric.get_lyric_info, 'info')
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.clearLyricScrollTimeout()
|
||||
document.removeEventListener('mousemove', this.handleMouseMsMove)
|
||||
document.removeEventListener('mouseup', this.handleMouseMsUp)
|
||||
document.removeEventListener('touchmove', this.handleTouchMove)
|
||||
document.removeEventListener('touchend', 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.rlyric = data.rlrc
|
||||
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 'stop':
|
||||
this.isPlay = false
|
||||
this.lyrics.lyric = ''
|
||||
this.lyrics.tlyric = ''
|
||||
this.lyrics.lxlyric = ''
|
||||
this.lyrics.rlyric = ''
|
||||
this.setLyric()
|
||||
break
|
||||
case 'info':
|
||||
// console.log('info', data)
|
||||
this.lyrics.lyric = data.lrc
|
||||
this.lyrics.tlyric = data.tlrc
|
||||
this.lyrics.rlyric = data.rlrc
|
||||
this.lyrics.lxlyric = data.lxlrc
|
||||
this.setLyric()
|
||||
this.$nextTick(() => {
|
||||
this.lyric.line = data.line
|
||||
setTimeout(() => {
|
||||
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(duration = 300) {
|
||||
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, duration)
|
||||
},
|
||||
handleLyricDown(target, x, y) {
|
||||
if (target.classList.contains('font') ||
|
||||
target.parentNode.classList.contains('font') ||
|
||||
target.classList.contains('translation') ||
|
||||
target.parentNode.classList.contains('translation')) {
|
||||
this.lyricEvent.isMsDown = true
|
||||
this.lyricEvent.msDownY = y
|
||||
this.lyricEvent.msDownScrollY = this.$refs.dom_lyric.scrollTop
|
||||
} else {
|
||||
if (delayScrollTimeout) {
|
||||
clearTimeout(delayScrollTimeout)
|
||||
delayScrollTimeout = null
|
||||
}
|
||||
this.winEvent.isMsDown = true
|
||||
this.winEvent.msDownX = x
|
||||
this.winEvent.msDownY = y
|
||||
}
|
||||
},
|
||||
handleLyricMouseDown(e) {
|
||||
this.handleLyricDown(e.target, e.clientX, e.clientY)
|
||||
},
|
||||
handleLyricTouchStart(e) {
|
||||
if (e.changedTouches.length) {
|
||||
const touch = e.changedTouches[0]
|
||||
this.handleLyricDown(e.target, touch.clientX, touch.clientY)
|
||||
}
|
||||
},
|
||||
handleMouseMsUp(e) {
|
||||
this.lyricEvent.isMsDown = false
|
||||
this.winEvent.isMsDown = false
|
||||
},
|
||||
handleMove(x, y) {
|
||||
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 - y
|
||||
this.startLyricScrollTimeout()
|
||||
} else if (this.winEvent.isMsDown) {
|
||||
rendererSend(NAMES.winLyric.set_win_bounds, {
|
||||
x: x - this.winEvent.msDownX,
|
||||
y: y - this.winEvent.msDownY,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
})
|
||||
}
|
||||
},
|
||||
handleTouchMove(e) {
|
||||
if (e.changedTouches.length) {
|
||||
const touch = e.changedTouches[0]
|
||||
this.handleMove(touch.clientX, touch.clientY)
|
||||
}
|
||||
},
|
||||
handleMouseMsMove(e) {
|
||||
this.handleMove(e.clientX, e.clientY)
|
||||
},
|
||||
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() {
|
||||
const extendedLyrics = []
|
||||
if (this.isShowLyricTranslation && this.lyrics.tlyric) extendedLyrics.push(this.lyrics.tlyric)
|
||||
if (this.isShowLyricRoma && this.lyrics.rlyric) extendedLyrics.push(this.lyrics.rlyric)
|
||||
window.lrc.setLyric(
|
||||
this.isPlayLxlrc && this.lyrics.lxlyric ? this.lyrics.lxlyric : this.lyrics.lyric,
|
||||
extendedLyrics,
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@lyric/assets/styles/layout.less';
|
||||
|
||||
.lyric {
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
color: @color-theme-lyric;
|
||||
contain: strict;
|
||||
cursor: move;
|
||||
|
||||
:global {
|
||||
.lrc-content {
|
||||
line-height: 1.2;
|
||||
margin: 16px 0;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
.font {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.font, .extended {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.extended {
|
||||
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;
|
||||
}
|
||||
.extended {
|
||||
color: @color-theme;
|
||||
}
|
||||
// span {
|
||||
// color: @color-theme;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
.draging {
|
||||
:global {
|
||||
.lrc-content {
|
||||
.font, .extended {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
:global {
|
||||
.lrc-content {
|
||||
&.active {
|
||||
.extended {
|
||||
font-size: .94em;
|
||||
}
|
||||
span {
|
||||
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 {
|
||||
.extended {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
.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>
|
||||
@@ -3,11 +3,22 @@ import camelCase from 'lodash/camelCase'
|
||||
|
||||
const requireComponent = require.context('./', true, /\.vue$/)
|
||||
|
||||
const vueFileRxp = /\.vue$/
|
||||
|
||||
export default app => {
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const filePath = fileName.replace(/^\.\//, '')
|
||||
|
||||
if (!filePath.split('/').every((path, index, arr) => {
|
||||
const char = path.charAt(0)
|
||||
return vueFileRxp.test(path) || char.toUpperCase() !== char || arr[index + 1] == 'index.vue'
|
||||
})) return
|
||||
|
||||
const componentConfig = requireComponent(fileName)
|
||||
|
||||
const componentName = upperFirst(camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')))
|
||||
let componentName = upperFirst(camelCase(filePath.replace(/\.\w+$/, '')))
|
||||
|
||||
if (componentName.endsWith('Index')) componentName = componentName.replace(/Index$/, '')
|
||||
|
||||
app.component(componentName, componentConfig.default || componentConfig)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div :class="$style.container">
|
||||
<transition enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut">
|
||||
<div v-show="!isShowThemeList" :class="$style.btns">
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__close')" @click="handleClose">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-close" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__' + (setting['desktopLyric.isLock'] ? 'unlock' : 'lock'))" @click="handleLock">
|
||||
<svg v-if="setting['desktopLyric.isLock']" version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-unlock" />
|
||||
</svg>
|
||||
<svg v-else version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-lock" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__font_increase')" @click="handleFontChange('increase', 1)">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-font-increase" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__font_decrease')" @click="handleFontChange('decrease', 1)">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-font-decrease" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__opactiy_increase')" @click="handleOpactiyChange('increase', 10)" @contextmenu="handleOpactiyChange('increase', 2)">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-opactiy-increase" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__opactiy_decrease')" @click="handleOpactiyChange('decrease', 10)" @contextmenu="handleOpactiyChange('decrease', 2)">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-opactiy-decrease" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__' + (setting['desktopLyric.style.isZoomActiveLrc'] ? 'lrc_active_zoom_off' : 'lrc_active_zoom_on'))" @click="handleZoomLrc">
|
||||
<svg v-if="setting['desktopLyric.style.isZoomActiveLrc']" version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-vibrate-off" />
|
||||
</svg>
|
||||
<svg v-else version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-vibrate" />
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" :title="$t('desktop_lyric__' + (setting['desktopLyric.isAlwaysOnTop'] ? 'win_top_off' : 'win_top_on'))" @click="handleAlwaysOnTop">
|
||||
<svg v-if="setting['desktopLyric.isAlwaysOnTop']" version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-top-off" />
|
||||
</svg>
|
||||
<svg v-else version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="20px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-top-on" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from '@common/utils/vueTools'
|
||||
import { setting, themeList } from '@lyric/store/state'
|
||||
import { updateSetting } from '@lyric/store/action'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const isShowThemeList = ref(false)
|
||||
|
||||
const handleClose = () => {
|
||||
updateSetting({ 'desktopLyric.enable': false })
|
||||
}
|
||||
const handleLock = () => {
|
||||
updateSetting({ 'desktopLyric.isLock': true })
|
||||
}
|
||||
const handleAlwaysOnTop = () => {
|
||||
updateSetting({ 'desktopLyric.isAlwaysOnTop': !setting['desktopLyric.isAlwaysOnTop'] })
|
||||
}
|
||||
const handleZoomLrc = () => {
|
||||
updateSetting({ 'desktopLyric.style.isZoomActiveLrc': !setting['desktopLyric.style.isZoomActiveLrc'] })
|
||||
}
|
||||
const handleFontChange = (action, step) => {
|
||||
let num
|
||||
switch (action) {
|
||||
case 'increase':
|
||||
num = Math.min(setting['desktopLyric.style.fontSize'] + step, 80)
|
||||
break
|
||||
case 'decrease':
|
||||
num = Math.max(setting['desktopLyric.style.fontSize'] - step, 10)
|
||||
break
|
||||
}
|
||||
if (setting['desktopLyric.style.fontSize'] == num) return
|
||||
updateSetting({ 'desktopLyric.style.fontSize': num })
|
||||
}
|
||||
const handleOpactiyChange = (action, step) => {
|
||||
let num
|
||||
switch (action) {
|
||||
case 'increase':
|
||||
num = Math.min(setting['desktopLyric.style.opacity'] + step, 100)
|
||||
break
|
||||
case 'decrease':
|
||||
num = Math.max(setting['desktopLyric.style.opacity'] - step, 6)
|
||||
break
|
||||
}
|
||||
if (setting['desktopLyric.style.opacity'] == num) return
|
||||
updateSetting({ 'desktopLyric.style.opacity': num })
|
||||
}
|
||||
return {
|
||||
setting,
|
||||
themeList,
|
||||
isShowThemeList,
|
||||
|
||||
handleClose,
|
||||
handleLock,
|
||||
handleAlwaysOnTop,
|
||||
handleZoomLrc,
|
||||
handleFontChange,
|
||||
handleOpactiyChange,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
@bar-height: 38px;
|
||||
@bar-height-padding: 7px;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
// height: 50px;
|
||||
transition: opacity @transition-theme;
|
||||
// opacity: 0;
|
||||
// &:hover {
|
||||
// opacity: 1;
|
||||
// }
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.btn {
|
||||
min-height: @bar-height;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
color: #fff;
|
||||
transition: opacity @transition-theme;
|
||||
&:hover {
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" style="display: none;">
|
||||
<defs>
|
||||
<g id="icon-close" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||
</g>
|
||||
<g id="icon-unlock" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,1 10,15A2,2 0 0,1 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17Z" />
|
||||
</g>
|
||||
<g id="icon-lock" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" />
|
||||
</g>
|
||||
<g id="icon-font-decrease" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,17L23,11.93L21.59,10.5L19,13.1V7H17V13.1L14.41,10.5L13,11.93L18,17Z" />
|
||||
</g>
|
||||
<g id="icon-font-increase" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,7L13,12.07L14.41,13.5L17,10.9V17H19V10.9L21.59,13.5L23,12.07L18,7Z" />
|
||||
</g>
|
||||
<g id="icon-opactiy-increase" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M12,6A6,6 0 0,1 18,12C18,14.22 16.79,16.16 15,17.2V19A1,1 0 0,1 14,20H10A1,1 0 0,1 9,19V17.2C7.21,16.16 6,14.22 6,12A6,6 0 0,1 12,6M14,21V22A1,1 0 0,1 13,23H11A1,1 0 0,1 10,22V21H14M20,11H23V13H20V11M1,11H4V13H1V11M13,1V4H11V1H13M4.92,3.5L7.05,5.64L5.63,7.05L3.5,4.93L4.92,3.5M16.95,5.63L19.07,3.5L20.5,4.93L18.37,7.05L16.95,5.63Z" />
|
||||
</g>
|
||||
<g id="icon-opactiy-decrease" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M20,11H23V13H20V11M1,11H4V13H1V11M13,1V4H11V1H13M4.92,3.5L7.05,5.64L5.63,7.05L3.5,4.93L4.92,3.5M16.95,5.63L19.07,3.5L20.5,4.93L18.37,7.05L16.95,5.63M12,6A6,6 0 0,1 18,12C18,14.22 16.79,16.16 15,17.2V19A1,1 0 0,1 14,20H10A1,1 0 0,1 9,19V17.2C7.21,16.16 6,14.22 6,12A6,6 0 0,1 12,6M14,21V22A1,1 0 0,1 13,23H11A1,1 0 0,1 10,22V21H14M11,18H13V15.87C14.73,15.43 16,13.86 16,12A4,4 0 0,0 12,8A4,4 0 0,0 8,12C8,13.86 9.27,15.43 11,15.87V18Z" />
|
||||
</g>
|
||||
<g id="icon-top-on" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" />
|
||||
</g>
|
||||
<g id="icon-top-off" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M2,5.27L3.28,4L20,20.72L18.73,22L12.8,16.07V22H11.2V16H6V14L8,12V11.27L2,5.27M16,12L18,14V16H17.82L8,6.18V4H7V2H17V4H16V12Z" />
|
||||
</g>
|
||||
<g id="icon-theme" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z" />
|
||||
</g>
|
||||
<g id="icon-vibrate" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M16,19H8V5H16M16.5,3H7.5A1.5,1.5 0 0,0 6,4.5V19.5A1.5,1.5 0 0,0 7.5,21H16.5A1.5,1.5 0 0,0 18,19.5V4.5A1.5,1.5 0 0,0 16.5,3M19,17H21V7H19M22,9V15H24V9M3,17H5V7H3M0,15H2V9H0V15Z" />
|
||||
</g>
|
||||
<g id="icon-vibrate-off" fill="currentColor">
|
||||
<!-- 0 0 24 24-->
|
||||
<path d="M8.2,5L6.55,3.35C6.81,3.12 7.15,3 7.5,3H16.5A1.5,1.5 0 0,1 18,4.5V14.8L16,12.8V5H8.2M0,15H2V9H0V15M21,17V7H19V15.8L20.2,17H21M3,17H5V7H3V17M18,17.35L22.11,21.46L20.84,22.73L18,19.85C17.83,20.54 17.21,21 16.5,21H7.5A1.5,1.5 0 0,1 6,19.5V7.89L1.11,3L2.39,1.73L6.09,5.44L8,7.34L16,15.34L18,17.34V17.35M16,17.89L8,9.89V19H16V17.89M22,9V15H24V9H22Z" />
|
||||
</g>
|
||||
<g id="icon-back" fill="currentColor">
|
||||
<!-- 0 0 512 512-->
|
||||
<path d="M511.563,434.259c-1.728-142.329-124.42-258.242-277.087-263.419V95.999c0-17.645-14.342-31.999-31.974-31.999 c-7.931,0-15.591,3.042-21.524,8.562c0,0-134.828,124.829-173.609,163.755C2.623,241.109,0,248.088,0,255.994 c0,7.906,2.623,14.885,7.369,19.687c38.781,38.915,173.609,163.745,173.609,163.745c5.933,5.521,13.593,8.562,21.524,8.562 c17.631,0,31.974-14.354,31.974-31.999v-74.591c153.479,2.156,255.792,50.603,255.792,95.924c0,5.896,4.767,10.666,10.658,10.666 c0.167,0.021,0.333,0.01,0.416,0c5.891,0,10.658-4.771,10.658-10.666C512,436.259,511.854,435.228,511.563,434.259z" />
|
||||
</g>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div
|
||||
ref="dom_lyric"
|
||||
:class="[$style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }, { [$style.ellipsis]: ellipsis } ]"
|
||||
:style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" @touchstart="handleLyricTouchStart"
|
||||
>
|
||||
<div :class="$style.lyricSpace" />
|
||||
<div ref="dom_lyric_text" :class="[$style.lyricText]" />
|
||||
<div :class="$style.lyricSpace" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
import useLyric from './useLyric'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const isZoomActiveLrc = computed(() => setting['desktopLyric.style.isZoomActiveLrc'])
|
||||
const ellipsis = computed(() => setting['desktopLyric.style.ellipsis'])
|
||||
// const fontWeight = computed(() => setting['desktopLyric.style.fontWeight'])
|
||||
const lrcStyles = computed(() => ({
|
||||
fontFamily: setting['desktopLyric.style.font'],
|
||||
fontSize: Math.trunc(setting['desktopLyric.style.fontSize']) + 'px',
|
||||
opacity: setting['desktopLyric.style.opacity'] / 100,
|
||||
textAlign: setting['desktopLyric.style.align'],
|
||||
}))
|
||||
const {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
} = useLyric()
|
||||
|
||||
return {
|
||||
isZoomActiveLrc,
|
||||
lrcStyles,
|
||||
ellipsis,
|
||||
// fontWeight,
|
||||
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@lyric/assets/styles/layout.less';
|
||||
|
||||
.lyric {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
contain: strict;
|
||||
cursor: move;
|
||||
// font-weight: bold;
|
||||
|
||||
:global {
|
||||
.font-lrc {
|
||||
color: var(--color-lyric-unplay);
|
||||
}
|
||||
.shadow {
|
||||
color: transparent;
|
||||
}
|
||||
.line-content {
|
||||
line-height: 1.2;
|
||||
margin: 16px 0;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
.font-lrc {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.extended {
|
||||
font-size: 0.8em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
&.line-mode {
|
||||
.font-lrc {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size, color;
|
||||
}
|
||||
}
|
||||
&.line-mode.active .font-lrc, &.font-mode.played .font-lrc {
|
||||
color: var(--color-lyric-played);
|
||||
}
|
||||
&.font-mode .extended .font-lrc {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size, color;
|
||||
}
|
||||
&.font-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.font-mode > .line > .font-lrc {
|
||||
> span {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size;
|
||||
font-size: 1em;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--color-lyric-unplay);
|
||||
background-image: -webkit-linear-gradient(left, var(--color-lyric-played), var(--color-lyric-played));
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-size: 0 100%;
|
||||
}
|
||||
}
|
||||
// &.line-mode {
|
||||
// .shadow {
|
||||
// text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.40);
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.font-mode {
|
||||
// }
|
||||
}
|
||||
.line-mode .font-lrc, .extended .font-lrc {
|
||||
// text-shadow: 0 0 2px rgba(0, 0, 0, 0.7), 0 0 2px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.3);
|
||||
.stroke2(var(--color-lyric-shadow));
|
||||
// .stroke2(rgba(0, 0, 0, 0.18));
|
||||
// .stroke(1px, rgba(0, 0, 0, 0.08));
|
||||
// .stroke(2px, rgba(0, 0, 0, 0.025));
|
||||
transition: font-size @transition-slow;
|
||||
}
|
||||
.font-mode .line .shadow {
|
||||
.stroke(1px, var(--color-lyric-shadow-font-mode));
|
||||
transition: font-size @transition-slow;
|
||||
// text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3), 1px 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
}
|
||||
// p {
|
||||
// padding: 8px 0;
|
||||
// line-height: 1.2;
|
||||
// overflow-wrap: break-word;
|
||||
// transition: @transition-normal !important;
|
||||
// transition-property: color, font-size;
|
||||
// }
|
||||
}
|
||||
|
||||
// .lrc-line {
|
||||
// display: inline-block;
|
||||
// padding: 8px 0;
|
||||
// line-height: 1.2;
|
||||
// overflow-wrap: break-word;
|
||||
// transition: @transition-normal;
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
.draging {
|
||||
:global {
|
||||
.line-content {
|
||||
.font-lrc {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
:global {
|
||||
.line-content {
|
||||
&.active {
|
||||
.extended {
|
||||
font-size: .94em;
|
||||
}
|
||||
.line {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
:global {
|
||||
.font-lrc, .shadow {
|
||||
display: -webkit-box !important;
|
||||
.mixin-ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// .font-weight {
|
||||
// :global {
|
||||
// .font-mode > .line {
|
||||
// font-weight: bold;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .footer {
|
||||
// flex: 0 0 100px;
|
||||
// overflow: hidden;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// }
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,202 @@
|
||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from '@common/utils/vueTools'
|
||||
import { scrollTo } from '@common/utils/renderer'
|
||||
import { lyric } from '@lyric/store/lyric'
|
||||
import { isPlay, setting } from '@lyric/store/state'
|
||||
import { setWindowBounds } from '@lyric/utils/ipc'
|
||||
|
||||
export default () => {
|
||||
const dom_lyric = ref(null)
|
||||
const dom_lyric_text = ref(null)
|
||||
const isMsDown = ref(false)
|
||||
let isStopScroll = false
|
||||
|
||||
const winEvent = {
|
||||
isMsDown: false,
|
||||
msDownX: 0,
|
||||
msDownY: 0,
|
||||
}
|
||||
|
||||
let msDownY = 0
|
||||
let msDownScrollY = 0
|
||||
let timeout = null
|
||||
let cancelScrollFn
|
||||
let dom_lines
|
||||
let isSetedLines = false
|
||||
|
||||
|
||||
const handleScrollLrc = (duration = 300) => {
|
||||
if (!dom_lines?.length || !dom_lyric.value) return
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
if (isStopScroll) return
|
||||
let dom_p = dom_lines[lyric.line]
|
||||
cancelScrollFn = scrollTo(dom_lyric.value, dom_p ? (dom_p.offsetTop - dom_lyric.value.clientHeight * 0.5 + dom_p.clientHeight / 2) : 0, duration)
|
||||
}
|
||||
const clearLyricScrollTimeout = () => {
|
||||
if (!timeout) return
|
||||
clearTimeout(timeout)
|
||||
timeout = null
|
||||
}
|
||||
const startLyricScrollTimeout = () => {
|
||||
clearLyricScrollTimeout()
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
isStopScroll = false
|
||||
if (!isPlay.value) return
|
||||
handleScrollLrc()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const handleLyricDown = (target, x, y) => {
|
||||
if (target.classList.contains('font-lrc') ||
|
||||
target.parentNode.classList.contains('font-lrc') ||
|
||||
target.classList.contains('extended') ||
|
||||
target.parentNode.classList.contains('extended')
|
||||
) {
|
||||
if (delayScrollTimeout) {
|
||||
clearTimeout(delayScrollTimeout)
|
||||
delayScrollTimeout = null
|
||||
}
|
||||
isMsDown.value = true
|
||||
msDownY = y
|
||||
msDownScrollY = dom_lyric.value.scrollTop
|
||||
} else {
|
||||
winEvent.isMsDown = true
|
||||
winEvent.msDownX = x
|
||||
winEvent.msDownY = y
|
||||
}
|
||||
}
|
||||
const handleLyricMouseDown = event => {
|
||||
handleLyricDown(event.target, event.clientX, event.clientY)
|
||||
}
|
||||
const handleLyricTouchStart = event => {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
handleLyricDown(event.target, touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
const handleMouseMsUp = () => {
|
||||
isMsDown.value = false
|
||||
winEvent.isMsDown = false
|
||||
}
|
||||
|
||||
const handleMove = (x, y) => {
|
||||
if (isMsDown.value) {
|
||||
if (!isStopScroll) isStopScroll = true
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollTop = msDownScrollY + msDownY - y
|
||||
startLyricScrollTimeout()
|
||||
} else if (winEvent.isMsDown) {
|
||||
setWindowBounds({
|
||||
x: x - winEvent.msDownX,
|
||||
y: y - winEvent.msDownY,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleMouseMsMove = event => {
|
||||
handleMove(event.clientX, event.clientY)
|
||||
}
|
||||
const handleTouchMove = (e) => {
|
||||
if (e.changedTouches.length) {
|
||||
const touch = e.changedTouches[0]
|
||||
handleMove(touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
const handleWheel = (event) => {
|
||||
console.log(event.deltaY)
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollTop = dom_lyric.value.scrollTop + event.deltaY
|
||||
startLyricScrollTimeout()
|
||||
}
|
||||
|
||||
const setLyric = (lines) => {
|
||||
const dom_line_content = document.createDocumentFragment()
|
||||
for (const line of lines) {
|
||||
dom_line_content.appendChild(line.dom_line)
|
||||
}
|
||||
dom_lyric_text.value.textContent = ''
|
||||
dom_lyric_text.value.appendChild(dom_line_content)
|
||||
nextTick(() => {
|
||||
dom_lines = dom_lyric.value.querySelectorAll('.line-content')
|
||||
handleScrollLrc()
|
||||
})
|
||||
}
|
||||
|
||||
const initLrc = (lines, oLines) => {
|
||||
isSetedLines = true
|
||||
if (oLines) {
|
||||
if (lines.length) {
|
||||
setLyric(lines)
|
||||
} else {
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
cancelScrollFn = scrollTo(dom_lyric.value, 0, 300, () => {
|
||||
if (lyric.lines !== lines) return
|
||||
setLyric(lines)
|
||||
}, 50)
|
||||
}
|
||||
} else {
|
||||
setLyric(lines)
|
||||
}
|
||||
}
|
||||
|
||||
let delayScrollTimeout
|
||||
const scrollLine = (line, oldLine) => {
|
||||
if (line < 0 || !lyric.lines.length) return
|
||||
if (line == 0 && isSetedLines) return isSetedLines = false
|
||||
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
|
||||
|
||||
if (setting['desktopLyric.isDelayScroll']) {
|
||||
delayScrollTimeout = setTimeout(() => {
|
||||
delayScrollTimeout = null
|
||||
handleScrollLrc(600)
|
||||
}, 600)
|
||||
} else {
|
||||
handleScrollLrc()
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => lyric.lines, initLrc)
|
||||
watch(() => lyric.line, scrollLine)
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', handleMouseMsMove)
|
||||
document.addEventListener('mouseup', handleMouseMsUp)
|
||||
document.addEventListener('touchmove', handleTouchMove)
|
||||
document.addEventListener('touchend', handleMouseMsUp)
|
||||
|
||||
initLrc(lyric.lines, null)
|
||||
nextTick(() => {
|
||||
scrollLine(lyric.line)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', handleMouseMsMove)
|
||||
document.removeEventListener('mouseup', handleMouseMsUp)
|
||||
document.removeEventListener('touchmove', handleTouchMove)
|
||||
document.removeEventListener('touchend', handleMouseMsUp)
|
||||
})
|
||||
|
||||
return {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div
|
||||
ref="dom_lyric"
|
||||
:class="[$style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }, { [$style.ellipsis]: ellipsis } ]"
|
||||
:style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" @touchstart="handleLyricTouchStart"
|
||||
>
|
||||
<div :class="$style.lyricSpace" />
|
||||
<div ref="dom_lyric_text" :class="[$style.lyricText]" />
|
||||
<div :class="$style.lyricSpace" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
import useLyric from './useLyric'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const isZoomActiveLrc = computed(() => setting['desktopLyric.style.isZoomActiveLrc'])
|
||||
const ellipsis = computed(() => setting['desktopLyric.style.ellipsis'])
|
||||
// const fontWeight = computed(() => setting['desktopLyric.style.fontWeight'])
|
||||
const lrcStyles = computed(() => ({
|
||||
fontFamily: setting['desktopLyric.style.font'],
|
||||
fontSize: Math.trunc(setting['desktopLyric.style.fontSize']) + 'px',
|
||||
opacity: setting['desktopLyric.style.opacity'] / 100,
|
||||
textAlign: setting['desktopLyric.style.align'],
|
||||
}))
|
||||
const {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
} = useLyric()
|
||||
|
||||
return {
|
||||
isZoomActiveLrc,
|
||||
lrcStyles,
|
||||
ellipsis,
|
||||
// fontWeight,
|
||||
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@lyric/assets/styles/layout.less';
|
||||
|
||||
.lyric {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
overflow-x: scroll;
|
||||
font-size: 16px;
|
||||
contain: strict;
|
||||
cursor: move;
|
||||
writing-mode: vertical-rl;
|
||||
width: 100%;
|
||||
&::-webkit-scrollbar {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
:global {
|
||||
.font-lrc {
|
||||
color: var(--color-lyric-unplay);
|
||||
}
|
||||
.shadow {
|
||||
color: transparent;
|
||||
}
|
||||
.line-content {
|
||||
line-height: 1.2;
|
||||
margin: 0 16px;
|
||||
overflow-wrap: break-word;
|
||||
letter-spacing: 5px;
|
||||
|
||||
.font-lrc {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.extended {
|
||||
font-size: 0.8em;
|
||||
margin-right: 2px;
|
||||
}
|
||||
&.line-mode {
|
||||
.font-lrc {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size, color;
|
||||
}
|
||||
}
|
||||
&.line-mode.active .font-lrc, &.font-mode.played .font-lrc {
|
||||
color: var(--color-lyric-played);
|
||||
}
|
||||
&.font-mode .extended .font-lrc {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size, color;
|
||||
}
|
||||
&.font-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.font-mode > .line > .font-lrc {
|
||||
> span {
|
||||
transition: @transition-slow;
|
||||
transition-property: font-size;
|
||||
font-size: 1em;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--color-lyric-unplay);
|
||||
background-image: -webkit-linear-gradient(top, var(--color-lyric-played), var(--color-lyric-played));
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-size: 0 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
// .shadow {
|
||||
// .stroke2(rgba(0, 0, 0, 0.05));
|
||||
// transition: font-size @transition-normal;
|
||||
// }
|
||||
// .font-mode .line .shadow {
|
||||
// .stroke(1px, rgba(0, 0, 0, 0.05));
|
||||
// // text-shadow: 1px 0 2px rgba(0, 0, 0, 0.30), 1px 0 1px rgba(0, 0, 0, 0.20);
|
||||
// }
|
||||
.line-mode .font-lrc, .extended .font-lrc {
|
||||
// text-shadow: 0 0 2px rgba(0, 0, 0, 0.7), 0 0 2px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.3);
|
||||
// .stroke2(rgba(0, 0, 0, 0.14));
|
||||
.stroke2(var(--color-lyric-shadow));
|
||||
// .stroke(1px, rgba(0, 0, 0, 0.08));
|
||||
// .stroke(2px, rgba(0, 0, 0, 0.025));
|
||||
transition: font-size @transition-slow;
|
||||
}
|
||||
.font-mode .line .shadow {
|
||||
.stroke(1px, var(--color-lyric-shadow-font-mode));
|
||||
// .stroke(1px, rgba(0, 0, 0, 0.07));
|
||||
transition: font-size @transition-slow;
|
||||
// text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3), 1px 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// p {
|
||||
// padding: 8px 0;
|
||||
// line-height: 1.2;
|
||||
// overflow-wrap: break-word;
|
||||
// transition: @transition-normal !important;
|
||||
// transition-property: color, font-size;
|
||||
// }
|
||||
}
|
||||
// .lrc-line {
|
||||
// display: inline-block;
|
||||
// padding: 8px 0;
|
||||
// line-height: 1.2;
|
||||
// overflow-wrap: break-word;
|
||||
// transition: @transition-normal;
|
||||
// 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 {
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
}
|
||||
// .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;
|
||||
// }
|
||||
// }
|
||||
.draging {
|
||||
:global {
|
||||
.line-content {
|
||||
.font-lrc {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
:global {
|
||||
.line-content {
|
||||
&.active {
|
||||
.extended {
|
||||
font-size: .94em;
|
||||
}
|
||||
.line {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
:global {
|
||||
.font-lrc, .shadow {
|
||||
display: -webkit-box !important;
|
||||
.mixin-ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// .font-weight {
|
||||
// :global {
|
||||
// .font-mode > .line {
|
||||
// font-weight: bold;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .footer {
|
||||
// flex: 0 0 100px;
|
||||
// overflow: hidden;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// }
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,202 @@
|
||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from '@common/utils/vueTools'
|
||||
import { scrollXRTo } from '@common/utils/renderer'
|
||||
import { lyric } from '@lyric/store/lyric'
|
||||
import { isPlay, setting } from '@lyric/store/state'
|
||||
import { setWindowBounds } from '@lyric/utils/ipc'
|
||||
|
||||
export default () => {
|
||||
const dom_lyric = ref(null)
|
||||
const dom_lyric_text = ref(null)
|
||||
const isMsDown = ref(false)
|
||||
let isStopScroll = false
|
||||
|
||||
const winEvent = {
|
||||
isMsDown: false,
|
||||
msDownX: 0,
|
||||
msDownY: 0,
|
||||
}
|
||||
|
||||
let msDownX = 0
|
||||
let msDownScrollX = 0
|
||||
let timeout = null
|
||||
let cancelScrollFn
|
||||
let dom_lines
|
||||
let isSetedLines = false
|
||||
|
||||
|
||||
const handleScrollLrc = (duration = 300) => {
|
||||
if (!dom_lines?.length || !dom_lyric.value) return
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
if (isStopScroll) return
|
||||
let dom_p = dom_lines[lyric.line]
|
||||
cancelScrollFn = scrollXRTo(dom_lyric.value, dom_p ? (dom_p.offsetLeft - dom_lyric.value.clientWidth * 0.5 + dom_p.clientWidth / 2) : 0, duration)
|
||||
}
|
||||
const clearLyricScrollTimeout = () => {
|
||||
if (!timeout) return
|
||||
clearTimeout(timeout)
|
||||
timeout = null
|
||||
}
|
||||
const startLyricScrollTimeout = () => {
|
||||
clearLyricScrollTimeout()
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
isStopScroll = false
|
||||
if (!isPlay.value) return
|
||||
handleScrollLrc()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const handleLyricDown = (target, x, y) => {
|
||||
if (target.classList.contains('font-lrc') ||
|
||||
target.parentNode.classList.contains('font-lrc') ||
|
||||
target.classList.contains('extended') ||
|
||||
target.parentNode.classList.contains('extended')
|
||||
) {
|
||||
if (delayScrollTimeout) {
|
||||
clearTimeout(delayScrollTimeout)
|
||||
delayScrollTimeout = null
|
||||
}
|
||||
isMsDown.value = true
|
||||
msDownX = x
|
||||
msDownScrollX = dom_lyric.value.scrollLeft
|
||||
} else {
|
||||
winEvent.isMsDown = true
|
||||
winEvent.msDownX = x
|
||||
winEvent.msDownY = y
|
||||
}
|
||||
}
|
||||
const handleLyricMouseDown = event => {
|
||||
handleLyricDown(event.target, event.clientX, event.clientY)
|
||||
}
|
||||
const handleLyricTouchStart = event => {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
handleLyricDown(event.target, touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
const handleMouseMsUp = () => {
|
||||
isMsDown.value = false
|
||||
winEvent.isMsDown = false
|
||||
}
|
||||
|
||||
const handleMove = (x, y) => {
|
||||
if (isMsDown.value) {
|
||||
if (!isStopScroll) isStopScroll = true
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollLeft = msDownScrollX + msDownX - x
|
||||
startLyricScrollTimeout()
|
||||
} else if (winEvent.isMsDown) {
|
||||
setWindowBounds({
|
||||
x: x - winEvent.msDownX,
|
||||
y: y - winEvent.msDownY,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleMouseMsMove = event => {
|
||||
handleMove(event.clientX, event.clientY)
|
||||
}
|
||||
const handleTouchMove = (e) => {
|
||||
if (e.changedTouches.length) {
|
||||
const touch = e.changedTouches[0]
|
||||
handleMove(touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
const handleWheel = (event) => {
|
||||
console.log(event.deltaY)
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
dom_lyric.value.scrollLeft = dom_lyric.value.scrollLeft - event.deltaY
|
||||
startLyricScrollTimeout()
|
||||
}
|
||||
|
||||
const setLyric = (lines) => {
|
||||
const dom_line_content = document.createDocumentFragment()
|
||||
for (const line of lines) {
|
||||
dom_line_content.appendChild(line.dom_line)
|
||||
}
|
||||
dom_lyric_text.value.textContent = ''
|
||||
dom_lyric_text.value.appendChild(dom_line_content)
|
||||
nextTick(() => {
|
||||
dom_lines = dom_lyric.value.querySelectorAll('.line-content')
|
||||
handleScrollLrc()
|
||||
})
|
||||
}
|
||||
|
||||
const initLrc = (lines, oLines) => {
|
||||
isSetedLines = true
|
||||
if (oLines) {
|
||||
if (lines.length) {
|
||||
setLyric(lines)
|
||||
} else {
|
||||
if (cancelScrollFn) {
|
||||
cancelScrollFn()
|
||||
cancelScrollFn = null
|
||||
}
|
||||
cancelScrollFn = scrollXRTo(dom_lyric.value, 0, 300, () => {
|
||||
if (lyric.lines !== lines) return
|
||||
setLyric(lines)
|
||||
}, 50)
|
||||
}
|
||||
} else {
|
||||
setLyric(lines)
|
||||
}
|
||||
}
|
||||
|
||||
let delayScrollTimeout
|
||||
const scrollLine = (line, oldLine) => {
|
||||
if (line < 0) return
|
||||
if (line == 0 && isSetedLines) return isSetedLines = false
|
||||
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
|
||||
|
||||
if (setting['desktopLyric.isDelayScroll']) {
|
||||
delayScrollTimeout = setTimeout(() => {
|
||||
delayScrollTimeout = null
|
||||
handleScrollLrc(600)
|
||||
}, 600)
|
||||
} else {
|
||||
handleScrollLrc()
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => lyric.lines, initLrc)
|
||||
watch(() => lyric.line, scrollLine)
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', handleMouseMsMove)
|
||||
document.addEventListener('mouseup', handleMouseMsUp)
|
||||
document.addEventListener('touchmove', handleTouchMove)
|
||||
document.addEventListener('touchend', handleMouseMsUp)
|
||||
|
||||
initLrc(lyric.lines, null)
|
||||
nextTick(() => {
|
||||
scrollLine(lyric.line)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', handleMouseMsMove)
|
||||
document.removeEventListener('mouseup', handleMouseMsUp)
|
||||
document.removeEventListener('touchmove', handleTouchMove)
|
||||
document.removeEventListener('touchend', handleMouseMsUp)
|
||||
})
|
||||
|
||||
return {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
isMsDown,
|
||||
handleLyricMouseDown,
|
||||
handleLyricTouchStart,
|
||||
handleWheel,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Lyric from '@common/utils/lyric-font-player'
|
||||
import { markRawList } from '@common/utils/vueTools'
|
||||
import { setLines, setOffset, setTempOffset, setText, lyrics } from '@lyric/store/lyric'
|
||||
import { musicInfo, setting } from '@lyric/store/state'
|
||||
|
||||
let lrc: Lyric
|
||||
|
||||
export const init = () => {
|
||||
lrc = new Lyric({
|
||||
shadowContent: true,
|
||||
activeLineClassName: 'active',
|
||||
isVertical: setting['desktopLyric.direction'] == 'vertical',
|
||||
onPlay(line, text) {
|
||||
setText(text, line)
|
||||
// console.log(line, text)
|
||||
},
|
||||
onSetLyric(lines, offset) { // listening lyrics seting event
|
||||
// console.log(lines) // lines is array of all lyric text
|
||||
setLines(markRawList([...lines]))
|
||||
setText(lines[0] ?? '', 0)
|
||||
setOffset(offset) // 歌词延迟
|
||||
setTempOffset(0) // 重置临时延迟
|
||||
},
|
||||
onUpdateLyric(lines) {
|
||||
setLines(markRawList([...lines]))
|
||||
setText(lines[0] ?? '', 0)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const setLyricOffset = (offset: number) => {
|
||||
setTempOffset(offset)
|
||||
lrc.setOffset(offset)
|
||||
}
|
||||
|
||||
export const setLyric = () => {
|
||||
if (!musicInfo.id) return
|
||||
const extendedLyrics = []
|
||||
if (setting['player.isShowLyricTranslation'] && lyrics.tlyric) extendedLyrics.push(lyrics.tlyric)
|
||||
if (setting['player.isShowLyricRoma'] && lyrics.rlyric) extendedLyrics.push(lyrics.rlyric)
|
||||
lrc.setLyric(
|
||||
setting['player.isPlayLxlrc'] && lyrics.lxlyric ? lyrics.lxlyric : lyrics.lyric,
|
||||
extendedLyrics,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const play = (time: number) => {
|
||||
if (!lyrics.lyric) return
|
||||
lrc.play(time)
|
||||
}
|
||||
|
||||
export const pause = () => {
|
||||
lrc.pause()
|
||||
}
|
||||
|
||||
export const stop = () => {
|
||||
lrc.setLyric('')
|
||||
// setLines([])
|
||||
setText('', 0)
|
||||
}
|
||||
|
||||
export const setVertical = (isVertical: boolean) => {
|
||||
lrc.setVertical(isVertical)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { onProvideMainWindowChannel } from '@lyric/utils/ipc'
|
||||
import { onBeforeUnmount } from '@common/utils/vueTools'
|
||||
import { setMusicInfo, setIsPlay } from '../store/action'
|
||||
import { pause, play, setLyric, setLyricOffset, stop } from './lyric'
|
||||
import { lyrics } from '@lyric/store/lyric'
|
||||
|
||||
let mainWindowPort: Electron.IpcRendererEvent['ports'][0] | null = null
|
||||
export const sendDesktopLyricInfo = (info: LX.DesktopLyric.WinMainActions) => {
|
||||
if (mainWindowPort == null) return
|
||||
mainWindowPort.postMessage({ action: info })
|
||||
}
|
||||
|
||||
const listeners: Array<(event: LX.DesktopLyric.LyricActions) => void> = []
|
||||
|
||||
const handleDesktopLyricMessage = (event: LX.DesktopLyric.LyricActions) => {
|
||||
switch (event.action) {
|
||||
case 'set_info':
|
||||
setMusicInfo({
|
||||
id: event.data.id,
|
||||
singer: event.data.singer,
|
||||
name: event.data.name,
|
||||
album: event.data.album,
|
||||
})
|
||||
lyrics.lyric = event.data.lrc ?? ''
|
||||
lyrics.tlyric = event.data.tlrc
|
||||
lyrics.rlyric = event.data.rlrc
|
||||
lyrics.lxlyric = event.data.lxlrc
|
||||
setLyric()
|
||||
if (event.data.isPlay) {
|
||||
setImmediate(() => {
|
||||
getStatus()
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'set_lyric':
|
||||
lyrics.lyric = event.data.lrc ?? ''
|
||||
lyrics.tlyric = event.data.tlrc
|
||||
lyrics.rlyric = event.data.rlrc
|
||||
lyrics.lxlyric = event.data.lxlrc
|
||||
setLyric()
|
||||
break
|
||||
case 'set_status':
|
||||
setIsPlay(event.data.isPlay)
|
||||
if (event.data.isPlay) play(event.data.played_time)
|
||||
else pause()
|
||||
break
|
||||
case 'set_offset':
|
||||
setLyricOffset(event.data)
|
||||
break
|
||||
case 'set_pause':
|
||||
setIsPlay(false)
|
||||
pause()
|
||||
break
|
||||
case 'set_play':
|
||||
setIsPlay(true)
|
||||
play(event.data)
|
||||
break
|
||||
case 'set_stop':
|
||||
setIsPlay(false)
|
||||
stop()
|
||||
break
|
||||
default:
|
||||
for (const listener of listeners) {
|
||||
listener(event)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
export const init = () => {
|
||||
onProvideMainWindowChannel(({ event }) => {
|
||||
const [port] = event.ports
|
||||
mainWindowPort = port
|
||||
|
||||
// ... register a handler to receive results ...
|
||||
port.onmessage = ({ data }) => {
|
||||
handleDesktopLyricMessage(data)
|
||||
// console.log('received result:', data)
|
||||
}
|
||||
// ... and start sending it work!
|
||||
|
||||
port.onmessageerror = (event) => {
|
||||
console.log('onmessageerror', event)
|
||||
}
|
||||
|
||||
getInfo()
|
||||
})
|
||||
}
|
||||
|
||||
export const useEvent = (listener: (event: LX.DesktopLyric.LyricActions) => void) => {
|
||||
listeners.push(listener)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
listeners.splice(listeners.indexOf(listener), 1)
|
||||
})
|
||||
}
|
||||
|
||||
export const getInfo = () => {
|
||||
sendDesktopLyricInfo('get_info')
|
||||
}
|
||||
|
||||
export const getAnalyserDataArray = () => {
|
||||
sendDesktopLyricInfo('get_analyser_data_array')
|
||||
}
|
||||
|
||||
export const getStatus = () => {
|
||||
sendDesktopLyricInfo('get_status')
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<html lang="cn">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
|
||||
<title>桌面歌词-洛雪音乐助手</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
window.dom_style_theme = document.createElement('style')
|
||||
window.dom_style_lyric = document.createElement('style')
|
||||
|
||||
window.setTheme = colors => {
|
||||
window.dom_style_theme.innerText = `:root {${(Object.entries(colors)).map(([key, value]) => `${key}:${value};`).join('')}}`
|
||||
}
|
||||
window.setLyricColor = colors => {
|
||||
console.log(colors)
|
||||
window.dom_style_lyric.innerText = `:root {${(Object.entries(colors)).map(([key, value]) => `${key}:${value};`).join('')}}`
|
||||
}
|
||||
document.body.appendChild(window.dom_style_lyric)
|
||||
|
||||
const applyThemeColor = (theme) => {
|
||||
theme = JSON.parse(decodeURIComponent(theme))
|
||||
window.setTheme(theme.colors)
|
||||
document.body.appendChild(window.dom_style_theme)
|
||||
}
|
||||
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
html(lang="cn")
|
||||
head
|
||||
meta(charset="UTF-8")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1.0")
|
||||
meta(http-equiv="X-UA-Compatible" content="ie=edge")
|
||||
title 桌面歌词-洛雪音乐助手
|
||||
|
||||
body
|
||||
#root
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import i18n from './plugins/i18n'
|
||||
|
||||
import mountComponents from './components'
|
||||
|
||||
import App from './App'
|
||||
|
||||
import '../common/error'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(i18n)
|
||||
mountComponents(app)
|
||||
app.mount('#root')
|
||||
@@ -0,0 +1,38 @@
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import { i18nPlugin } from './plugins/i18n'
|
||||
|
||||
import mountComponents from './components'
|
||||
|
||||
import App from './App.vue'
|
||||
|
||||
import '@/common/error'
|
||||
import { getSetting, onMainWindowInited, onSettingChanged, sendConnectMainWindowEvent } from './utils/ipc'
|
||||
import { initSetting, mergeSetting } from './store/action'
|
||||
import { init as initMainWindowChannel } from './core/mainWindowChannel'
|
||||
|
||||
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
|
||||
|
||||
void getSetting().then((setting) => {
|
||||
// window.lx.appSetting = setting
|
||||
// Set language automatically
|
||||
const languageId = setting['common.langId']
|
||||
if (window.i18n.locale !== languageId && languageId != null) window.i18n.setLanguage(languageId)
|
||||
|
||||
// store.commit('setSetting', setting)
|
||||
initSetting(setting)
|
||||
|
||||
onSettingChanged(({ params: setting }) => {
|
||||
// console.log('onSettingChanged', setting)
|
||||
mergeSetting(setting)
|
||||
})
|
||||
onMainWindowInited(() => {
|
||||
sendConnectMainWindowEvent()
|
||||
})
|
||||
initMainWindowChannel()
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(i18nPlugin)
|
||||
mountComponents(app)
|
||||
app.mount('#root')
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Vue i18n
|
||||
*
|
||||
* @library
|
||||
*
|
||||
* http://kazupon.github.io/vue-i18n/en/
|
||||
*/
|
||||
|
||||
// Lib imports
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { messages } from '@/lang'
|
||||
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'zh-cn',
|
||||
fallbackLocale: 'zh-cn',
|
||||
messages,
|
||||
})
|
||||
|
||||
window.i18n = i18n.global
|
||||
|
||||
export default i18n
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createI18n, i18nPlugin, useI18n, I18n } from '@/lang'
|
||||
|
||||
window.i18n = createI18n()
|
||||
|
||||
export {
|
||||
i18nPlugin,
|
||||
useI18n,
|
||||
I18n,
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { setting, musicInfo, isPlay } from './state'
|
||||
import { updateSetting as saveSetting } from '@lyric/utils/ipc'
|
||||
|
||||
export const initSetting = (newSetting: LX.DesktopLyric.Config) => {
|
||||
mergeSetting(newSetting)
|
||||
}
|
||||
|
||||
export const mergeSetting = (newSetting: Partial<LX.DesktopLyric.Config>) => {
|
||||
for (const [key, value] of Object.entries(newSetting)) {
|
||||
// @ts-expect-error
|
||||
setting[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
export const updateSetting = (setting: Partial<LX.DesktopLyric.Config>) => {
|
||||
void saveSetting(setting)
|
||||
}
|
||||
|
||||
type MusicInfoKeys = keyof typeof musicInfo
|
||||
const musicInfoKeys: MusicInfoKeys[] = Object.keys(musicInfo) as MusicInfoKeys[]
|
||||
|
||||
export const setMusicInfo = (_musicInfo: Partial<typeof musicInfo>) => {
|
||||
for (const key of musicInfoKeys) {
|
||||
const val = _musicInfo[key]
|
||||
if (val !== undefined) {
|
||||
// @ts-expect-error
|
||||
musicInfo[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const setIsPlay = (_status: boolean) => {
|
||||
isPlay.value = _status
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { markRaw, reactive } from '@common/utils/vueTools'
|
||||
|
||||
|
||||
export const lyrics = markRaw<{
|
||||
lyric: string
|
||||
tlyric: string | null
|
||||
rlyric: string | null
|
||||
lxlyric: string | null
|
||||
}>({
|
||||
lyric: '',
|
||||
tlyric: '',
|
||||
rlyric: '',
|
||||
lxlyric: '',
|
||||
})
|
||||
|
||||
interface Line {
|
||||
text: string
|
||||
time: number
|
||||
extendedLyrics: string[]
|
||||
dom_line: HTMLDivElement
|
||||
}
|
||||
|
||||
export const lyric = reactive<{
|
||||
lines: Line[]
|
||||
text: string
|
||||
line: number
|
||||
offset: number // 歌词延迟
|
||||
tempOffset: number // 歌词临时延迟
|
||||
}>({
|
||||
lines: [],
|
||||
text: '',
|
||||
line: 0,
|
||||
offset: 0, // 歌词延迟
|
||||
tempOffset: 0, // 歌词临时延迟
|
||||
})
|
||||
|
||||
export const setLines = (lines: Line[]) => {
|
||||
if (!lines.length && !lyric.lines.length) return
|
||||
lyric.lines = lines
|
||||
}
|
||||
export const setText = (text: string, line: number) => {
|
||||
lyric.text = text
|
||||
lyric.line = line
|
||||
}
|
||||
export const setOffset = (offset: number) => {
|
||||
lyric.offset = offset
|
||||
}
|
||||
export const setTempOffset = (offset: number) => {
|
||||
lyric.tempOffset = offset
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { ref, shallowReactive } from '@common/utils/vueTools'
|
||||
|
||||
export const setting = shallowReactive<LX.DesktopLyric.Config>({
|
||||
'desktopLyric.enable': false,
|
||||
'desktopLyric.isLock': false,
|
||||
'desktopLyric.isAlwaysOnTop': false,
|
||||
'desktopLyric.isAlwaysOnTopLoop': false,
|
||||
'desktopLyric.audioVisualization': false,
|
||||
'desktopLyric.width': 450,
|
||||
'desktopLyric.height': 300,
|
||||
'desktopLyric.x': null,
|
||||
'desktopLyric.y': null,
|
||||
'desktopLyric.isLockScreen': true,
|
||||
'desktopLyric.isDelayScroll': true,
|
||||
'desktopLyric.isHoverHide': false,
|
||||
'desktopLyric.direction': 'horizontal',
|
||||
'desktopLyric.style.align': 'center',
|
||||
'desktopLyric.style.lyricUnplayColor': 'rgba(255, 255, 255, 1)',
|
||||
'desktopLyric.style.lyricPlayedColor': 'rgba(7, 197, 86, 1)',
|
||||
'desktopLyric.style.lyricShadowColor': 'rgba(0, 0, 0, 0.14)',
|
||||
'desktopLyric.style.font': '',
|
||||
'desktopLyric.style.fontSize': 120,
|
||||
// 'desktopLyric.style.fontWeight': true,
|
||||
'desktopLyric.style.opacity': 95,
|
||||
'desktopLyric.style.ellipsis': false,
|
||||
'desktopLyric.style.isZoomActiveLrc': true,
|
||||
'common.langId': 'zh-cn',
|
||||
'player.isShowLyricTranslation': false,
|
||||
'player.isShowLyricRoma': false,
|
||||
'player.isPlayLxlrc': false,
|
||||
})
|
||||
|
||||
// export const themeList = markRaw([
|
||||
// {
|
||||
// id: 0,
|
||||
// className: 'green',
|
||||
// },
|
||||
// {
|
||||
// id: 1,
|
||||
// className: 'yellow',
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// className: 'blue',
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// className: 'red',
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// className: 'pink',
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// className: 'purple',
|
||||
// },
|
||||
// {
|
||||
// id: 6,
|
||||
// className: 'orange',
|
||||
// },
|
||||
// {
|
||||
// id: 7,
|
||||
// className: 'grey',
|
||||
// },
|
||||
// {
|
||||
// id: 8,
|
||||
// className: 'ming',
|
||||
// },
|
||||
// {
|
||||
// id: 9,
|
||||
// className: 'blue2',
|
||||
// },
|
||||
// ])
|
||||
|
||||
// export declare type Status = 'playing' | 'paused' | 'stopped'
|
||||
|
||||
// export const status = ref<Status>('stopped')
|
||||
export const isPlay = ref(false)
|
||||
|
||||
export const musicInfo = shallowReactive<{
|
||||
id: string | null
|
||||
name: string
|
||||
singer: string
|
||||
album: string | null
|
||||
}>({
|
||||
id: null,
|
||||
name: '^',
|
||||
singer: '^',
|
||||
album: null,
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "nodenext",
|
||||
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
"@common/*": ["common/*"],
|
||||
// "@renderer/*": ["renderer/*"],
|
||||
"@lyric/*": ["renderer-lyric/*"],
|
||||
"@static/*": ["static/*"],
|
||||
"@/*": ["./*"],
|
||||
},
|
||||
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"./types"
|
||||
],
|
||||
},
|
||||
"vueCompilerOptions": {
|
||||
"plugins": [
|
||||
"@volar/vue-language-plugin-pug"
|
||||
]
|
||||
}
|
||||
// "include": [
|
||||
// "./**/*.ts",
|
||||
// // "./**/*.js",
|
||||
// "./**/*.vue",
|
||||
// "./**/*.json",
|
||||
// ],
|
||||
}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { I18n } from '@lyric/plugins/i18n'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS?: string
|
||||
|
||||
i18n: I18n
|
||||
|
||||
lxData: any
|
||||
|
||||
setTheme: (colors: Record<string, string>) => void
|
||||
setLyricColor: (colors: Record<string, string>) => void
|
||||
}
|
||||
|
||||
namespace LX {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// declare const ELECTRON_DISABLE_SECURITY_WARNINGS: string
|
||||
// declare const userApiPath: string
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// import '@common/types/app_setting'
|
||||
// import '@common/types/common'
|
||||
// import '@common/types/user_api'
|
||||
// import '@common/types/sync'
|
||||
// import '@common/types/music'
|
||||
// import '@common/types/list'
|
||||
// import '@common/types/download_list'
|
||||
// import '@common/types/player'
|
||||
import '@common/types/shims_vue'
|
||||
// import '@common/types/utils'
|
||||
import '@common/types/theme'
|
||||
import '@common/types/desktop_lyric'
|
||||
import '@common/types/ipc_renderer'
|
||||
@@ -0,0 +1,88 @@
|
||||
import { computed, ref } from '@common/utils/vueTools'
|
||||
import { setting } from '@lyric/store/state'
|
||||
|
||||
let mouseCheckTools: {
|
||||
x: number
|
||||
y: number
|
||||
preX: number
|
||||
preY: number
|
||||
timeout: NodeJS.Timeout | null
|
||||
handleCheck: (setShow: () => void) => void
|
||||
handleMove: (x: number, y: number, setShow: () => void) => void
|
||||
startTimeout: (setShow: () => void) => void
|
||||
stopTimeout: () => void
|
||||
} = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
preX: 0,
|
||||
preY: 0,
|
||||
timeout: null,
|
||||
handleCheck(setShow: () => void) {
|
||||
let xDiff = Math.abs(this.x - this.preX)
|
||||
let yDiff = Math.abs(this.y - this.preY)
|
||||
if (xDiff > 8) {
|
||||
if (this.x > this.preX) {
|
||||
if (this.x + xDiff * 1.25 > window.innerWidth - 16) return setShow()
|
||||
} else {
|
||||
if (this.x - xDiff * 1.25 < 8) return setShow()
|
||||
}
|
||||
}
|
||||
if (yDiff > 8) {
|
||||
if (this.y > this.preY) {
|
||||
if (this.y + yDiff * 1.25 > window.innerHeight - 16) return setShow()
|
||||
} else {
|
||||
if (this.y - yDiff * 1.25 < 8) return setShow()
|
||||
}
|
||||
}
|
||||
|
||||
// setShow(false)
|
||||
},
|
||||
handleMove(x: number, y: number, setShow: () => void) {
|
||||
// console.log(x, y, this.x, this.y)
|
||||
this.preX = this.x
|
||||
this.preY = this.y
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.startTimeout(setShow)
|
||||
},
|
||||
startTimeout(setShow: () => void) {
|
||||
this.stopTimeout()
|
||||
this.timeout = setTimeout(this.handleCheck.bind(this), 200, setShow)
|
||||
},
|
||||
stopTimeout() {
|
||||
if (!this.timeout) return
|
||||
clearTimeout(this.timeout)
|
||||
this.timeout = null
|
||||
},
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const isMouseEnter = ref(false)
|
||||
|
||||
const isHoverHide = computed(() => {
|
||||
return setting['desktopLyric.isLock'] && setting['desktopLyric.isHoverHide']
|
||||
})
|
||||
|
||||
const handleMouseMoveMain = (event: MouseEvent) => {
|
||||
if (!isHoverHide.value) return
|
||||
handleMouseEnter()
|
||||
mouseCheckTools.handleMove(event.clientX, event.clientY, () => {
|
||||
handleMouseLeave()
|
||||
})
|
||||
}
|
||||
const handleMouseEnter = () => {
|
||||
if (!isHoverHide.value || isMouseEnter.value) return
|
||||
isMouseEnter.value = true
|
||||
}
|
||||
const handleMouseLeave = () => {
|
||||
if (!isHoverHide.value) return
|
||||
isMouseEnter.value = false
|
||||
mouseCheckTools.stopTimeout()
|
||||
}
|
||||
|
||||
return {
|
||||
isMouseEnter,
|
||||
isHoverHide,
|
||||
handleMouseMoveMain,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { watch } from '@common/utils/vueTools'
|
||||
import { setLyric, setVertical } from '@lyric/core/lyric'
|
||||
import { setting } from '@lyric/store/state'
|
||||
|
||||
export default () => {
|
||||
watch(() => setting['player.isShowLyricTranslation'], setLyric)
|
||||
watch(() => setting['player.isShowLyricRoma'], setLyric)
|
||||
watch(() => setting['player.isPlayLxlrc'], setLyric)
|
||||
watch(() => setting['desktopLyric.direction'], (direction) => {
|
||||
setVertical(direction == 'vertical')
|
||||
// if (isPlay.value)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { onThemeChange } from '@lyric/utils/ipc'
|
||||
import { RGB_Alpha_Shade } from '@common/theme/colorUtils'
|
||||
|
||||
export default () => {
|
||||
const rThemeChange = onThemeChange(({ params: setting }) => {
|
||||
window.setTheme(setting.theme.colors)
|
||||
})
|
||||
watch(() => [setting['desktopLyric.style.lyricUnplayColor'], setting['desktopLyric.style.lyricPlayedColor'], setting['desktopLyric.style.lyricShadowColor']], ([unplayColor, playedColor, shadowColor]) => {
|
||||
window.setLyricColor({
|
||||
'--color-lyric-unplay': unplayColor,
|
||||
'--color-lyric-played': playedColor,
|
||||
'--color-lyric-shadow': shadowColor,
|
||||
'--color-lyric-shadow-font-mode': RGB_Alpha_Shade(0.5, shadowColor),
|
||||
})
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
rThemeChange()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { onBeforeUnmount, onMounted } from '@common/utils/vueTools'
|
||||
import { setWindowBounds } from '@lyric/utils/ipc'
|
||||
|
||||
type Origin = 'left'
|
||||
| 'top'
|
||||
| 'right'
|
||||
| 'bottom'
|
||||
| 'top-left'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right'
|
||||
|
||||
export default () => {
|
||||
const resize: {
|
||||
origin: Origin | null
|
||||
msDownX: number
|
||||
msDownY: number
|
||||
} = {
|
||||
origin: null,
|
||||
msDownX: 0,
|
||||
msDownY: 0,
|
||||
}
|
||||
|
||||
const handleMove = (clientX: number, clientY: number) => {
|
||||
if (!resize.origin || setting['desktopLyric.isLock']) return
|
||||
// if (!event.target.classList.contains('resize-' + resize.origin)) return
|
||||
// console.log(event.target)
|
||||
let bounds: LX.DesktopLyric.NewBounds = {
|
||||
w: 0,
|
||||
h: 0,
|
||||
y: 0,
|
||||
}
|
||||
let temp
|
||||
switch (resize.origin) {
|
||||
case 'left':
|
||||
temp = clientX - resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
break
|
||||
case 'right':
|
||||
bounds.w = clientX - resize.msDownX
|
||||
resize.msDownX += bounds.w
|
||||
break
|
||||
case 'top':
|
||||
temp = clientY - resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
break
|
||||
case 'bottom':
|
||||
bounds.h = clientY - resize.msDownY
|
||||
resize.msDownY += bounds.h
|
||||
break
|
||||
case 'top-left':
|
||||
temp = clientX - resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
temp = clientY - resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
break
|
||||
case 'top-right':
|
||||
temp = clientY - resize.msDownY
|
||||
bounds.y = temp
|
||||
bounds.h = -temp
|
||||
bounds.w = clientX - resize.msDownX
|
||||
resize.msDownX += bounds.w
|
||||
break
|
||||
case 'bottom-left':
|
||||
temp = clientX - resize.msDownX
|
||||
bounds.w = -temp
|
||||
bounds.x = temp
|
||||
bounds.h = clientY - resize.msDownY
|
||||
resize.msDownY += bounds.h
|
||||
break
|
||||
case 'bottom-right':
|
||||
bounds.w = clientX - resize.msDownX
|
||||
resize.msDownX += bounds.w
|
||||
bounds.h = clientY - resize.msDownY
|
||||
resize.msDownY += bounds.h
|
||||
break
|
||||
}
|
||||
// console.log(bounds)
|
||||
bounds.w = window.innerWidth + bounds.w
|
||||
bounds.h = window.innerHeight + bounds.h
|
||||
setWindowBounds(bounds)
|
||||
}
|
||||
|
||||
const handleDown = (origin: Origin, clientX: number, clientY: number) => {
|
||||
handleMouseUp()
|
||||
resize.origin = origin
|
||||
resize.msDownX = clientX
|
||||
resize.msDownY = clientY
|
||||
}
|
||||
const handleMouseUp = () => {
|
||||
resize.origin = null
|
||||
}
|
||||
|
||||
const handleMouseDown = (origin: Origin, event: MouseEvent) => {
|
||||
handleDown(origin, event.clientX, event.clientY)
|
||||
}
|
||||
const handleTouchDown = (origin: Origin, event: TouchEvent) => {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
handleDown(origin, touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
handleMove(event.clientX, event.clientY)
|
||||
}
|
||||
const handleTouchMove = (event: TouchEvent) => {
|
||||
if (event.changedTouches.length) {
|
||||
const touch = event.changedTouches[0]
|
||||
handleMove(touch.clientX, touch.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
document.addEventListener('touchmove', handleTouchMove)
|
||||
document.addEventListener('touchend', handleMouseUp)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
document.removeEventListener('touchmove', handleTouchMove)
|
||||
document.removeEventListener('touchend', handleMouseUp)
|
||||
})
|
||||
|
||||
return {
|
||||
handleMouseDown,
|
||||
handleTouchDown,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { rendererSend, rendererInvoke, rendererOn, rendererOff } from '@common/rendererIpc'
|
||||
import { CMMON_EVENT_NAME, WIN_LYRIC_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
|
||||
type RemoveListener = () => void
|
||||
|
||||
export const getSetting = async() => {
|
||||
return await rendererInvoke<LX.DesktopLyric.Config>(WIN_LYRIC_RENDERER_EVENT_NAME.get_config)
|
||||
}
|
||||
export const updateSetting = async(setting: Partial<LX.DesktopLyric.Config>) => {
|
||||
await rendererInvoke(WIN_LYRIC_RENDERER_EVENT_NAME.set_config, setting)
|
||||
}
|
||||
export const onSettingChanged = (listener: LX.IpcRendererEventListenerParams<Partial<LX.DesktopLyric.Config>>): RemoveListener => {
|
||||
rendererOn<Partial<LX.DesktopLyric.Config>>(WIN_LYRIC_RENDERER_EVENT_NAME.on_config_change, listener)
|
||||
return () => {
|
||||
rendererOff(WIN_LYRIC_RENDERER_EVENT_NAME.on_config_change, listener)
|
||||
}
|
||||
}
|
||||
export const setWindowBounds = (bounds: LX.DesktopLyric.NewBounds) => {
|
||||
rendererSend<LX.DesktopLyric.NewBounds>(WIN_LYRIC_RENDERER_EVENT_NAME.set_win_bounds, bounds)
|
||||
}
|
||||
|
||||
export const sendConnectMainWindowEvent = () => {
|
||||
rendererSend(WIN_LYRIC_RENDERER_EVENT_NAME.request_main_window_channel)
|
||||
}
|
||||
export const onProvideMainWindowChannel = (listener: LX.IpcRendererEventListener): RemoveListener => {
|
||||
rendererOn(WIN_LYRIC_RENDERER_EVENT_NAME.provide_main_window_channel, listener)
|
||||
return () => {
|
||||
rendererOff(WIN_LYRIC_RENDERER_EVENT_NAME.provide_main_window_channel, listener)
|
||||
}
|
||||
}
|
||||
export const onMainWindowInited = (listener: LX.IpcRendererEventListener): RemoveListener => {
|
||||
rendererOn(WIN_LYRIC_RENDERER_EVENT_NAME.main_window_inited, listener)
|
||||
return () => {
|
||||
rendererOff(WIN_LYRIC_RENDERER_EVENT_NAME.main_window_inited, listener)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On Theme Change
|
||||
* @param listener LX.IpcRendererEventListenerParams<shouldUseDarkColors: boolean>
|
||||
* @returns RemoveListener Fn
|
||||
*/
|
||||
export const onThemeChange = (listener: LX.IpcRendererEventListenerParams<LX.ThemeSetting>): RemoveListener => {
|
||||
rendererOn(CMMON_EVENT_NAME.theme_change, listener)
|
||||
return () => {
|
||||
rendererOff(CMMON_EVENT_NAME.theme_change, listener)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user