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 { CategoryDock } from './start/CategoryDock' import { WeatherFx } from './start/WeatherFx' // 背景图:放 src/assets/startpage/ 下。6:00–24:00 从 No.1~No.5 里随机取一张; // 0:00–6:00 用 Night。缺图时互相回退,再无则用渐变。说明见该目录 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] } // 白天候选:No.1~No.5(按编号排序);夜间:Night。 const dayBgs = Object.entries(bgFiles) .filter(([p]) => /\/no\.\d+\./i.test(p)) .sort(([a], [b]) => a.localeCompare(b)) .map(([, url]) => url) const nightBg = bgByName('night') // 昼夜判断:纯按本地时间,0:00–6:00 为夜。 function isNight(): boolean { return new Date().getHours() < 6 } // 动态效果类型:只做雨/雪两类,其余静态。 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 } export function StartPage() { 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 // 白天随机选一张,整页生命周期内固定,避免每分钟一拍时背景乱跳。 const [dayIdx] = useState(() => Math.floor(Math.random() * Math.max(dayBgs.length, 1))) // 开发预览(仅 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() const dayBg = dayBgs[dayIdx] ?? dayBgs[0] const bg = (night ? nightBg : dayBg) ?? dayBg ?? nightBg 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=纯黑)▼▼ */}
{/* 天气动态层:雨/雪粒子(克制),位于遮罩之上、内容之下 */} {/* 占位:补回快捷入口腾出的高度,让问候/搜索框保持删除前的垂直位置与比例, 不因居中重排而被撑到页面正中。底部入口改由 CategoryDock 承载。 */}
{/* 底部分类抽屉:按类型分组的快捷入口,悬停某类时菜单向上抽出 */}
) }