返回文章列表
技术

把一帧压进 16.7 毫秒

3 分钟

60fps = 每帧 16.7ms。浏览器自己的合成、样式计算要吃掉 3–5ms,留给你的实际只有 10ms 左右

预算这么紧,就得像做穷游一样精打细算。

#一、先分清三条流水线

JS  →  Style  →  Layout  →  Paint  →  Composite

动画只走最后一步(Composite)时,成本几乎为零,因为它在 GPU 上跑,甚至不占主线程。

  • transform / opacity → 只 Composite ✅
  • width / left → 从 Layout 重跑 ❌
  • background-color → 从 Paint 重跑 ⚠️

#二、WebGL 的三个省法

本站背景是一个实时光线步进的流体,但它在低端机上也不掉帧,靠的是:

1. 降低渲染分辨率

const scale = 0.55;
canvas.width  = innerWidth  * devicePixelRatio * scale;
canvas.height = innerHeight * devicePixelRatio * scale;

分辨率降 45%,像素着色器的调用次数降到 30%。而背景本来就是模糊的,肉眼无损。

2. 自适应降级

统计最近 40 帧的平均耗时,超过 24ms 就把 scale 再降一档,同时减少步进次数。永远不要假设用户的设备和你的一样好。

3. 不可见就不算

document.addEventListener('visibilitychange', () => {
  running = !document.hidden;
});

标签页切走还在满速渲染的网站,是在偷用户的电池。

#三、DOM 侧的三个省法

  • 批量读写分离:先集中读所有 getBoundingClientRect,再集中写样式。混着来会触发强制同步布局(layout thrashing)。
  • IntersectionObserver 代替 scroll 监听:滚动事件一秒能打上百次,观察器由浏览器调度,几乎免费。
  • content-visibility: auto:长列表视口外的元素跳过渲染,一行 CSS 换几十毫秒。

#四、真正的性能感知

用户感知到的"快",有一半不是真快,是响应及时

100ms 内有反馈 = 即时;1s 内完成 = 流畅;超过 1s 必须有进度指示。

所以哪怕数据还没回来,按钮也要立刻有按下的形变。先给反馈,再给结果。

余烬电台

40 首 · 网易云源