diff --git a/src/components/Player/FullPlayerMobile.vue b/src/components/Player/FullPlayerMobile.vue
index 7303bfe56..727291ca9 100644
--- a/src/components/Player/FullPlayerMobile.vue
+++ b/src/components/Player/FullPlayerMobile.vue
@@ -167,7 +167,18 @@ let savedPageType: MobilePageType = "info";
@@ -326,6 +424,19 @@ const adjustLandscapeCoverOffset = (delta: number) => {
);
};
+const adjustGlobalLyricOffset = (delta: number) => {
+ settingStore.globalLyricOffsetValue = clampValue(
+ settingStore.globalLyricOffsetValue + delta,
+ -10000,
+ 10000,
+ );
+};
+
+const applyGlobalLyricOffsetPreset = (value: number) => {
+ const signMultiplier = settingStore.globalLyricOffsetPresetSign === "-" ? -1 : 1;
+ settingStore.globalLyricOffsetValue = value * signMultiplier;
+};
+
const adjustLandscapeLyricPadding = (delta: number) => {
settingStore.landscapeLyricPaddingX = clampValue(
settingStore.landscapeLyricPaddingX + delta,
diff --git a/src/components/Setting/config/lyric.ts b/src/components/Setting/config/lyric.ts
index fb5912c28..f1d85c9de 100644
--- a/src/components/Setting/config/lyric.ts
+++ b/src/components/Setting/config/lyric.ts
@@ -462,6 +462,66 @@ export const useLyricSettings = (): SettingConfig => {
set: (v) => (settingStore.lyricsBlendMode = v),
}),
},
+ {
+ key: "globalLyricOffsetEnabled",
+ label: "全局歌词偏移",
+ type: "switch",
+ description: "是否启用全局歌词时间轴偏移",
+ value: computed({
+ get: () => settingStore.globalLyricOffsetEnabled,
+ set: (v) => (settingStore.globalLyricOffsetEnabled = v),
+ }),
+ children: [
+ {
+ key: "globalLyricOffsetValue",
+ label: "偏移数值",
+ type: "input-number",
+ description: "全局歌词偏移数值 (+ -),单位毫秒",
+ min: -10000,
+ max: 10000,
+ step: 10,
+ suffix: "ms",
+ value: computed({
+ get: () => settingStore.globalLyricOffsetValue,
+ set: (v) => (settingStore.globalLyricOffsetValue = v || 0),
+ }),
+ },
+ {
+ key: "globalLyricOffsetPresetSign",
+ label: "快捷预设单位",
+ type: "select",
+ description: "快捷开关中的预设数值符号 (+ 或 -)",
+ options: [
+ { label: "+", value: "+" },
+ { label: "-", value: "-" },
+ ],
+ value: computed({
+ get: () => settingStore.globalLyricOffsetPresetSign,
+ set: (v) => (settingStore.globalLyricOffsetPresetSign = v),
+ }),
+ },
+ {
+ key: "globalLyricOffsetAlwaysApply",
+ label: "始终启用偏移",
+ type: "switch",
+ description: "开启后,任何歌曲播放时都将默认应用该偏移数值",
+ value: computed({
+ get: () => settingStore.globalLyricOffsetAlwaysApply,
+ set: (v) => (settingStore.globalLyricOffsetAlwaysApply = v),
+ }),
+ },
+ {
+ key: "globalLyricOffsetDoubleClickApply",
+ label: "双击歌词页封面应用偏移",
+ type: "switch",
+ description: "开启后,双击歌词页封面可应用当前全局偏移",
+ value: computed({
+ get: () => settingStore.globalLyricOffsetDoubleClickApply,
+ set: (v) => (settingStore.globalLyricOffsetDoubleClickApply = v),
+ }),
+ },
+ ],
+ },
{
key: "lyricOffsetStep",
label: "歌词时延调节步长",
diff --git a/src/stores/setting.ts b/src/stores/setting.ts
index 0adb3d7d3..df9e623e2 100644
--- a/src/stores/setting.ts
+++ b/src/stores/setting.ts
@@ -425,6 +425,16 @@ export interface SettingState {
time: boolean;
description: boolean;
};
+ /** 全局歌词时间轴偏移开关 */
+ globalLyricOffsetEnabled: boolean;
+ /** 全局歌词时间轴偏移数值 (ms) */
+ globalLyricOffsetValue: number;
+ /** 全局歌词时间轴快捷预设单位符号 */
+ globalLyricOffsetPresetSign: "+" | "-";
+ /** 双击歌词页封面应用全局偏移 */
+ globalLyricOffsetDoubleClickApply: boolean;
+ /** 始终启用全局偏移 */
+ globalLyricOffsetAlwaysApply: boolean;
/** 全屏播放器界面元素显示配置 */
fullscreenPlayerElements: {
like: boolean;
@@ -758,6 +768,11 @@ export const useSettingStore = defineStore("setting", {
time: true,
description: true,
},
+ globalLyricOffsetEnabled: false,
+ globalLyricOffsetValue: 0,
+ globalLyricOffsetPresetSign: "+",
+ globalLyricOffsetDoubleClickApply: false,
+ globalLyricOffsetAlwaysApply: false,
fullscreenPlayerElements: {
like: true,
addToPlaylist: true,
diff --git a/src/stores/status.ts b/src/stores/status.ts
index 815d640ff..420596905 100644
--- a/src/stores/status.ts
+++ b/src/stores/status.ts
@@ -11,6 +11,7 @@ import type {
import type { RepeatModeType, ShuffleModeType } from "@/types/shared/play-mode";
import { isDevBuild } from "@/utils/env";
import { defineStore } from "pinia";
+import { useSettingStore } from "./setting";
interface StatusState {
/** 菜单折叠状态 */
@@ -335,7 +336,14 @@ export const useStatusStore = defineStore("status", {
getSongOffset(songId?: number): number {
if (!songId) return 0;
const offsetTime = this.currentTimeOffsetMap?.[songId] ?? 0;
- return Math.floor(offsetTime * 1000);
+ const baseOffset = Math.floor(offsetTime * 1000);
+
+ // 注意:这里需要动态导入 settingStore 避免循环依赖
+ const settingStore = useSettingStore();
+ if (settingStore.globalLyricOffsetEnabled && settingStore.globalLyricOffsetAlwaysApply) {
+ return baseOffset + settingStore.globalLyricOffsetValue;
+ }
+ return baseOffset;
},
/**
* 设置指定歌曲的偏移
@@ -362,12 +370,13 @@ export const useStatusStore = defineStore("status", {
*/
incSongOffset(songId?: number, delta: number = 500) {
if (!songId) return;
- const current = this.getSongOffset(songId);
- const next = current + delta;
- if (next === 0) {
+ const offsetTime = this.currentTimeOffsetMap?.[songId] ?? 0;
+ const currentLocal = Math.floor(offsetTime * 1000);
+ const nextLocal = currentLocal + delta;
+ if (nextLocal === 0) {
delete this.currentTimeOffsetMap[songId];
} else {
- this.setSongOffset(songId, next);
+ this.setSongOffset(songId, nextLocal);
}
},
/** 重置指定歌曲的偏移为 0 */