Skip to content

Commit c975344

Browse files
committed
feat: 添加歌词矫正功能,支持增加和减少矫正时间
1 parent 08a1435 commit c975344

File tree

5 files changed

+131
-17
lines changed

5 files changed

+131
-17
lines changed

src/i18n/lang/en-US/player.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export default {
1111
mute: 'Mute',
1212
unmute: 'Unmute',
1313
songNum: 'Song Number: {num}',
14+
addCorrection: 'Add {num} seconds',
15+
subtractCorrection: 'Subtract {num} seconds',
1416
playFailed: 'Play Failed, Play Next Song',
1517
playMode: {
1618
sequence: 'Sequence',

src/i18n/lang/zh-CN/player.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export default {
1111
mute: '静音',
1212
unmute: '取消静音',
1313
songNum: '歌曲总数:{num}',
14+
addCorrection: '提前 {num} 秒',
15+
subtractCorrection: '延迟 {num} 秒',
1416
playFailed: '当前歌曲播放失败,播放下一首',
1517
playMode: {
1618
sequence: '顺序播放',
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script setup lang="ts">
2+
import { defineProps, defineEmits } from 'vue';
3+
import { useI18n } from 'vue-i18n';
4+
5+
const props = defineProps<{
6+
correctionTime: number
7+
}>();
8+
const emit = defineEmits<{
9+
(e: 'adjust', delta: number): void
10+
}>();
11+
12+
const { t } = useI18n();
13+
</script>
14+
15+
<template>
16+
<div
17+
class="lyric-correction-btns-mac absolute right-0 bottom-4 flex flex-col items-center space-y-1 z-50 select-none transition-opacity duration-200 opacity-0 pointer-events-none"
18+
>
19+
<n-tooltip placement="right">
20+
<template #trigger>
21+
<div
22+
class="lyric-correction-btn"
23+
@click="emit('adjust', -0.2)"
24+
:title="t('player.subtractCorrection', { num: 0.2 })"
25+
>
26+
<i class="ri-subtract-line text-base"></i>
27+
</div>
28+
</template>
29+
<span>{{ t('player.subtractCorrection', { num: 0.2 }) }}</span>
30+
</n-tooltip>
31+
<span class="text-xs py-0.5 px-1 rounded bg-white/70 dark:bg-neutral-800/70 shadow font-mono tracking-wider text-gray-700 dark:text-gray-200 bg-opacity-40 backdrop-blur-2xl">
32+
{{ props.correctionTime > 0 ? '+' : '' }}{{ props.correctionTime.toFixed(1) }}s
33+
</span>
34+
<n-tooltip placement="right">
35+
<template #trigger>
36+
<div
37+
class="lyric-correction-btn"
38+
@click="emit('adjust', 0.2)"
39+
:title="t('player.addCorrection', { num: 0.2 })"
40+
>
41+
<i class="ri-add-line text-base"></i>
42+
</div>
43+
</template>
44+
<span>{{ t('player.addCorrection', { num: 0.2 }) }}</span>
45+
</n-tooltip>
46+
</div>
47+
</template>
48+
49+
<style scoped>
50+
.lyric-correction-btn {
51+
@apply w-7 h-7 flex items-center justify-center rounded-lg bg-white dark:bg-neutral-800 border border-white/20 dark:border-neutral-700/40 shadow-md backdrop-blur-2xl cursor-pointer transition-all duration-150 text-gray-700 dark:text-gray-200 hover:bg-green-500/80 hover:text-white hover:border-green-400/60 active:scale-95 bg-opacity-40 dark:hover:bg-green-500/80 dark:hover:text-white dark:hover:border-green-400/60 dark:hover:bg-opacity-40;
52+
}
53+
</style>

src/renderer/hooks/MusicHook.ts

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const lrcTimeArray = ref<number[]>([]); // 歌词时间数组
1919
export const nowTime = ref(0); // 当前播放时间
2020
export const allTime = ref(0); // 总播放时间
2121
export const nowIndex = ref(0); // 当前播放歌词
22-
export const correctionTime = ref(0.4); // 歌词矫正时间Correction time
2322
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
2423
export const playMusic = computed(() => playerStore.playMusic as SongResult); // 当前播放歌曲
2524
export const sound = ref<Howl | null>(audioService.getCurrentSound());
@@ -482,25 +481,64 @@ export const pause = () => {
482481
}
483482
};
484483

485-
// 增加矫正时间
486-
export const addCorrectionTime = (time: number) => (correctionTime.value += time);
484+
// 歌词矫正时间映射(每首歌独立)
485+
const CORRECTION_KEY = 'lyric-correction-map';
486+
const correctionTimeMap = ref<Record<string, number>>({});
487487

488-
// 减少矫正时间
489-
export const reduceCorrectionTime = (time: number) => (correctionTime.value -= time);
488+
// 初始化 correctionTimeMap
489+
const loadCorrectionMap = () => {
490+
try {
491+
const raw = localStorage.getItem(CORRECTION_KEY);
492+
correctionTimeMap.value = raw ? JSON.parse(raw) : {};
493+
} catch {
494+
correctionTimeMap.value = {};
495+
}
496+
};
497+
const saveCorrectionMap = () => {
498+
localStorage.setItem(CORRECTION_KEY, JSON.stringify(correctionTimeMap.value));
499+
};
500+
501+
loadCorrectionMap();
502+
503+
// 歌词矫正时间,当前歌曲
504+
export const correctionTime = ref(0);
505+
506+
// 切歌时自动读取矫正时间
507+
watch(
508+
() => playMusic.value?.id,
509+
(id) => {
510+
if (!id) return;
511+
correctionTime.value = correctionTimeMap.value[id] ?? 0;
512+
},
513+
{ immediate: true }
514+
);
515+
516+
/**
517+
* 调整歌词矫正时间(每首歌独立)
518+
* @param delta 增加/减少的秒数(正为加,负为减)
519+
*/
520+
export const adjustCorrectionTime = (delta: number) => {
521+
const id = playMusic.value?.id;
522+
if (!id) return;
523+
const newVal = Math.max(-10, Math.min(10, (correctionTime.value ?? 0) + delta));
524+
correctionTime.value = newVal;
525+
correctionTimeMap.value[id] = newVal;
526+
saveCorrectionMap();
527+
};
490528

491529
// 获取当前播放歌词
492530
export const isCurrentLrc = (index: number, time: number): boolean => {
493531
const currentTime = lrcTimeArray.value[index];
494532
const nextTime = lrcTimeArray.value[index + 1];
495-
const nowTime = time + correctionTime.value;
496-
const isTrue = nowTime > currentTime && nowTime < nextTime;
497-
return isTrue;
533+
const correctedTime = time + correctionTime.value;
534+
return correctedTime > currentTime && correctedTime < nextTime;
498535
};
499536

500537
// 获取当前播放歌词INDEX
501538
export const getLrcIndex = (time: number): number => {
539+
const correctedTime = time + correctionTime.value;
502540
for (let i = 0; i < lrcTimeArray.value.length; i++) {
503-
if (isCurrentLrc(i, time)) {
541+
if (isCurrentLrc(i, correctedTime - correctionTime.value)) {
504542
nowIndex.value = i;
505543
return i;
506544
}
@@ -517,15 +555,22 @@ const currentLrcTiming = computed(() => {
517555

518556
// 获取歌词样式
519557
export const getLrcStyle = (index: number) => {
520-
if (index === nowIndex.value) {
558+
const currentTime = nowTime.value + correctionTime.value;
559+
const start = lrcTimeArray.value[index];
560+
const end = lrcTimeArray.value[index + 1] ?? (start + 1);
561+
562+
if (currentTime >= start && currentTime < end) {
563+
// 当前句,显示进度
564+
const progress = ((currentTime - start) / (end - start)) * 100;
521565
return {
522-
backgroundImage: `linear-gradient(to right, #ffffff ${currentLrcProgress.value}%, #ffffff8a ${currentLrcProgress.value}%)`,
566+
backgroundImage: `linear-gradient(to right, #ffffff ${progress}%, #ffffff8a ${progress}%)`,
523567
backgroundClip: 'text',
524568
WebkitBackgroundClip: 'text',
525569
color: 'transparent',
526570
transition: 'background-image 0.1s linear'
527571
};
528572
}
573+
// 其它句
529574
return {};
530575
};
531576

src/renderer/layout/components/MusicFull.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,13 @@
135135
<span>{{ t('player.lrc.noLrc') }}</span>
136136
</div>
137137
</div>
138+
<!-- 歌词右下角矫正按钮组件 -->
139+
<LyricCorrectionControl
140+
v-if="!isMobile"
141+
:correction-time="correctionTime"
142+
@adjust="adjustCorrectionTime"
143+
/>
138144
</n-layout>
139-
<!-- 时间矫正 -->
140-
<!-- <div class="music-content-time">
141-
<n-button @click="reduceCorrectionTime(0.2)">-</n-button>
142-
<n-button @click="addCorrectionTime(0.2)">+</n-button>
143-
</div> -->
144145
</div>
145146
</div>
146147
</n-drawer>
@@ -153,14 +154,17 @@ import { useI18n } from 'vue-i18n';
153154
154155
import LyricSettings from '@/components/lyric/LyricSettings.vue';
155156
import MiniPlayBar from '@/components/player/MiniPlayBar.vue';
157+
import LyricCorrectionControl from '@/components/lyric/LyricCorrectionControl.vue';
156158
import {
157159
artistList,
158160
lrcArray,
159161
nowIndex,
160162
playMusic,
161163
setAudioTime,
162164
textColors,
163-
useLyricProgress
165+
useLyricProgress,
166+
correctionTime,
167+
adjustCorrectionTime
164168
} from '@/hooks/MusicHook';
165169
import { useArtist } from '@/hooks/useArtist';
166170
import { usePlayerStore } from '@/store/modules/player';
@@ -797,4 +801,12 @@ defineExpose({
797801
color: white;
798802
animation: spin 1s linear infinite;
799803
}
804+
805+
.lyric-correction-btns-mac {
806+
/* 仅在 hover 歌词区域时显示 */
807+
.music-lrc:hover & {
808+
opacity: 1 !important;
809+
pointer-events: auto !important;
810+
}
811+
}
800812
</style>

0 commit comments

Comments
 (0)