新增播放详情页歌词右键菜单,新增歌词偏移设置

This commit is contained in:
lyswhut
2022-03-22 16:55:44 +08:00
parent 82ea1935de
commit bc301c27c2
18 changed files with 572 additions and 62 deletions
@@ -1,5 +1,5 @@
<template>
<div :class="['right', $style.right]" :style="lrcFontSize">
<div :class="['right', $style.right]" :style="lrcFontSize" @contextmenu.stop="handleShowLyricMenu">
<div :class="['lyric', $style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }]" :style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric">
<div :class="['pre', $style.lyricSpace]"></div>
<div ref="dom_lyric_text"></div>
@@ -25,6 +25,7 @@
</div>
</div>
</transition>
<LyricMenu v-model="lyricMenuVisible" :xy="lyricMenuXY" :lyricInfo="lyricInfo" @updateLyric="handleUpdateLyric" />
</div>
</template>
@@ -32,14 +33,18 @@
import { clipboardWriteText } from '@renderer/utils'
import { lyric } from '@renderer/core/share/lyric'
import { isFullscreen } from '@renderer/core/share'
import { isPlay, isShowLrcSelectContent, isShowPlayComment } from '@renderer/core/share/player'
import { onMounted, onBeforeUnmount, useCommit, useRefGetter, computed } from '@renderer/utils/vueTools'
import { isPlay, isShowLrcSelectContent, isShowPlayComment, musicInfo as playerMusicInfo, musicInfoItem, setMusicInfo } from '@renderer/core/share/player'
import { onMounted, onBeforeUnmount, useRefGetter, computed, reactive, ref } from '@renderer/utils/vueTools'
import useLyric from '@renderer/utils/compositions/useLyric'
import LyricMenu from './components/LyricMenu'
import { player as eventPlayerNames } from '@renderer/event/names'
export default {
components: {
LyricMenu,
},
setup() {
const setting = useRefGetter('setting')
const setPlayDetailLyricFont = useCommit('setPlayDetailLyricFont')
const {
dom_lyric,
dom_lyric_text,
@@ -54,13 +59,37 @@ export default {
handleSkipMouseLeave,
} = useLyric({ isPlay, lyric })
const fontSizeUp = () => {
if (setting.value.playDetail.style.fontSize >= 200) return
setPlayDetailLyricFont(setting.value.playDetail.style.fontSize + 1)
const lyricMenuVisible = ref(false)
const lyricMenuXY = reactive({
x: 0,
y: 0,
})
const lyricInfo = reactive({
lyric: '',
tlyric: '',
lxlyric: '',
musicInfo: null,
})
const updateMusicInfo = () => {
lyricInfo.lyric = playerMusicInfo.lrc
lyricInfo.tlyric = playerMusicInfo.tlrc
lyricInfo.lxlyric = playerMusicInfo.lxlrc
lyricInfo.musicInfo = musicInfoItem.value
}
const fontSizeDown = () => {
if (setting.value.playDetail.style.fontSize <= 70) return
setPlayDetailLyricFont(setting.value.playDetail.style.fontSize - 1)
const handleShowLyricMenu = event => {
updateMusicInfo()
lyricMenuXY.x = event.pageX
lyricMenuXY.y = event.pageY
lyricMenuVisible.value = true
}
const handleUpdateLyric = ({ lyric, tlyric, lxlyric, offset }) => {
setMusicInfo({
lrc: lyric,
tlrc: tlyric,
lxlrc: lxlyric,
})
console.log(offset)
window.eventHub.emit(eventPlayerNames.updateLyricOffset, offset)
}
const lrcStyles = computed(() => {
@@ -79,16 +108,10 @@ export default {
const isShowLyricProgressSetting = computed(() => setting.value.playDetail.isShowLyricProgressSetting)
onMounted(() => {
window.eventHub.on('key_shift++_down', fontSizeUp)
window.eventHub.on('key_numadd_down', fontSizeUp)
window.eventHub.on('key_-_down', fontSizeDown)
window.eventHub.on('key_numsub_down', fontSizeDown)
window.eventHub.on(eventPlayerNames.updateLyric, updateMusicInfo)
})
onBeforeUnmount(() => {
window.eventHub.off('key_shift++_down', fontSizeUp)
window.eventHub.off('key_numadd_down', fontSizeUp)
window.eventHub.off('key_-_down', fontSizeDown)
window.eventHub.off('key_numsub_down', fontSizeDown)
window.eventHub.off(eventPlayerNames.updateLyric, updateMusicInfo)
})
return {
@@ -109,6 +132,11 @@ export default {
isShowLyricProgressSetting,
isZoomActiveLrc,
isStopScroll,
lyricMenuVisible,
lyricMenuXY,
handleShowLyricMenu,
handleUpdateLyric,
lyricInfo,
}
},
methods: {
@@ -0,0 +1,300 @@
<template>
<teleport to="#root">
<div :class="$style.container" :style="menuStyles" ref="dom_menu" :aria-hidden="!modelValue">
<!-- <div :class="$style.group">
<div :class="$style.title">{{$t('lyric_menu__align')}}</div>
<div :class="$style.subGroup">
<div :class="[$style.btn, { [$style.active]: playDetailSetting.style.align == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{$t('lyric_menu__align_left')}}</div>
<div :class="[$style.btn, { [$style.active]: playDetailSetting.style.align == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{$t('lyric_menu__align_center')}}</div>
</div>
</div> -->
<div :class="$style.group">
<div :class="$style.subGroup">
<div :class="$style.title">{{$t('lyric_menu__lrc_size', { size: playDetailSetting.style.fontSize })}}</div>
<button :class="[$style.btn, $style.titleBtn]" :disabled="playDetailSetting.style.fontSize == 100" @click="fontSizeReset" ignore-tip :aria-label="$t('lyric_menu__size_reset')">{{$t('lyric_menu__size_reset')}}</button>
</div>
<div :class="$style.subGroup">
<button :class="$style.btn" @click="fontSizeUp(5)" @contextmenu="fontSizeUp(1)" :aria-label="$t('lyric_menu__size_add')">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="18px" viewBox="0 0 24 24" space="preserve">
<use xlink:href="#icon-font-increase"></use>
</svg>
</button>
<button :class="$style.btn" @click="fontSizeDown(5)" @contextmenu="fontSizeDown(1)" :aria-label="$t('lyric_menu__size_dec')">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="18px" viewBox="0 0 24 24" space="preserve">
<use xlink:href="#icon-font-decrease"></use>
</svg>
</button>
</div>
</div>
<div :class="$style.group">
<div :class="$style.subGroup">
<div :class="$style.title">{{$t('lyric_menu__offset', { offset })}}</div>
<button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || !offset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button>
</div>
<div :class="$style.subGroup">
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(10)" @contextmenu="setOffset(5)" :aria-label="$t('lyric_menu__offset_add_10')">+ 10ms</button>
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(-10)" @contextmenu="setOffset(-5)" :aria-label="$t('lyric_menu__offset_dec_10')">- 10ms</button>
</div>
<div :class="$style.subGroup">
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(100)" @contextmenu="setOffset(50)" :aria-label="$t('lyric_menu__offset_add_100')">+ 100ms</button>
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(-100)" @contextmenu="setOffset(-50)" :aria-label="$t('lyric_menu__offset_dec_100')">- 100ms</button>
</div>
</div>
</div>
</teleport>
</template>
<script>
import { computed, useRefGetter, ref, useCommit, watch } from '@renderer/utils/vueTools'
import useMenuLocation from '@renderer/utils/compositions/useMenuLocation'
import { setLyricEdited, removeLyricEdited, debounce } from '@renderer/utils'
const offsetTagRxp = /^\s*\[offset:\s*(\S+(?:\d+)*)\s*\]/
const saveLyric = debounce((musicInfo, lyricInfo) => {
setLyricEdited(musicInfo, lyricInfo)
})
const removeLyric = debounce(musicInfo => {
removeLyricEdited(musicInfo)
})
export default {
name: 'LyricMenu',
props: {
modelValue: Boolean,
xy: Object,
lyricInfo: Object,
},
emits: ['updateLyric', 'update:modelValue'],
setup(props, { emit }) {
const setting = useRefGetter('setting')
const playDetailSetting = useRefGetter('playDetailSetting')
const setPlayDetailLyricAlign = useCommit('setPlayDetailLyricAlign')
const setPlayDetailLyricFont = useCommit('setPlayDetailLyricFont')
const offset = ref(0)
const offsetDisabled = ref(true)
const visible = computed(() => props.modelValue)
const musicInfo = computed(() => props.lyricInfo.musicInfo)
const location = computed(() => props.xy)
const onHide = () => {
emit('update:modelValue', false)
}
const setFontAlign = val => {
if (playDetailSetting.value.style.align == val) return
setPlayDetailLyricAlign(val)
}
const fontSizeUp = step => {
if (setting.value.playDetail.style.fontSize >= 200) return
setPlayDetailLyricFont(Math.min(setting.value.playDetail.style.fontSize + step, 200))
}
const fontSizeDown = step => {
if (setting.value.playDetail.style.fontSize <= 70) return
setPlayDetailLyricFont(Math.max(setting.value.playDetail.style.fontSize - step, 70))
}
const fontSizeReset = () => {
setPlayDetailLyricFont(100)
}
const updateLyric = offset => {
let lyric = props.lyricInfo.lyric
let tlyric = props.lyricInfo.tlyric
let lxlyric = props.lyricInfo.lxlyric
if (offsetTagRxp.test(lyric)) {
lyric = lyric.replace(offsetTagRxp, `[offset:${offset}]`)
if (tlyric) tlyric = tlyric.replace(offsetTagRxp, `[offset:${offset}]`)
if (lxlyric) lxlyric = lxlyric.replace(offsetTagRxp, `[offset:${offset}]`)
} else {
lyric = `[offset:${offset}]\n` + lyric
if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric
if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric
}
if (offset) {
saveLyric(props.lyricInfo.musicInfo, {
lyric,
tlyric,
lxlyric,
})
} else removeLyric(props.lyricInfo.musicInfo)
emit('updateLyric', {
lyric,
tlyric,
lxlyric,
offset,
})
}
const setOffset = step => {
offset.value += step
updateLyric(offset.value)
}
const offsetReset = () => {
if (!offset.value) return
offset.value = 0
updateLyric(0)
}
const parseLrcOffset = () => {
let lrcOffset
if (props.lyricInfo.lyric) {
lrcOffset = offsetTagRxp.exec(props.lyricInfo.lyric)
if (lrcOffset) {
lrcOffset = parseInt(lrcOffset[1])
if (Number.isNaN(lrcOffset)) lrcOffset = 0
} else lrcOffset = 0
offsetDisabled.value = false
} else {
offsetDisabled.value = true
lrcOffset = 0
}
offset.value = lrcOffset
}
const { dom_menu, menuStyles } = useMenuLocation({
visible,
location,
onHide,
})
watch(musicInfo, () => {
if (!props.modelValue) return
parseLrcOffset()
})
watch(visible, val => {
if (!val) return
parseLrcOffset()
})
return {
dom_menu,
menuStyles,
playDetailSetting,
offset,
fontSizeUp,
fontSizeDown,
fontSizeReset,
setOffset,
offsetReset,
setFontAlign,
offsetDisabled,
}
},
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.container {
font-size: 12px;
position: absolute;
opacity: 0;
transform: scale(0);
transform-origin: 0 0 0;
transition: .25s ease;
transition-property: transform, opacity;
border-radius: @radius-border;
background-color: @color-theme_2-background_2;
box-shadow: 0 1px 8px 0 rgba(0,0,0,.2);
z-index: 10;
overflow: hidden;
}
.group {
display: flex;
flex-direction: column;
}
.title {
flex: auto;
padding: 10px 0 10px 10px;
color: @color-theme_2-font-label;
white-space: nowrap;
min-width: 120px;
}
.subGroup {
display: flex;
flex-flow: row nowrap;
}
.btn {
flex: auto;
white-space: nowrap;
cursor: pointer;
min-width: 60px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
// color: @color-btn;
padding: 0 10px;
outline: none;
transition: @transition-theme;
transition-property: background-color, opacity;
box-sizing: border-box;
.mixin-ellipsis-1;
background-color: @color-theme_2-background_2;
border: none;
&:hover {
opacity: .7;
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
&.active {
background-color: @color-theme_2-background_2;
color: @color-btn-active;
cursor: default;
opacity: 1;
}
&[disabled] {
cursor: default;
opacity: .4;
&:hover {
background: none !important;
}
}
}
.titleBtn {
flex: none;
padding: 0 10;
min-width: 40px;
opacity: .7;
&[disabled] {
opacity: .3;
}
}
each(@themes, {
:global(#root.@{value}) {
.container {
background-color: ~'@{color-@{value}-theme_2-background_2}';
}
.btn {
background-color: ~'@{color-@{value}-theme_2-background_2}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
&.active {
background-color: ~'@{color-@{value}-theme_2-background_2}';
color: ~'@{color-@{value}-btn}';
}
}
}
})
</style>