Files
hesu-JT/docs/superpowers/specs/2026-08-21-global-presence-map-design.md
T
2026-08-27 11:54:26 +08:00

117 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 首页 GLOBAL PRESENCE 地图动画替换 — 设计文档
日期:2026-08-21
状态:已与用户确认(1:1 还原 demo)
## 轮播模式迭代:累积滑窗(2026-08-21 四次补充)
应用户要求,首页地图与 auto-export 地图的轮播从"独占切换"(同一时刻一个)改为**累积滑窗**:
- 每步激活下一个据点,**已激活的保持显示**
- 窗口满后(n-1=4 个),每次新激活时**最旧的一个淡出**(0.45s opacity 过渡)
- 稳态 = 同时显示 4 个:最新的播完整动画序列,最旧的渐隐离场
- 生命周期不变:可见才播 / 完全离开归零 / 每次进入从第一个完整累积
实现:main.js 两个地图块的 `step()`(tick % n 激活,tick ≥ n-1 时 (tick-(n-1)) % n 淡出);CSS 给 `.location`/`.marker`/`.line` 加 opacity 过渡。版本 bump `?v=20260821-6`。
验证:两页 t0→t4 逐步累积至 4 个、t4 时首个淡出、t5 滑窗循环、离场归零、回来重播,全过。
## 丝滑包升级(2026-08-22)
应用户要求做动效升级,四项全部落地(两张地图统一):
1. **弧线引线**:首页 5 条折线 `d` 改为三次贝塞尔曲线(水平出发→自然弯到点位),绘制缓动 `cubic-bezier(.3,.7,.2,1)`
2. **退场反擦除**:入场=animation(.active),退场=反向 animation(.out 类,main.js 的 exit() 添加、650ms 后移除):线倒收回(lineOut dashoffset 0→1)、圆点缩小(dotOut)、标签下滑淡出(labelOut)、光环衰减(haloOut)。⚠️ 不能用 transition 实现退场 — CSS 规范规定 animation 移除时的值跳变不触发 transition
3. **重叠时序**:label .28s@.02s / line .55s@.12s / dot .32s@.5s — 各元素交叉进场
4. **呼吸 + 双涟漪**:dot 落定后 CSS `r` 属性呼吸(breatheR 7.5→8.3px / ae-breatheS 5.5→6.3 / ae-breatheL 6→6.9,`r` 与 transform 不冲突可同元素并存);每个点加第二圈光环/涟漪(halo2 / pulse.p2,延迟错开)
结构变化:子元素自管隐藏基态(group 不再切 opacity);HTML 每点 +1 光晕圆。版本 `?v=20260821-9`。
**验证注意**:Playwright 窗口不可见时 Chrome 冻结动画时钟(currentTime 恒 0)— 动画类验证必须先 `Page.bringToFront`。带前台后实测退场淡出中间态(175ms 时 opacity 0.71)精确符合曲线。
## 目标
把 [index.html](../../../index.html) 首页 GLOBAL PRESENCE 板块的静态占位地图(`image/home/global-map.png`)替换为 [global-map-animation-demo.html](../../../global-map-animation-demo.html) 中的据点轮播动画效果,1:1 还原。
## 效果(以 demo 为准)
- 背景图:`image/home/Frame 119.png`(1219 × 661)
- SVG 覆盖层与背景图共享 `viewBox="0 0 1219 661"` 坐标系,所有点/线/文字用原图坐标
- 5 个据点:DUBAI、ABU DHABI、ETHIOPIA、GHANA、RUSSIA
- 每个据点 = 白色标签 + 橙色(`--orange-700` = `#ff6a00`)折线引线 + 圆点 + 脉冲光环
- **同一时刻只激活一个据点**,每 3 秒切换下一个(循环)
- 动画序列:标签滑入(.35s)→ 引线绘制(.85s 延迟 .18s)→ 圆点弹出(.4s 延迟 1s)→ 光环脉冲(1.4s 无限循环)
- 含大屏首帧防闪烁修复:非 active 线 `stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0`
## 改动文件
| 文件 | 改动 |
|---|---|
| `index.html` | 替换 `.map-wrap` 内部为 `img.map-bg` + `svg.map-overlay`(保留外层 `reveal`、`aria-hidden`、`loading="lazy"`) |
| `css/style.css` | GLOBAL PRESENCE 段合入 demo 样式:`.map-wrap` 改 `aspect-ratio: 1219/661`,新增 `.map-bg`/`.map-overlay`/`.location` 系列 + 4 个 keyframes(颜色用 `var(--orange-700)`);删除废弃 `.map-img`;删除 ≤560px 媒体查询里 `.map-wrap { height: 260px }`(与 aspect-ratio 冲突,比例自适应后手机端高度 ≈200px 与原设计一致) |
| `css/mobile.css` | `.map-wrap` 规则去掉写死 `height: 200px`,保留 margin |
| `js/main.js` | 新增独立 IIFE:查 `.map-overlay`,`.location` 每 3s 轮播 `active`(选择器限定,不影响其他页面) |
## 刻意不做的(YAGNI)
- 不做全部常显模式(用户已确认单点轮播)
- 不移植 demo 里 vestigial 的 `--path-length` JS:CSS 从未读取该变量,`pathLength="1"` 属性已完成路径长度归一化
- 不删旧图 `global-map.png`(可能被 WP 主题或其他地方引用)
- 不加 demo 工具栏按钮(那是调试用的)
## 降级行为
- `prefers-reduced-motion`:站点全局 `animation: none`,当前据点显示标签+圆点(静止),引线保持未绘制 — 可接受
- 无 JS:DUBAI 常显(HTML 里初始 `active`)— 可接受
## 防闪加固(2026-08-21 补充)
用户报告症状:大屏上到达地图时先闪一帧完整线路 → 重置 → 才正常播放。
**根因**(Playwright 取证确认):非 CSS 首帧竞态(demo 的 base-state 修复已移植且有效),而是**状态机时序**——轮播从页面加载就开始空转,加载 6s 后(地图仍在视口外 2380px 处)ABU DHABI 的引线已处于 `fill: both` 保持的完结状态(opacity 1 / dashoffset 0)。用户滚到时第一眼撞见这条"陈旧完结帧"(= 闪一下),3s 后下个 tick 归零重置(= 消失),下一个据点才从头播放(= 正常出现)。屏幕越大板块离首屏越近,越容易触发。
**修复**([js/main.js](../../../js/main.js)):
- init 时先 `show(-1)` 清掉 HTML 里 DUBAI 的 `active`(main.js 是 body 末尾同步脚本,先于首帧执行;无 JS 时保留 DUBAI 静态回退)
- 首次激活用 IntersectionObserver 门控(观察 `.map-wrap`,threshold 0.12,与站点 reveal/count-up 同模式),触发后**双 rAF** 再 `show(0)`——先让不可见 base 状态绘制一帧,动画永远从已沉淀的样式树起步
- 进入视口才开始 3s 轮播;无 IO 的老浏览器直接 begin()
## 防闪加固第二层:re-entry 重启 + 缓存击穿(2026-08-21 二次补充)
用户反馈"还是会出现"。系统排查后确认两条残余路径:
1. **re-entry 陈旧态**:第一层修复只管"首次到达"。轮播一旦启动永不停 → 用户滚走再滚回时,当前据点又处于完结状态 → 闪 → 重置 → 下一个正常播放(症状与首次完全相同)。
2. **旧版 main.js 缓存**:python http.server 等无缓存头环境下,普通 F5 会命中启发式缓存执行旧 JS(Playwright 验证时实测踩中)。
**修复**:
- 轮播改为**可见才播放**的生命周期:IO 持续观察(threshold 0.12),`isIntersecting` → `start()`(每次进入都从 DUBAI 从头播,双 rAF 起步);完全离开 → `stop()`(清定时器 + 归零到不可见 base 态)。首次到达、回访、F5 原位刷新(Chrome 恢复滚动位置)三种场景统一为"从 DUBAI 完整播放"
- `start()` 内 rAF 等待期若已离开视口则放弃启动(守卫 `if (!timer) return`)
- index.html 脚本标签加版本号 `main.js?v=20260821` 击穿缓存(今后 main.js 有实质改动时手动 bump)
**验证**(Playwright):
- 静置顶部不滚动:零激活、所有线 opacity 0
- 滚入 → DUBAI dashoffset 0.93(从 0 画起);3.3s 后 ABU DHABI
- 滚到页脚离开 7s:`active: null`(轮播已停、已归零)
- 滚回:DUBAI dashoffset 重现从 0 画起;3.3s 后 ABU DHABI —— re-entry 不再撞见完结帧
- 原位 reload(Chrome 恢复 scrollY=2309 地图在视口):t=135ms DUBAI 已干净启动,无闪帧;t=10.2s 轮到 GHANA,节奏精准
- 控制台 0 错误 0 警告
**A/B 验证**:
- 修复前:加载 6s 未滚入 → ABU DHABI 激活、线完整(陈旧完结帧)
- 修复后:加载 8s 未滚入 → 零激活、所有线 opacity 0
- 滚入 0.6s → DUBAI 从 dashoffset 0.83 开始绘制;3.1s → ABU DHABI;6.3s → ETHIOPIA,循环正常
- 控制台 0 错误 0 警告
## 相关:bento 业务格渐显重播(2026-08-21 三次补充)
用户要求 FOUR CORE BUSINESSES(STRONG RESOURCES, RELIABLE DELIVERY)板块的图片逐个渐显在**每次进入视口时重播**(原 reveal 一次即 unobserve)。
- index.html:5 个 `.cell.reveal` 追加 `reveal-repeat` 标记类(仅此板块)
- main.js reveal 观察器:带 `reveal-repeat` 的元素不 unobserve,按 `isIntersecting` 切换 `.in` — ≥12% 可见整组阶梯重播,完全离开归零;其余元素保持一次性 reveal
- 脚本版本 bump 至 `?v=20260821-2`
验证:滚入 5/5 in → 滚走 0/5 → 滚回 5/5 重播;标题等一次性 reveal 不受影响;控制台 0 错误。
## 验证
- Playwright 打开 index.html:控制台无报错、3s 轮播切换、桌面/移动端尺寸比例正常