From 1797c8f9c5175c84ca51574aa4dedbe6408777ef Mon Sep 17 00:00:00 2001 From: imsyy Date: Thu, 21 May 2026 01:00:17 +0800 Subject: [PATCH 01/14] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E6=AD=8C=E8=AF=8D=E6=B8=B2=E6=9F=93=E7=9A=84?= =?UTF-8?q?=20GPU=20=E5=8D=A0=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/player/FullPlayer/PlayerBackground.vue | 2 +- src/components/player/Lyrics/engine/index.ts | 9 ++++++++- src/components/player/Lyrics/index.vue | 2 -- src/components/player/Lyrics/renderer.css | 7 ------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/player/FullPlayer/PlayerBackground.vue b/src/components/player/FullPlayer/PlayerBackground.vue index 545cf77..4f9db7e 100644 --- a/src/components/player/FullPlayer/PlayerBackground.vue +++ b/src/components/player/FullPlayer/PlayerBackground.vue @@ -112,7 +112,6 @@ onBeforeUnmount(() => { position: absolute; inset: 0; background-color: rgba(0, 0, 0, 0.5); - backdrop-filter: blur(100px) saturate(1.2); } /* 模糊模式 */ @@ -128,6 +127,7 @@ onBeforeUnmount(() => { height: 100%; object-fit: cover; transform: scale(1.5); + filter: blur(45px) saturate(1.2); opacity: 0; transition: opacity 0.5s ease-in-out; } diff --git a/src/components/player/Lyrics/engine/index.ts b/src/components/player/Lyrics/engine/index.ts index c69b584..cfbfabb 100644 --- a/src/components/player/Lyrics/engine/index.ts +++ b/src/components/player/Lyrics/engine/index.ts @@ -903,7 +903,10 @@ export class LyricRenderer { const blurKey = blurCurrent.toFixed(2); if (this.cachedBlurKeys[i] !== blurKey) { this.cachedBlurKeys[i] = blurKey; - this.lineElements[i].style.setProperty("--blur", blurKey); + // 仅在确有模糊时挂 filter,归零时移除,避免非模糊行常驻 filter 合成层 + const lineStyle = this.lineElements[i].style; + if (blurCurrent > 0.01) lineStyle.filter = `blur(${(blurCurrent * 1.5).toFixed(2)}px)`; + else lineStyle.removeProperty("filter"); } } } @@ -1014,6 +1017,10 @@ export class LyricRenderer { // 所有动画完成后触发清理 anim.onfinish = tryCleanup; } + // 已结束或暂停的动画不会再触发 onfinish,主动计入,避免该行动画永不被清理 + for (const anim of anims) { + if (anim.playState === "finished" || anim.playState === "paused") tryCleanup(); + } }; /** 清理所有非激活行的残留动画,释放合成资源 */ diff --git a/src/components/player/Lyrics/index.vue b/src/components/player/Lyrics/index.vue index 09d8458..b38107f 100644 --- a/src/components/player/Lyrics/index.vue +++ b/src/components/player/Lyrics/index.vue @@ -157,7 +157,6 @@ const handleLineClick = (timeMs: number) => { }; onMounted(() => { - console.log("[LyricPlayer] onMounted"); if (!containerRef.value) return; const { lyricLines: _lyricLines, ...config } = props; renderer = new LyricRenderer(containerRef.value, { @@ -171,7 +170,6 @@ onMounted(() => { }); onUnmounted(() => { - console.log("[LyricPlayer] onUnmounted"); renderer?.dispose(); renderer = null; }); diff --git a/src/components/player/Lyrics/renderer.css b/src/components/player/Lyrics/renderer.css index 1c624a7..98d8097 100644 --- a/src/components/player/Lyrics/renderer.css +++ b/src/components/player/Lyrics/renderer.css @@ -33,14 +33,10 @@ transform-origin: left; opacity: var(--lp-inactive-opacity, 0.3); contain: content; - will-change: transform, filter; - backface-visibility: hidden; transition: opacity 0.3s ease; pointer-events: none; --ba: 0.2; --da: 0.2; - --blur: 0; - filter: blur(calc(var(--blur) * 1.5px)); &.active { opacity: var(--lp-active-opacity, 1); @@ -117,7 +113,6 @@ display: inline-block; white-space: pre-wrap; vertical-align: bottom; - backface-visibility: hidden; } /* 强调单词包裹层(逐字符拆分的容器) */ @@ -125,14 +120,12 @@ display: inline-block; white-space: pre; vertical-align: bottom; - backface-visibility: hidden; /* 为缩放/辉光溢出预留空间,负 margin 抵消布局影响 */ padding: 1em; margin: -1em; & > span { display: inline-block; - backface-visibility: hidden; padding: 1em; margin: -1em; } From d83934d253ec9fd202b56b346d0e974069c1d9ec Mon Sep 17 00:00:00 2001 From: imsyy Date: Thu, 21 May 2026 11:31:53 +0800 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E9=83=A8?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E9=9D=A2=E7=9A=84=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/services/thumbar.ts | 5 +++ electron/main/window/main.ts | 2 +- index.html | 4 +-- shared/defaults/hotkeys.ts | 6 ++++ shared/types/hotkey.ts | 3 +- src/components/layout/NavSearch.vue | 8 ++++- src/core/hotkey/registry.ts | 4 +++ src/core/player/index.ts | 2 +- src/i18n/locales/en-US.json | 48 +++++------------------------ src/i18n/locales/zh-CN.json | 48 +++++------------------------ src/pages/Cloud.vue | 10 +----- src/pages/Liked.vue | 10 +----- src/stores/history.ts | 21 ++++++++----- src/stores/status.ts | 3 ++ 14 files changed, 63 insertions(+), 111 deletions(-) diff --git a/electron/main/services/thumbar.ts b/electron/main/services/thumbar.ts index bb7028d..7304e79 100644 --- a/electron/main/services/thumbar.ts +++ b/electron/main/services/thumbar.ts @@ -23,6 +23,7 @@ class ThumbarImpl implements Thumbar { private pause: ThumbarButton; private isPlaying: boolean = false; private onThemeUpdated: () => void; + private onWindowShown: () => void; constructor(win: BrowserWindow) { this.win = win; @@ -57,9 +58,13 @@ class ThumbarImpl implements Thumbar { this.updateThumbar(this.isPlaying); }; nativeTheme.on("updated", this.onThemeUpdated); + // 窗口从托盘恢复显示后系统会清空任务栏按钮,需重新下发一次 + this.onWindowShown = () => this.updateThumbar(this.isPlaying); + win.on("show", this.onWindowShown); // 窗口销毁时移除监听 win.on("closed", () => { nativeTheme.removeListener("updated", this.onThemeUpdated); + win.removeListener("show", this.onWindowShown); }); } diff --git a/electron/main/window/main.ts b/electron/main/window/main.ts index 3191dd6..7f8debf 100644 --- a/electron/main/window/main.ts +++ b/electron/main/window/main.ts @@ -43,7 +43,7 @@ export const createMainWindow = (): BrowserWindow => { initTray(); // 缩略图工具栏 - mainWindow.once("ready-to-show", () => { + mainWindow.webContents.once("did-finish-load", () => { initThumbar(mainWindow!); }); diff --git a/index.html b/index.html index 8f1125b..3cb7d4e 100644 --- a/index.html +++ b/index.html @@ -104,8 +104,8 @@ text-align: center; font-size: 12px; font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", - "Microsoft YaHei", sans-serif; + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", + sans-serif; animation: splash-rise-footer 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.36s both; } @keyframes splash-rise { diff --git a/shared/defaults/hotkeys.ts b/shared/defaults/hotkeys.ts index e454056..a8082a0 100644 --- a/shared/defaults/hotkeys.ts +++ b/shared/defaults/hotkeys.ts @@ -127,6 +127,12 @@ export const HOTKEY_ACTIONS: HotkeyActionMeta[] = [ defaultBinding: { inApp: "CommandOrControl+P", global: null }, allowGlobal: false, }, + { + id: "view.openSearch", + labelKey: "settings.hotkeys.actions.openSearch", + defaultBinding: { inApp: "CommandOrControl+F", global: null }, + allowGlobal: false, + }, ]; /** 默认绑定表(HotkeyBindingsMap) */ diff --git a/shared/types/hotkey.ts b/shared/types/hotkey.ts index 6aaade6..14aa70e 100644 --- a/shared/types/hotkey.ts +++ b/shared/types/hotkey.ts @@ -18,7 +18,8 @@ export type HotkeyActionId = | "window.toggleTaskbarLyric" | "view.openPlayer" | "view.closePlayer" - | "view.togglePlaylist"; + | "view.togglePlaylist" + | "view.openSearch"; /** 单个动作的两条作用域绑定 */ export interface HotkeyBinding { diff --git a/src/components/layout/NavSearch.vue b/src/components/layout/NavSearch.vue index 576e4b5..705e0cd 100644 --- a/src/components/layout/NavSearch.vue +++ b/src/components/layout/NavSearch.vue @@ -1,5 +1,6 @@ + + diff --git a/src/components/list/CoverList.vue b/src/components/list/CoverList.vue index afc5f5c..14b9661 100644 --- a/src/components/list/CoverList.vue +++ b/src/components/list/CoverList.vue @@ -1,13 +1,14 @@ + +
+ +
diff --git a/src/components/ui/SCard.vue b/src/components/ui/SCard.vue index ccd9352..bc84435 100644 --- a/src/components/ui/SCard.vue +++ b/src/components/ui/SCard.vue @@ -6,6 +6,8 @@ export interface SCardProps { bordered?: boolean; /** 内边距尺寸 */ size?: "small" | "medium" | "large"; + /** 圆角尺寸 */ + radius?: "md" | "lg" | "xl"; /** 鼠标悬浮高亮 */ hoverable?: boolean; } @@ -13,6 +15,7 @@ export interface SCardProps { withDefaults(defineProps(), { bordered: true, size: "medium", + radius: "lg", }); const sizePadding: Record, string> = { @@ -21,6 +24,12 @@ const sizePadding: Record, string> = { large: "px-5 py-4", }; +const radiusClass: Record, string> = { + md: "rounded-md", + lg: "rounded-lg", + xl: "rounded-xl", +}; + const slots = useSlots(); const structured = computed(() => !!slots.header || !!slots["header-extra"] || !!slots.footer); @@ -28,10 +37,11 @@ const structured = computed(() => !!slots.header || !!slots["header-extra"] || !