mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-14 14:50:50 +08:00
feat: 添加歌词矫正功能,支持增加和减少矫正时间
This commit is contained in:
@@ -11,6 +11,8 @@ export default {
|
|||||||
mute: 'Mute',
|
mute: 'Mute',
|
||||||
unmute: 'Unmute',
|
unmute: 'Unmute',
|
||||||
songNum: 'Song Number: {num}',
|
songNum: 'Song Number: {num}',
|
||||||
|
addCorrection: 'Add {num} seconds',
|
||||||
|
subtractCorrection: 'Subtract {num} seconds',
|
||||||
playFailed: 'Play Failed, Play Next Song',
|
playFailed: 'Play Failed, Play Next Song',
|
||||||
playMode: {
|
playMode: {
|
||||||
sequence: 'Sequence',
|
sequence: 'Sequence',
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export default {
|
|||||||
mute: '静音',
|
mute: '静音',
|
||||||
unmute: '取消静音',
|
unmute: '取消静音',
|
||||||
songNum: '歌曲总数:{num}',
|
songNum: '歌曲总数:{num}',
|
||||||
|
addCorrection: '提前 {num} 秒',
|
||||||
|
subtractCorrection: '延迟 {num} 秒',
|
||||||
playFailed: '当前歌曲播放失败,播放下一首',
|
playFailed: '当前歌曲播放失败,播放下一首',
|
||||||
playMode: {
|
playMode: {
|
||||||
sequence: '顺序播放',
|
sequence: '顺序播放',
|
||||||
|
|||||||
53
src/renderer/components/lyric/LyricCorrectionControl.vue
Normal file
53
src/renderer/components/lyric/LyricCorrectionControl.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
correctionTime: number
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'adjust', delta: number): void
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<n-tooltip placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<div
|
||||||
|
class="lyric-correction-btn"
|
||||||
|
@click="emit('adjust', -0.2)"
|
||||||
|
:title="t('player.subtractCorrection', { num: 0.2 })"
|
||||||
|
>
|
||||||
|
<i class="ri-subtract-line text-base"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span>{{ t('player.subtractCorrection', { num: 0.2 }) }}</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<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">
|
||||||
|
{{ props.correctionTime > 0 ? '+' : '' }}{{ props.correctionTime.toFixed(1) }}s
|
||||||
|
</span>
|
||||||
|
<n-tooltip placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<div
|
||||||
|
class="lyric-correction-btn"
|
||||||
|
@click="emit('adjust', 0.2)"
|
||||||
|
:title="t('player.addCorrection', { num: 0.2 })"
|
||||||
|
>
|
||||||
|
<i class="ri-add-line text-base"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span>{{ t('player.addCorrection', { num: 0.2 }) }}</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.lyric-correction-btn {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,7 +19,6 @@ export const lrcTimeArray = ref<number[]>([]); // 歌词时间数组
|
|||||||
export const nowTime = ref(0); // 当前播放时间
|
export const nowTime = ref(0); // 当前播放时间
|
||||||
export const allTime = ref(0); // 总播放时间
|
export const allTime = ref(0); // 总播放时间
|
||||||
export const nowIndex = ref(0); // 当前播放歌词
|
export const nowIndex = ref(0); // 当前播放歌词
|
||||||
export const correctionTime = ref(0.4); // 歌词矫正时间Correction time
|
|
||||||
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
|
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
|
||||||
export const playMusic = computed(() => playerStore.playMusic as SongResult); // 当前播放歌曲
|
export const playMusic = computed(() => playerStore.playMusic as SongResult); // 当前播放歌曲
|
||||||
export const sound = ref<Howl | null>(audioService.getCurrentSound());
|
export const sound = ref<Howl | null>(audioService.getCurrentSound());
|
||||||
@@ -482,25 +481,64 @@ export const pause = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 增加矫正时间
|
// 歌词矫正时间映射(每首歌独立)
|
||||||
export const addCorrectionTime = (time: number) => (correctionTime.value += time);
|
const CORRECTION_KEY = 'lyric-correction-map';
|
||||||
|
const correctionTimeMap = ref<Record<string, number>>({});
|
||||||
|
|
||||||
// 减少矫正时间
|
// 初始化 correctionTimeMap
|
||||||
export const reduceCorrectionTime = (time: number) => (correctionTime.value -= time);
|
const loadCorrectionMap = () => {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CORRECTION_KEY);
|
||||||
|
correctionTimeMap.value = raw ? JSON.parse(raw) : {};
|
||||||
|
} catch {
|
||||||
|
correctionTimeMap.value = {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const saveCorrectionMap = () => {
|
||||||
|
localStorage.setItem(CORRECTION_KEY, JSON.stringify(correctionTimeMap.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
loadCorrectionMap();
|
||||||
|
|
||||||
|
// 歌词矫正时间,当前歌曲
|
||||||
|
export const correctionTime = ref(0);
|
||||||
|
|
||||||
|
// 切歌时自动读取矫正时间
|
||||||
|
watch(
|
||||||
|
() => playMusic.value?.id,
|
||||||
|
(id) => {
|
||||||
|
if (!id) return;
|
||||||
|
correctionTime.value = correctionTimeMap.value[id] ?? 0;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整歌词矫正时间(每首歌独立)
|
||||||
|
* @param delta 增加/减少的秒数(正为加,负为减)
|
||||||
|
*/
|
||||||
|
export const adjustCorrectionTime = (delta: number) => {
|
||||||
|
const id = playMusic.value?.id;
|
||||||
|
if (!id) return;
|
||||||
|
const newVal = Math.max(-10, Math.min(10, (correctionTime.value ?? 0) + delta));
|
||||||
|
correctionTime.value = newVal;
|
||||||
|
correctionTimeMap.value[id] = newVal;
|
||||||
|
saveCorrectionMap();
|
||||||
|
};
|
||||||
|
|
||||||
// 获取当前播放歌词
|
// 获取当前播放歌词
|
||||||
export const isCurrentLrc = (index: number, time: number): boolean => {
|
export const isCurrentLrc = (index: number, time: number): boolean => {
|
||||||
const currentTime = lrcTimeArray.value[index];
|
const currentTime = lrcTimeArray.value[index];
|
||||||
const nextTime = lrcTimeArray.value[index + 1];
|
const nextTime = lrcTimeArray.value[index + 1];
|
||||||
const nowTime = time + correctionTime.value;
|
const correctedTime = time + correctionTime.value;
|
||||||
const isTrue = nowTime > currentTime && nowTime < nextTime;
|
return correctedTime > currentTime && correctedTime < nextTime;
|
||||||
return isTrue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前播放歌词INDEX
|
// 获取当前播放歌词INDEX
|
||||||
export const getLrcIndex = (time: number): number => {
|
export const getLrcIndex = (time: number): number => {
|
||||||
|
const correctedTime = time + correctionTime.value;
|
||||||
for (let i = 0; i < lrcTimeArray.value.length; i++) {
|
for (let i = 0; i < lrcTimeArray.value.length; i++) {
|
||||||
if (isCurrentLrc(i, time)) {
|
if (isCurrentLrc(i, correctedTime - correctionTime.value)) {
|
||||||
nowIndex.value = i;
|
nowIndex.value = i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -517,15 +555,22 @@ const currentLrcTiming = computed(() => {
|
|||||||
|
|
||||||
// 获取歌词样式
|
// 获取歌词样式
|
||||||
export const getLrcStyle = (index: number) => {
|
export const getLrcStyle = (index: number) => {
|
||||||
if (index === nowIndex.value) {
|
const currentTime = nowTime.value + correctionTime.value;
|
||||||
|
const start = lrcTimeArray.value[index];
|
||||||
|
const end = lrcTimeArray.value[index + 1] ?? (start + 1);
|
||||||
|
|
||||||
|
if (currentTime >= start && currentTime < end) {
|
||||||
|
// 当前句,显示进度
|
||||||
|
const progress = ((currentTime - start) / (end - start)) * 100;
|
||||||
return {
|
return {
|
||||||
backgroundImage: `linear-gradient(to right, #ffffff ${currentLrcProgress.value}%, #ffffff8a ${currentLrcProgress.value}%)`,
|
backgroundImage: `linear-gradient(to right, #ffffff ${progress}%, #ffffff8a ${progress}%)`,
|
||||||
backgroundClip: 'text',
|
backgroundClip: 'text',
|
||||||
WebkitBackgroundClip: 'text',
|
WebkitBackgroundClip: 'text',
|
||||||
color: 'transparent',
|
color: 'transparent',
|
||||||
transition: 'background-image 0.1s linear'
|
transition: 'background-image 0.1s linear'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// 其它句
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -135,12 +135,13 @@
|
|||||||
<span>{{ t('player.lrc.noLrc') }}</span>
|
<span>{{ t('player.lrc.noLrc') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 歌词右下角矫正按钮组件 -->
|
||||||
|
<LyricCorrectionControl
|
||||||
|
v-if="!isMobile"
|
||||||
|
:correction-time="correctionTime"
|
||||||
|
@adjust="adjustCorrectionTime"
|
||||||
|
/>
|
||||||
</n-layout>
|
</n-layout>
|
||||||
<!-- 时间矫正 -->
|
|
||||||
<!-- <div class="music-content-time">
|
|
||||||
<n-button @click="reduceCorrectionTime(0.2)">-</n-button>
|
|
||||||
<n-button @click="addCorrectionTime(0.2)">+</n-button>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-drawer>
|
</n-drawer>
|
||||||
@@ -153,6 +154,7 @@ import { useI18n } from 'vue-i18n';
|
|||||||
|
|
||||||
import LyricSettings from '@/components/lyric/LyricSettings.vue';
|
import LyricSettings from '@/components/lyric/LyricSettings.vue';
|
||||||
import MiniPlayBar from '@/components/player/MiniPlayBar.vue';
|
import MiniPlayBar from '@/components/player/MiniPlayBar.vue';
|
||||||
|
import LyricCorrectionControl from '@/components/lyric/LyricCorrectionControl.vue';
|
||||||
import {
|
import {
|
||||||
artistList,
|
artistList,
|
||||||
lrcArray,
|
lrcArray,
|
||||||
@@ -160,7 +162,9 @@ import {
|
|||||||
playMusic,
|
playMusic,
|
||||||
setAudioTime,
|
setAudioTime,
|
||||||
textColors,
|
textColors,
|
||||||
useLyricProgress
|
useLyricProgress,
|
||||||
|
correctionTime,
|
||||||
|
adjustCorrectionTime
|
||||||
} from '@/hooks/MusicHook';
|
} from '@/hooks/MusicHook';
|
||||||
import { useArtist } from '@/hooks/useArtist';
|
import { useArtist } from '@/hooks/useArtist';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
import { usePlayerStore } from '@/store/modules/player';
|
||||||
@@ -797,4 +801,12 @@ defineExpose({
|
|||||||
color: white;
|
color: white;
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lyric-correction-btns-mac {
|
||||||
|
/* 仅在 hover 歌词区域时显示 */
|
||||||
|
.music-lrc:hover & {
|
||||||
|
opacity: 1 !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user