进度条要由真实进度驱动

**「点击后加载动画」不能用无限循环动画假装:要么绑定真实加载状态,要么基于经过时间连续推进。用 rAF 的帧间隔(`dt ≈ 16ms`)去判断 60ms 定时器,条件永远不成立。**

一、需求本身有四个约束

  1. 覆盖范围要完整 —— 侧边栏的品牌栏与主 nav 是两个独立元素(只是恰好都高 60px、都有底边框),只给主 nav 挂动画,左边自然是空的。
  2. 要一直从左到右扫过,不是闪烁。
  3. 扫到尽头时刚好加载完成 —— 与加载状态同步。
  4. 到尽头后停在满格,等加载完才消失。

二、坏掉的推进逻辑(根因)

const dt = now - last;   // ≈ 16ms(两帧间隔)
last = now;              // 每帧都重置
if (dt >= TICK_MS) {     // 16 >= 60 → 永远 false
  setProgress(...)
}

两个叠加错误:

  • dt 单帧只有 ~16ms,永远达不到 60ms,setProgress 在绝大多数帧里根本不执行;
  • last = now 每帧重置,dt 永远累积不起来。

真正把进度推到 100% 的是 pathname effect 里的一次性赋值。于是视觉上就是 0 → 100%,中间没有任何帧 —— 这就是"固定时间快速闪过、与加载不同步"。

正确做法:按经过时间做连续计算(elapsed / duration),而不是用帧间隔做定时判定。

三、验证时的采样陷阱

用 MutationObserver 观察 style 变化来采样进度,只拿到 4 个点 —— 因为 rAF 每帧写入相同的值会被浏览器合并,只有真正变化的属性才触发。要验证中间帧,得连续读取,不能依赖观察器。

相关

  • 判据要用真值而非代理 —— 本条是该 synthesis 的一处实例:拿「帧间隔/循环动画」当真实进度
  • 流式更新要合并字段而非替换对象(同一天的前端状态更新问题)
  • 样式丢失先核对类名再怀疑缓存与权限(先确认元素是不是同一个)
  • 高频手势缩放不要走React状态(同类:高频更新不该驱动框架状态)