From 5ba9e6591a00612304a2dde856636715a6253923 Mon Sep 17 00:00:00 2001 From: alger Date: Mon, 20 Apr 2026 14:48:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(lyric):=20=E6=8A=BD=E5=8F=96=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E8=83=8C=E6=99=AF/=E6=96=87=E5=AD=97=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E9=80=BB=E8=BE=91=E4=B8=BA=20useLyricBackground=20com?= =?UTF-8?q?posable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MusicFull.vue 与 MusicFullMobile.vue 各自持有的 setTextColors / currentBackground / animationFrame / isDark 合并到共享 composable, 消除两份几乎一致的包装逻辑。Mobile 的 --bg-color 差异通过 writeBgColor option 注入,行为等价。 --- src/renderer/components/lyric/MusicFull.vue | 55 +------------- .../components/lyric/MusicFullMobile.vue | 63 +++------------ src/renderer/hooks/useLyricBackground.ts | 76 +++++++++++++++++++ 3 files changed, 89 insertions(+), 105 deletions(-) create mode 100644 src/renderer/hooks/useLyricBackground.ts diff --git a/src/renderer/components/lyric/MusicFull.vue b/src/renderer/components/lyric/MusicFull.vue index 6a72618..9fdd5ec 100644 --- a/src/renderer/components/lyric/MusicFull.vue +++ b/src/renderer/components/lyric/MusicFull.vue @@ -202,19 +202,18 @@ import { useLyricProgress } from '@/hooks/MusicHook'; import { useArtist } from '@/hooks/useArtist'; +import { useLyricBackground } from '@/hooks/useLyricBackground'; import { usePlayerStore } from '@/store/modules/player'; import { useSettingsStore } from '@/store/modules/settings'; import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric'; import { getImgUrl, isMobile } from '@/utils'; -import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor'; +import { getTextColors } from '@/utils/linearColor'; const { t } = useI18n(); // 定义 refs const lrcSider = ref(null); const isMouse = ref(false); -const currentBackground = ref(''); -const animationFrame = ref(null); -const isDark = ref(false); +const { currentBackground, applyBackground } = useLyricBackground(); // 计算自定义背景样式 const customBackgroundStyle = computed(() => { @@ -381,42 +380,6 @@ watch( } ); -const setTextColors = (background: string) => { - if (!background) { - textColors.value = getTextColors(); - document.documentElement.style.setProperty('--hover-bg-color', getHoverBackgroundColor(false)); - document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary); - document.documentElement.style.setProperty('--text-color-active', textColors.value.active); - return; - } - - // 更新文字颜色 - textColors.value = getTextColors(background); - isDark.value = textColors.value.active === '#000000'; - - document.documentElement.style.setProperty( - '--hover-bg-color', - getHoverBackgroundColor(isDark.value) - ); - document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary); - document.documentElement.style.setProperty('--text-color-active', textColors.value.active); - - // 处理背景颜色动画 - if (currentBackground.value) { - if (animationFrame.value) { - cancelAnimationFrame(animationFrame.value); - } - const result = animateGradient(currentBackground.value, background, (gradient) => { - currentBackground.value = gradient; - }); - if (typeof result === 'number') { - animationFrame.value = result; - } - } else { - currentBackground.value = background; - } -}; - const targetBackground = computed(() => { if (config.value.useCustomBackground && customBackgroundStyle.value) { if (typeof customBackgroundStyle.value === 'string') { @@ -434,7 +397,7 @@ watch( targetBackground, (newBg) => { if (newBg) { - setTextColors(newBg); + applyBackground(newBg); } }, { immediate: true } @@ -523,13 +486,6 @@ const getWordStyle = (lineIndex: number, _wordIndex: number, word: any) => { } }; -// 组件卸载时清理动画 -onBeforeUnmount(() => { - if (animationFrame.value) { - cancelAnimationFrame(animationFrame.value); - } -}); - const settingsStore = useSettingsStore(); const { navigateToArtist } = useArtist(); @@ -626,9 +582,6 @@ onMounted(() => { // 移除滚动监听和全屏状态监听 onBeforeUnmount(() => { - if (animationFrame.value) { - cancelAnimationFrame(animationFrame.value); - } if (lrcSider.value?.$el) { lrcSider.value.$el.removeEventListener('scroll', handleScroll); } diff --git a/src/renderer/components/lyric/MusicFullMobile.vue b/src/renderer/components/lyric/MusicFullMobile.vue index 1c0a8c1..de3c57c 100644 --- a/src/renderer/components/lyric/MusicFullMobile.vue +++ b/src/renderer/components/lyric/MusicFullMobile.vue @@ -408,12 +408,13 @@ import { useLyricProgress } from '@/hooks/MusicHook'; import { useArtist } from '@/hooks/useArtist'; +import { useLyricBackground } from '@/hooks/useLyricBackground'; import { usePlayMode } from '@/hooks/usePlayMode'; import { audioService } from '@/services/audioService'; import { usePlayerStore } from '@/store/modules/player'; import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric'; import { getImgUrl, secondToMinute } from '@/utils'; -import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor'; +import { getTextColors } from '@/utils/linearColor'; import { showBottomToast } from '@/utils/shortcutToast'; const { t } = useI18n(); @@ -876,10 +877,10 @@ const handleThumbTouchEnd = (e: TouchEvent) => { isThumbDragging.value = false; }; -// 背景相关 -const currentBackground = ref(''); -const animationFrame = ref(null); -const isDark = ref(false); +// 背景相关(由 composable 管理) +const { isDark, applyBackground } = useLyricBackground({ + writeBgColor: () => playerStore.playMusic.primaryColor || undefined +}); const config = ref({ ...DEFAULT_LYRIC_CONFIG }); // 可见歌词计算 @@ -937,49 +938,6 @@ const isVisible = computed({ set: (value) => emit('update:modelValue', value) }); -// 设置文字颜色 -const setTextColors = (background: string) => { - if (!background) { - textColors.value = getTextColors(); - document.documentElement.style.setProperty('--hover-bg-color', getHoverBackgroundColor(false)); - document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary); - document.documentElement.style.setProperty('--text-color-active', textColors.value.active); - document.documentElement.style.setProperty('--bg-color', 'rgba(25, 25, 25, 1)'); - return; - } - - // 更新文字颜色 - textColors.value = getTextColors(background); - isDark.value = textColors.value.active === '#000000'; - - document.documentElement.style.setProperty( - '--hover-bg-color', - getHoverBackgroundColor(isDark.value) - ); - document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary); - document.documentElement.style.setProperty('--text-color-active', textColors.value.active); - - // 解析背景颜色用于封面融合 - let bgColor = playerStore.playMusic.primaryColor || 'rgba(25, 25, 25, 1)'; - - document.documentElement.style.setProperty('--bg-color', bgColor); - - // 处理背景颜色动画 - if (currentBackground.value) { - if (animationFrame.value) { - cancelAnimationFrame(animationFrame.value); - } - const result = animateGradient(currentBackground.value, background, (gradient) => { - currentBackground.value = gradient; - }); - if (typeof result === 'number') { - animationFrame.value = result; - } - } else { - currentBackground.value = background; - } -}; - const targetBackground = computed(() => { if (config.value.theme !== 'default') { return themeMusic[config.value.theme] || props.background; @@ -992,17 +950,14 @@ watch( targetBackground, (newBg) => { if (newBg) { - setTextColors(newBg); + applyBackground(newBg); } }, { immediate: true } ); -// 组件卸载时清理动画 +// 组件卸载清理 onBeforeUnmount(() => { - if (animationFrame.value) { - cancelAnimationFrame(animationFrame.value); - } if (autoScrollTimer.value) { clearTimeout(autoScrollTimer.value); } @@ -1113,7 +1068,7 @@ watch(isVisible, (newVal) => { if (newVal) { // 播放器显示时,重新设置背景颜色 if (targetBackground.value) { - setTextColors(targetBackground.value); + applyBackground(targetBackground.value); } } else { showFullLyrics.value = false; diff --git a/src/renderer/hooks/useLyricBackground.ts b/src/renderer/hooks/useLyricBackground.ts new file mode 100644 index 0000000..85840b1 --- /dev/null +++ b/src/renderer/hooks/useLyricBackground.ts @@ -0,0 +1,76 @@ +import { onBeforeUnmount, ref } from 'vue'; + +import { textColors } from '@/hooks/MusicHook'; +import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor'; + +type UseLyricBackgroundOptions = { + /** + * 可选:返回需要写入 --bg-color CSS 变量的颜色字符串。 + * - 不提供:完全不写 --bg-color(桌面全屏场景) + * - 提供:有背景分支调用以取值,undefined 时落回 DEFAULT_BG_COLOR; + * 空背景分支固定写入 DEFAULT_BG_COLOR(与移动端原有行为一致) + */ + writeBgColor?: () => string | undefined; +}; + +const DEFAULT_BG_COLOR = 'rgba(25, 25, 25, 1)'; + +export function useLyricBackground(options: UseLyricBackgroundOptions = {}) { + const currentBackground = ref(''); + const animationFrame = ref(null); + const isDark = ref(false); + + const { writeBgColor } = options; + const root = document.documentElement; + + const applyBackground = (background: string) => { + if (!background) { + textColors.value = getTextColors(); + root.style.setProperty('--hover-bg-color', getHoverBackgroundColor(false)); + root.style.setProperty('--text-color-primary', textColors.value.primary); + root.style.setProperty('--text-color-active', textColors.value.active); + if (writeBgColor) { + root.style.setProperty('--bg-color', DEFAULT_BG_COLOR); + } + return; + } + + textColors.value = getTextColors(background); + isDark.value = textColors.value.active === '#000000'; + + root.style.setProperty('--hover-bg-color', getHoverBackgroundColor(isDark.value)); + root.style.setProperty('--text-color-primary', textColors.value.primary); + root.style.setProperty('--text-color-active', textColors.value.active); + + if (writeBgColor) { + const bg = writeBgColor(); + root.style.setProperty('--bg-color', bg || DEFAULT_BG_COLOR); + } + + if (currentBackground.value) { + if (animationFrame.value) { + cancelAnimationFrame(animationFrame.value); + } + const result = animateGradient(currentBackground.value, background, (gradient) => { + currentBackground.value = gradient; + }); + if (typeof result === 'number') { + animationFrame.value = result; + } + } else { + currentBackground.value = background; + } + }; + + onBeforeUnmount(() => { + if (animationFrame.value) { + cancelAnimationFrame(animationFrame.value); + } + }); + + return { + isDark, + currentBackground, + applyBackground + }; +}