mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-02-19 14:20:51 +08:00
- 移除 DOS 主题入口、切换器组件与相关样式逻辑,统一主题为 Modern / GameBoy / Win98。 - 新增 WrappedGameboyDither 组件,并在背景与 CRT 叠加层中引入 GameBoy 噪点效果。 - 优化 wrapped 页面视口高度与背景同步逻辑(含 ResizeObserver 与 100dvh 适配),提升桌面容器显示稳定性。 - 调整封面标题与预览位移、回复速度卡片滚动行为等细节,提升主题下视觉与交互一致性。
110 lines
3.0 KiB
JavaScript
110 lines
3.0 KiB
JavaScript
/**
|
||
* 年度总结页面主题管理 composable
|
||
* 支持三种主题:modern(现代)、gameboy(Game Boy)、win98(Windows 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
|
||
}
|
||
}
|