Files
WeChatDataAnalysis/frontend/composables/useWrappedTheme.js
2977094657 e9c81caa12 improvement(wrapped-ui): 下线 DOS 主题并优化 Wrapped 多主题体验
- 移除 DOS 主题入口、切换器组件与相关样式逻辑,统一主题为 Modern / GameBoy / Win98。

- 新增 WrappedGameboyDither 组件,并在背景与 CRT 叠加层中引入 GameBoy 噪点效果。

- 优化 wrapped 页面视口高度与背景同步逻辑(含 ResizeObserver 与 100dvh 适配),提升桌面容器显示稳定性。

- 调整封面标题与预览位移、回复速度卡片滚动行为等细节,提升主题下视觉与交互一致性。
2026-02-07 20:59:03 +08:00

110 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 年度总结页面主题管理 composable
* 支持三种主题modern现代、gameboyGame Boy、win98Windows 98
*/
const STORAGE_KEY = 'wrapped-theme'
const VALID_THEMES = ['off', 'gameboy', 'win98']
const RETRO_THEMES = new Set(['gameboy'])
// 全局响应式状态(跨组件共享)
const theme = ref('off')
let initialized = false
let keyboardInitialized = false
export function useWrappedTheme() {
// 初始化:从 localStorage 读取(仅执行一次)
const initTheme = () => {
if (initialized || !import.meta.client) return
const saved = localStorage.getItem(STORAGE_KEY)
if (saved && VALID_THEMES.includes(saved)) {
theme.value = saved
}
initialized = true
}
// 设置主题
const setTheme = (newTheme) => {
if (!VALID_THEMES.includes(newTheme)) {
console.warn(`Invalid theme: ${newTheme}`)
return
}
theme.value = newTheme
if (import.meta.client) {
localStorage.setItem(STORAGE_KEY, newTheme)
}
}
// 切换到下一个主题(循环)
const cycleTheme = () => {
const currentIndex = VALID_THEMES.indexOf(theme.value)
const nextIndex = (currentIndex + 1) % VALID_THEMES.length
setTheme(VALID_THEMES[nextIndex])
}
// 计算属性:是否为复古模式(非 off
const isRetro = computed(() => theme.value !== 'off')
// 计算属性:当前主题的 CSS 类名
const themeClass = computed(() => {
if (theme.value === 'off') return ''
// Note: not every non-modern theme is "retro pixel/CRT".
// Keep wrapped-retro for themes that rely on pixel/CRT shared styles.
const base = RETRO_THEMES.has(theme.value) ? 'wrapped-retro ' : ''
return `${base}wrapped-theme-${theme.value}`
})
// 计算属性:主题显示名称
const themeName = computed(() => {
const names = {
off: 'Modern',
gameboy: 'Game Boy',
win98: 'Windows 98'
}
return names[theme.value] || 'Modern'
})
// 全局 F1-F3 快捷键切换主题(仅初始化一次)
const initKeyboardShortcuts = () => {
if (keyboardInitialized || !import.meta.client) return
keyboardInitialized = true
const handleKeydown = (e) => {
// 检查是否在可编辑元素中
const el = e.target
if (el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || el.isContentEditable)) {
return
}
if (e.key === 'F1') {
e.preventDefault()
setTheme('off')
} else if (e.key === 'F2') {
e.preventDefault()
setTheme('gameboy')
} else if (e.key === 'F3') {
e.preventDefault()
setTheme('win98')
}
}
window.addEventListener('keydown', handleKeydown)
}
// 客户端挂载后再初始化:避免 SSR 与首帧 hydration 不一致
onMounted(() => {
initTheme()
initKeyboardShortcuts()
})
return {
theme: readonly(theme),
setTheme,
cycleTheme,
isRetro,
themeClass,
themeName,
VALID_THEMES
}
}