import { useEffect, useState } from 'react' import { motion } from 'framer-motion' import { fadeUp, stagger } from './motion' import { useWeather } from '../hooks/useWeather' import { Greeting } from './start/Greeting' import { SearchBar } from './start/SearchBar' import { QuickLinks } from './start/QuickLinks' import { SettingsGear } from './start/SettingsGear' import { WeatherFx } from './start/WeatherFx' // 背景图:放 src/assets/startpage/ 下。按昼夜选 light / dark; // 缺这两张时回退到目录里排序第一张,再无则用渐变。说明见该目录 README。 const bgFiles = import.meta.glob('../assets/startpage/*.{jpg,jpeg,png,webp}', { eager: true, query: '?url', import: 'default', }) function bgByName(name: string): string | undefined { return Object.entries(bgFiles).find(([p]) => p.toLowerCase().includes(`/${name}.`))?.[1] } const lightBg = bgByName('light') const darkBg = bgByName('dark') const anyBg = Object.entries(bgFiles) .sort(([a], [b]) => a.localeCompare(b)) .map(([, url]) => url)[0] // 和风图标码中能区分昼夜的子集(晴/多云/阵雨)。其余码无昼夜之分。 const NIGHT_ICONS = new Set(['150', '151', '152', '153', '350', '351', '456', '457']) const DAY_ICONS = new Set(['100', '101', '102', '103', '300', '301', '400', '401']) // 昼夜判断:图标码优先,判断不了的码用本地时间兜底(18:00–06:00 为夜)。 function isNight(icon?: string): boolean { if (icon) { if (NIGHT_ICONS.has(icon)) return true if (DAY_ICONS.has(icon)) return false } const h = new Date().getHours() return h < 6 || h >= 18 } // 动态效果类型:只做雨/雪两类,其余静态。 function fxKind(icon?: string): 'rain' | 'snow' | 'none' { const n = icon ? Number.parseInt(icon, 10) : NaN if (Number.isNaN(n)) return 'none' if ((n >= 400 && n <= 499) || n === 901) return 'snow' if (n >= 300 && n <= 399) return 'rain' return 'none' } function isThunder(icon?: string): boolean { return icon ? ['302', '303', '304'].includes(icon) : false } interface StartPageProps { /** 点右下齿轮:切到登录页 */ onOpenSettings: () => void } export function StartPage({ onOpenSettings }: StartPageProps) { const wx = useWeather() // 每分钟一拍,让昼夜随时间自动翻(即便天气没刷新)。 const [, setTick] = useState(0) useEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 60_000) return () => clearInterval(id) }, []) const icon = wx.data && wx.data.ok ? wx.data.icon : undefined // 开发预览(仅 DEV):?bg=light|dark 强制背景;?fx=rain|snow|thunder|none 强制动效。 // 例:http://localhost:5173/?bg=dark&fx=snow const ov = import.meta.env.DEV ? new URLSearchParams(window.location.search) : null const ovBg = ov?.get('bg') const ovFx = ov?.get('fx') const night = ovBg ? ovBg === 'dark' : isNight(icon) const bg = (night ? darkBg : lightBg) ?? lightBg ?? darkBg ?? anyBg let kind = fxKind(icon) let thunder = isThunder(icon) if (ovFx === 'snow') { kind = 'snow' thunder = false } else if (ovFx === 'thunder') { kind = 'rain' thunder = true } else if (ovFx === 'rain') { kind = 'rain' thunder = false } else if (ovFx === 'none') { kind = 'none' thunder = false } return (
{/* 背景层:有图用图,无图回退渐变 */} {bg ? (
) : (
)} {/* scrim:上下压暗,保证玻璃与文字在任意图片上都可读 */}
{/* ▼▼ 半透明黑遮罩:整体压暗背景。调透明度改下面的 0.5(0=全透明,1=纯黑)▼▼ */}
{/* 天气动态层:雨/雪粒子(克制),位于遮罩之上、内容之下 */}
) }