From cc3223c0c1698c38c2482c4134c0bd0380a098c7 Mon Sep 17 00:00:00 2001 From: kazukokawagawa <2580099704@qq.com> Date: Mon, 9 Feb 2026 19:35:41 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(=E6=AD=8C=E8=AF=8D):=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=87=AA=E9=80=82=E5=BA=94=E6=AD=8C=E8=AF=8D=E5=AD=97?= =?UTF-8?q?=E4=BD=93=E5=A4=A7=E5=B0=8F=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加歌词字体大小自适应模式,可根据窗口高度自动缩放歌词大小,避免全屏时过小或窗口时过大。新增设置选项允许用户在固定大小和自适应模式之间切换,并更新了歌词预览和播放器组件以支持新的字体大小计算逻辑。 --- src/components/Player/PlayerLyric/AMLyric.vue | 9 +++++- .../Player/PlayerLyric/DefaultLyric.vue | 13 +++++++-- .../Setting/components/LyricPreview.vue | 19 +++++++++---- src/components/Setting/config/lyric.ts | 28 +++++++++++++++++-- src/stores/setting.ts | 3 ++ 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/components/Player/PlayerLyric/AMLyric.vue b/src/components/Player/PlayerLyric/AMLyric.vue index c3cc64047..603cd5f93 100644 --- a/src/components/Player/PlayerLyric/AMLyric.vue +++ b/src/components/Player/PlayerLyric/AMLyric.vue @@ -39,7 +39,7 @@ :wordFadeWidth="settingStore.wordFadeWidth" :style="{ '--display-count-down-show': settingStore.countDownShow ? 'flex' : 'none', - '--amll-lp-font-size': settingStore.lyricFontSize + 'px', + '--amll-lp-font-size': getFontSize(settingStore.lyricFontSize), 'font-weight': settingStore.lyricFontWeight, 'font-family': settingStore.LyricFont !== 'follow' ? settingStore.LyricFont : '', ...lyricLangFontStyle(settingStore), @@ -71,6 +71,13 @@ const statusStore = useStatusStore(); const settingStore = useSettingStore(); const player = usePlayerController(); +const getFontSize = (size: number) => { + if (settingStore.lyricFontSizeMode === "adaptive") { + return `calc(${size} / 1080 * 100vh)`; + } + return `${size}px`; +}; + const lyricPlayerRef = ref(null); // 当前歌词 diff --git a/src/components/Player/PlayerLyric/DefaultLyric.vue b/src/components/Player/PlayerLyric/DefaultLyric.vue index 1aa4dd241..ae6773ba0 100644 --- a/src/components/Player/PlayerLyric/DefaultLyric.vue +++ b/src/components/Player/PlayerLyric/DefaultLyric.vue @@ -2,9 +2,9 @@
(null); // 是否为逐字歌词模式 diff --git a/src/components/Setting/components/LyricPreview.vue b/src/components/Setting/components/LyricPreview.vue index f91ed5bc2..5252c3828 100644 --- a/src/components/Setting/components/LyricPreview.vue +++ b/src/components/Setting/components/LyricPreview.vue @@ -5,9 +5,9 @@ 'flex-direction': 'column', 'align-items': settingStore.lyricsPosition, '--font-weight': settingStore.lyricFontWeight, - '--font-size': settingStore.lyricFontSize, - '--font-tran-size': tranFontSize, - '--font-roma-size': romaFontSize, + '--font-size': getFontSize(settingStore.lyricFontSize), + '--font-tran-size': getFontSize(tranFontSize), + '--font-roma-size': getFontSize(romaFontSize), '--transform-origin': settingStore.lyricsPosition === 'center' ? 'center' @@ -40,6 +40,13 @@ import { useSettingStore } from "@/stores"; const settingStore = useSettingStore(); +const getFontSize = (size: number) => { + if (settingStore.lyricFontSizeMode === "adaptive") { + return `calc(${size} / 1080 * 100vh)`; + } + return `${size}px`; +}; + const fontSizeComputed = (key: string) => computed({ get: () => @@ -72,15 +79,15 @@ const romaFontSize = fontSizeComputed("lyricRomaFontSize"); &:nth-of-type(1) { font-weight: var(--font-weight); - font-size: calc(var(--font-size) * 1px); + font-size: var(--font-size); } &:nth-of-type(2) { opacity: 0.6; - font-size: calc(var(--font-tran-size) * 1px); + font-size: var(--font-tran-size); } &:nth-of-type(3) { opacity: 0.6; - font-size: calc(var(--font-roma-size) * 1px); + font-size: var(--font-roma-size); } } } diff --git a/src/components/Setting/config/lyric.ts b/src/components/Setting/config/lyric.ts index e33fc1dd3..97d8b2548 100644 --- a/src/components/Setting/config/lyric.ts +++ b/src/components/Setting/config/lyric.ts @@ -93,11 +93,25 @@ export const useLyricSettings = (): SettingConfig => { noWrapper: true, component: markRaw(LyricPreview), }, + { + key: "lyricFontSizeMode", + label: "自适应歌词大小", + type: "switch", + description: "开启后歌词大小将根据窗口高度自动缩放,避免全屏时过小或窗口时过大", + value: computed({ + get: () => settingStore.lyricFontSizeMode === "adaptive", + set: (v) => (settingStore.lyricFontSizeMode = v ? "adaptive" : "fixed"), + }), + }, { key: "lyricFontSize", label: "歌词字体大小", type: "input-number", - description: "单位 px,最小 12,最大 60", + description: computed(() => + settingStore.lyricFontSizeMode === "adaptive" + ? "作为基准大小 (以 1080p 高度为准)" + : "单位 px,最小 12,最大 60" + ), min: 12, max: 60, suffix: "px", @@ -111,7 +125,11 @@ export const useLyricSettings = (): SettingConfig => { key: "lyricTranFontSize", label: "翻译歌词大小", type: "input-number", - description: "单位 px,最小 5,最大 40", + description: computed(() => + settingStore.lyricFontSizeMode === "adaptive" + ? "作为基准大小 (以 1080p 高度为准)" + : "单位 px,最小 5,最大 40" + ), min: 5, max: 40, suffix: "px", @@ -130,7 +148,11 @@ export const useLyricSettings = (): SettingConfig => { key: "lyricRomaFontSize", label: "音译歌词大小", type: "input-number", - description: "单位 px,最小 5,最大 40", + description: computed(() => + settingStore.lyricFontSizeMode === "adaptive" + ? "作为基准大小 (以 1080p 高度为准)" + : "单位 px,最小 5,最大 40" + ), min: 5, max: 40, suffix: "px", diff --git a/src/stores/setting.ts b/src/stores/setting.ts index d35ba0777..3d73edfd9 100644 --- a/src/stores/setting.ts +++ b/src/stores/setting.ts @@ -70,6 +70,8 @@ export interface SettingState { updateChannel: "stable" | "nightly"; /** 隐藏 VIP 标签 */ hideVipTag: boolean; + /** 歌词字体大小模式 */ + lyricFontSizeMode: "fixed" | "adaptive"; /** 歌词字体大小 */ lyricFontSize: number; /** 歌词翻译字体大小 */ @@ -549,6 +551,7 @@ export const useSettingStore = defineStore("setting", { playSongDemo: false, scrobbleSong: false, dynamicCover: false, + lyricFontSizeMode: "adaptive", lyricFontSize: 46, lyricTranFontSize: 22, lyricRomaFontSize: 18, From 4573cd963d843d089f70f64dab3c6c8a5c35bdf2 Mon Sep 17 00:00:00 2001 From: kazukokawagawa <2580099704@qq.com> Date: Mon, 9 Feb 2026 20:15:23 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AEQRC=E6=AD=8C=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Setting/config/lyric.ts | 6 +++--- src/utils/lyric/qrc-parser.ts | 27 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/components/Setting/config/lyric.ts b/src/components/Setting/config/lyric.ts index 97d8b2548..b76e2d02e 100644 --- a/src/components/Setting/config/lyric.ts +++ b/src/components/Setting/config/lyric.ts @@ -110,7 +110,7 @@ export const useLyricSettings = (): SettingConfig => { description: computed(() => settingStore.lyricFontSizeMode === "adaptive" ? "作为基准大小 (以 1080p 高度为准)" - : "单位 px,最小 12,最大 60" + : "单位 px,最小 12,最大 60", ), min: 12, max: 60, @@ -128,7 +128,7 @@ export const useLyricSettings = (): SettingConfig => { description: computed(() => settingStore.lyricFontSizeMode === "adaptive" ? "作为基准大小 (以 1080p 高度为准)" - : "单位 px,最小 5,最大 40" + : "单位 px,最小 5,最大 40", ), min: 5, max: 40, @@ -151,7 +151,7 @@ export const useLyricSettings = (): SettingConfig => { description: computed(() => settingStore.lyricFontSizeMode === "adaptive" ? "作为基准大小 (以 1080p 高度为准)" - : "单位 px,最小 5,最大 40" + : "单位 px,最小 5,最大 40", ), min: 5, max: 40, diff --git a/src/utils/lyric/qrc-parser.ts b/src/utils/lyric/qrc-parser.ts index 322070e06..12dbcb57e 100644 --- a/src/utils/lyric/qrc-parser.ts +++ b/src/utils/lyric/qrc-parser.ts @@ -26,12 +26,37 @@ const domParser: QRCParserFn = (xmlStr) => { } }; +const decodeXml = (str: string) => { + return str + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); +}; + // 实现正则策略 const regexParser: QRCParserFn = (xmlStr) => { if (!xmlStr) return ""; + + // 尝试贪婪匹配 (处理 LyricContent="... "..." ..." 这种非标准 XML 引号未转义的情况) + // 匹配从 LyricContent=" 开始,直到标签结束前的最后一个引号 + const greedyMatch = /LyricContent\s*=\s*"([\s\S]*)"\s*\/?>/.exec(xmlStr); + if (greedyMatch) { + const content = greedyMatch[1]; + // 启发式检查:如果提取的内容中包含类似 ` Attribute="` 的结构,说明贪婪匹配吃掉了其他属性 + // 这种情况下回退到非贪婪匹配 + if (!/\s+\w+\s*=\s*"/.test(content)) { + return decodeXml(content); + } + } + + // 标准非贪婪匹配 (兼容标准 XML 属性) const match = /LyricContent\s*=\s*"([^"]*)"/.exec(xmlStr); + // 如果没有匹配到 XML 结构,假设输入本身就是内容 (保持原有逻辑的兼容性) - return match?.[1] || xmlStr; + const result = match?.[1] || xmlStr; + return decodeXml(result); }; // 避免每次调用时的运行时检查 From f976466d5c081d0527af8b5086e6629e38a88d6f Mon Sep 17 00:00:00 2001 From: kazukokawagawa <2580099704@qq.com> Date: Mon, 9 Feb 2026 21:25:09 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor(=E6=AD=8C=E8=AF=8D=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F):=20=E5=B0=86=E5=AD=97=E4=BD=93=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E9=80=BB=E8=BE=91=E6=8F=90=E5=8F=96=E4=B8=BA?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将多个组件中重复的字体大小计算逻辑提取到统一的工具函数 getFontSize 中 该函数根据字体大小模式(自适应或固定)返回相应的 CSS 值 --- src/components/Player/PlayerLyric/AMLyric.vue | 10 ++-------- src/components/Player/PlayerLyric/DefaultLyric.vue | 14 ++++---------- src/components/Setting/components/LyricPreview.vue | 14 ++++---------- src/utils/style.ts | 13 +++++++++++++ 4 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 src/utils/style.ts diff --git a/src/components/Player/PlayerLyric/AMLyric.vue b/src/components/Player/PlayerLyric/AMLyric.vue index 603cd5f93..108fee9a6 100644 --- a/src/components/Player/PlayerLyric/AMLyric.vue +++ b/src/components/Player/PlayerLyric/AMLyric.vue @@ -39,7 +39,7 @@ :wordFadeWidth="settingStore.wordFadeWidth" :style="{ '--display-count-down-show': settingStore.countDownShow ? 'flex' : 'none', - '--amll-lp-font-size': getFontSize(settingStore.lyricFontSize), + '--amll-lp-font-size': getFontSize(settingStore.lyricFontSize, settingStore.lyricFontSizeMode), 'font-weight': settingStore.lyricFontWeight, 'font-family': settingStore.LyricFont !== 'follow' ? settingStore.LyricFont : '', ...lyricLangFontStyle(settingStore), @@ -58,6 +58,7 @@ import { getLyricLanguage } from "@/utils/format"; import { usePlayerController } from "@/core/player/PlayerController"; import { cloneDeep } from "lodash-es"; import { lyricLangFontStyle } from "@/utils/lyric/lyricFontConfig"; +import { getFontSize } from "@/utils/style"; defineProps({ currentTime: { @@ -71,13 +72,6 @@ const statusStore = useStatusStore(); const settingStore = useSettingStore(); const player = usePlayerController(); -const getFontSize = (size: number) => { - if (settingStore.lyricFontSizeMode === "adaptive") { - return `calc(${size} / 1080 * 100vh)`; - } - return `${size}px`; -}; - const lyricPlayerRef = ref(null); // 当前歌词 diff --git a/src/components/Player/PlayerLyric/DefaultLyric.vue b/src/components/Player/PlayerLyric/DefaultLyric.vue index ae6773ba0..0d2e37ceb 100644 --- a/src/components/Player/PlayerLyric/DefaultLyric.vue +++ b/src/components/Player/PlayerLyric/DefaultLyric.vue @@ -2,9 +2,9 @@
{ - if (settingStore.lyricFontSizeMode === "adaptive") { - return `calc(${size} / 1080 * 100vh)`; - } - return `${size}px`; -}; - const lyricScrollContainer = ref(null); // 是否为逐字歌词模式 diff --git a/src/components/Setting/components/LyricPreview.vue b/src/components/Setting/components/LyricPreview.vue index 5252c3828..bae55d47f 100644 --- a/src/components/Setting/components/LyricPreview.vue +++ b/src/components/Setting/components/LyricPreview.vue @@ -5,9 +5,9 @@ 'flex-direction': 'column', 'align-items': settingStore.lyricsPosition, '--font-weight': settingStore.lyricFontWeight, - '--font-size': getFontSize(settingStore.lyricFontSize), - '--font-tran-size': getFontSize(tranFontSize), - '--font-roma-size': getFontSize(romaFontSize), + '--font-size': getFontSize(settingStore.lyricFontSize, settingStore.lyricFontSizeMode), + '--font-tran-size': getFontSize(tranFontSize, settingStore.lyricFontSizeMode), + '--font-roma-size': getFontSize(romaFontSize, settingStore.lyricFontSizeMode), '--transform-origin': settingStore.lyricsPosition === 'center' ? 'center' @@ -37,16 +37,10 @@