feat(wrapped-ui): 新增年度总结页面与热力图卡片

- 新增 /wrapped PPT 风格滑动浏览(封面 + 卡片页)

- 新增 Card#1 组件与 24×7 周-小时热力图可视化

- 首页新增年度总结入口;useApi 增加 getWrappedAnnual;补充 wrapped 背景纹理
This commit is contained in:
2977094657
2026-01-30 16:26:52 +08:00
parent 519e9e9299
commit 79da96b2d3
12 changed files with 820 additions and 1 deletions

View File

@@ -0,0 +1,47 @@
// Utilities for Wrapped heatmap rendering.
export const clamp01 = (v) => {
const n = Number(v)
if (!Number.isFinite(n)) return 0
if (n < 0) return 0
if (n > 1) return 1
return n
}
export const maxInMatrix = (matrix) => {
if (!Array.isArray(matrix)) return 0
let m = 0
for (const row of matrix) {
if (!Array.isArray(row)) continue
for (const v of row) {
const n = Number(v)
if (Number.isFinite(n) && n > m) m = n
}
}
return m
}
// Color inspired by WeChat green, with a slight "gold" shift on high intensity
// (EchoTrace-style accent) while keeping the overall WeChat vibe.
export const heatColor = (value, max) => {
const v = Number(value) || 0
const m = Number(max) || 0
if (!(v > 0) || !(m > 0)) return 'rgba(0,0,0,0.05)'
// Use sqrt scaling to make low values still visible.
const t = clamp01(Math.sqrt(v / m))
// Hue from green (~145) -> yellow-green (~95)
const hue = 145 - 50 * t
const sat = 70
const light = 92 - 42 * t
return `hsl(${hue.toFixed(1)} ${sat}% ${light.toFixed(1)}%)`
}
export const formatHourRange = (hour) => {
const h = Number(hour)
if (!Number.isFinite(h)) return ''
const hh = String(h).padStart(2, '0')
return `${hh}:00-${hh}:59`
}