117 lines
8.6 KiB
Markdown
117 lines
8.6 KiB
Markdown
# 首页 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 轮播切换、桌面/移动端尺寸比例正常
|