Web 动画性能优化:从 60fps 到丝滑体验

Lisa Tan | 2026-09-14T21:16:00 | Frontend

页面动画卡顿是前端性能问题里最影响用户体验的。总结了一套从排查到优化的完整方法论。

页面其他方面都很快,但动画卡顿就会让用户觉得"这个网站好卡"。最近花了一周专门优化动画性能,总结一下方法。 ## 动画卡顿的根因 浏览器的渲染流水线:JS → Style → Layout → Paint → Composite 动画卡顿 = 某一帧的处理时间 > 16.67ms(60fps) 最常见的原因是动画触发了 Layout(回流)或 Paint(重绘),这两步最耗时。 ## 排查方法 ### Chrome DevTools Performance 面板 1. 打开 Performance 面板 2. 点 Record,操作触发动画 3. 停止录制,看火焰图 重点看: - 有没有紫色的 Layout 块(回流) - 有没有绿色的 Paint 块(重绘) - 每帧是否超过 16ms ### Rendering 面板 打开 DevTools → More Tools → Rendering: - **Paint flashing**:绿色高亮表示重绘区域 - **Layout shift regions**:蓝色高亮表示布局偏移 ## 优化手段 ### 1. 只动画 transform 和 opacity 这两个属性可以在 Compositor 线程处理,不触发 Layout 和 Paint: ```css /* 差:触发 Layout */ .animate-bad { animation: move 0.3s; } @keyframes move { to { left: 100px; top: 50px; } } /* 好:只用 transform */ .animate-good { animation: move 0.3s; } @keyframes move { to { transform: translate(100px, 50px); } } ``` ### 2. 用 will-change 提前告知浏览器 ```css .card { will-change: transform; /* 提前创建合成层 */ transition: transform 0.3s ease; } .card:hover { transform: scale(1.05); } ``` 注意:不要到处加 `will-change`,每个合成层都消耗 GPU 内存。 ### 3. 用 requestAnimationFrame 替代 setTimeout ```javascript // 差 setInterval(() => { element.style.transform = `translateX(${x++}px)`; }, 16); // 好 function animate() { element.style.transform = `translateX(${x++}px)`; requestAnimationFrame(animate); } requestAnimationFrame(animate); ``` ### 4. 使用 Web Animations API ```javascript element.animate([ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' }, ], { duration: 300, easing: 'ease-out', fill: 'forwards', }); ``` Web Animations API 可以让浏览器在 Compositor 线程运行动画,完全不阻塞主线程。 ### 5. 虚拟列表处理长列表动画 长列表滚动时不要给每个 item 都加动画,只给可视区域的 item 加。用 IntersectionObserver 检测可见性。 ## 实际效果 我们官网的一个动画页面优化前后对比: - 优化前:滚动时 FPS 波动在 25-40 之间 - 优化后:稳定 60 FPS,偶尔掉到 55 核心就是把所有 `width/height/top/left` 动画改成了 `transform`,效果立竿见影。

← Back to Blog