Files
WeChatDataAnalysis/frontend/utils/wrapped/heatmap.js
2977094657 7ce6abecca improvement(wrapped-ui): 移除 VHS 主题并优化 DOS/CRT 视觉效果
- 主题系统收敛为 Modern/Game Boy/DOS(快捷键改为 F1-F3)
- 删除 VHS 切换器与相关样式(卡片/控件/年份选择/图表等)
- DOS 主题统一使用像素字体,减弱发光强度并细化扫描线/闪烁参数
- DOS 闪烁光标改由 WrappedCRTOverlay 渲染,避免全局样式副作用
- 移除热力图 vhs 配色分支
2026-02-01 19:27:51 +08:00

73 lines
2.0 KiB
JavaScript

// 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)}%)`
}
// Theme-aware heat color function
export const themedHeatColor = (value, max, theme) => {
const v = Number(value) || 0
const m = Number(max) || 0
const t = (v > 0 && m > 0) ? clamp01(Math.sqrt(v / m)) : 0
switch (theme) {
case 'gameboy': {
// Game Boy 4-color palette: #0f380f, #306230, #8bac0f, #9bbc0f
if (t === 0) return '#9bbc0f'
if (t < 0.33) return '#8bac0f'
if (t < 0.66) return '#306230'
return '#0f380f'
}
case 'dos': {
// DOS green phosphor: from dark to bright green
if (t === 0) return 'rgba(51, 255, 51, 0.1)'
const light = 20 + 60 * t
return `hsl(120 100% ${light.toFixed(1)}%)`
}
default:
// Modern (off) - use original heatColor
return heatColor(value, max)
}
}
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`
}