feat: 优化逐字歌词效果,桌面歌词添加逐字歌词效果

This commit is contained in:
alger
2025-10-12 17:11:48 +08:00
parent 77f3069e67
commit a5d3ff359c
8 changed files with 351 additions and 82 deletions
+32 -10
View File
@@ -111,27 +111,31 @@
</span>
</div>
</div>
<!-- 无时间戳歌词提示 -->
<div v-if="!supportAutoScroll" class="music-lrc-text no-scroll-tip">
<span>本歌词不支持自动滚动</span>
</div>
<div
v-for="(item, index) in lrcArray"
:id="`music-lrc-text-${index}`"
:key="index"
class="music-lrc-text"
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
@click="setAudioTime(index)"
:class="{
'now-text': index === nowIndex,
'hover-text': item.text && item.startTime !== -1
}"
@click="item.startTime !== -1 ? setAudioTime(index) : null"
>
<!-- 逐字歌词显示 -->
<div
v-if="item.hasWordByWord && item.words && item.words.length > 0"
class="word-by-word-lyric"
>
<span
v-for="(word, wordIndex) in item.words"
:key="wordIndex"
class="lyric-word"
:style="getWordStyle(index, wordIndex, word)"
<template v-for="(word, wordIndex) in item.words" :key="wordIndex">
<span class="lyric-word" :style="getWordStyle(index, wordIndex, word)">
{{ word.text }} </span
><span class="lyric-word" v-if="word.space">&nbsp;</span></template
>
{{ word.text }}
</span>
</div>
<!-- 普通歌词显示 -->
<span v-else :style="getLrcStyle(index)">{{ item.text }}</span>
@@ -221,6 +225,10 @@ watch(
{ deep: true }
);
const supportAutoScroll = computed(() => {
return lrcArray.value.length > 0 && lrcArray.value[0].startTime !== -1;
});
const props = defineProps({
modelValue: {
type: Boolean,
@@ -246,7 +254,7 @@ const isVisible = computed({
// 歌词滚动方法
const lrcScroll = (behavior: ScrollBehavior = 'smooth', forceTop: boolean = false) => {
if (!isVisible.value || !lrcSider.value) return;
if (!isVisible.value || !lrcSider.value || !supportAutoScroll.value) return;
if (forceTop) {
lrcSider.value.scrollTo({
@@ -749,6 +757,20 @@ defineExpose({
letter-spacing: var(--lyric-letter-spacing, 0) !important;
line-height: var(--lyric-line-height, 2) !important;
&.no-scroll-tip {
@apply text-base opacity-60 cursor-default py-2;
color: var(--text-color-primary);
font-weight: normal;
span {
padding-right: 0;
}
&:hover {
background-color: transparent;
}
}
span {
background-clip: text !important;
-webkit-background-clip: text !important;
@@ -49,27 +49,31 @@
@scroll="handleScroll"
>
<div class="lyrics-padding-top"></div>
<!-- 无时间戳歌词提示 -->
<div v-if="!supportAutoScroll" class="lyric-line no-scroll-tip">
<span>本歌词不支持自动滚动</span>
</div>
<div
v-for="(item, index) in lrcArray"
:key="index"
:id="`lyric-line-${index}`"
class="lyric-line"
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
@click="jumpToLyricTime(index)"
:class="{
'now-text': index === nowIndex,
'hover-text': item.text && item.startTime !== -1
}"
@click="item.startTime !== -1 ? jumpToLyricTime(index) : null"
>
<!-- 逐字歌词显示 -->
<div
v-if="item.hasWordByWord && item.words && item.words.length > 0"
class="word-by-word-lyric"
>
<span
v-for="(word, wordIndex) in item.words"
:key="wordIndex"
class="lyric-word"
:style="getWordStyle(index, wordIndex, word)"
<template v-for="(word, wordIndex) in item.words" :key="wordIndex">
<span class="lyric-word" :style="getWordStyle(index, wordIndex, word)">
{{ word.text }} </span
><span class="lyric-word" v-if="word.space">&nbsp;</span></template
>
{{ word.text }}
</span>
</div>
<!-- 普通歌词显示 -->
<span v-else :style="getLrcStyle(index)">{{ item.text }}</span>
@@ -247,27 +251,31 @@
@scroll="handleScroll"
>
<div class="lyrics-padding-top"></div>
<!-- 无时间戳歌词提示 -->
<div v-if="!supportAutoScroll" class="lyric-line no-scroll-tip">
<span>本歌词不支持自动滚动</span>
</div>
<div
v-for="(item, index) in lrcArray"
:key="index"
:id="`landscape-lyric-line-${index}`"
class="lyric-line"
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
@click="jumpToLyricTime(index)"
:class="{
'now-text': index === nowIndex,
'hover-text': item.text && item.startTime !== -1
}"
@click="item.startTime !== -1 ? jumpToLyricTime(index) : null"
>
<!-- 逐字歌词显示 -->
<div
v-if="item.hasWordByWord && item.words && item.words.length > 0"
class="word-by-word-lyric"
>
<span
v-for="(word, wordIndex) in item.words"
:key="wordIndex"
class="lyric-word"
:style="getWordStyle(index, wordIndex, word)"
<template v-for="(word, wordIndex) in item.words" :key="wordIndex">
<span class="lyric-word" :style="getWordStyle(index, wordIndex, word)">
{{ word.text }} </span
><span class="lyric-word" v-if="word.space">&nbsp;</span></template
>
{{ word.text }}
</span>
</div>
<!-- 普通歌词显示 -->
<span v-else :style="getLrcStyle(index)">{{ item.text }}</span>
@@ -458,6 +466,10 @@ const showFullLyricScreen = () => {
});
};
const supportAutoScroll = computed(() => {
return lrcArray.value.length > 0 && lrcArray.value[0].startTime !== -1;
});
// 关闭全屏歌词
const closeFullLyrics = () => {
showFullLyrics.value = false;
@@ -476,6 +488,11 @@ const scrollToCurrentLyric = (immediate = false, customScrollerRef?: HTMLElement
return;
}
if (!supportAutoScroll.value) {
console.log('歌词不支持自动滚动');
return;
}
// 如果用户正在手动滚动,不打断他们的操作
if (isTouchScrolling.value && !immediate) {
return;
@@ -1681,6 +1698,16 @@ const getWordStyle = (lineIndex: number, _wordIndex: number, word: any) => {
color: var(--text-color-primary);
opacity: 0.8;
&.no-scroll-tip {
@apply text-base opacity-60 cursor-default py-2;
color: var(--text-color-primary);
font-weight: normal;
span {
padding-right: 0;
}
}
span {
background-clip: text !important;
-webkit-background-clip: text !important;
@@ -1712,6 +1739,7 @@ const getWordStyle = (lineIndex: number, _wordIndex: number, word: any) => {
line-height: inherit;
cursor: inherit;
position: relative;
padding-right: 0 !important;
&:hover {
background-color: rgba(255, 255, 255, 0.1);