requestanimationframe才是控制帧率的关键,它使动画逻辑与屏幕刷新同步,自动适配不同刷新率,避免丢帧和卡顿。

HTML 本身不控制帧率,requestAnimationFrame 才是关键
HTML 是标记语言,没有“帧率”概念;浏览器渲染引擎(如 Blink、WebKit)负责将 DOM + CSS + JS 转为画面,而帧率由渲染管线决定。真正能主动参与帧率调度的,只有 requestAnimationFrame(简称 raf)。它不是“设置帧率”,而是“在下一帧绘制前通知你”,让动画逻辑与屏幕刷新节奏对齐。
常见错误是用 setTimeout 或 setInterval 模拟 60fps(如 setTimeout(fn, 16)),但系统负载高时会丢帧、卡顿,且无法自动适配高刷屏(120Hz)或节能模式下的降频。
-
raf自动匹配设备实际刷新率(60Hz/90Hz/120Hz/144Hz),无需硬编码 16ms - 页面后台运行时,
raf会被暂停,省电且不浪费资源 - 必须在每次回调中显式调用下一次
raf,否则动画只执行一帧
let x = 0;
function animate() {
x += 2;
element.style.transform = `translateX(${x}px)`;
requestAnimationFrame(animate); // 必须递归调用
}
requestAnimationFrame(animate);
CSS 动画比 JS 改 style 更稳,但有前提条件
直接修改 element.style.transform 或 opacity 可触发合成(compositing),走 GPU 加速通路,通常能稳定 60fps;但改 left、top、width 等会触发 layout + paint,极易掉帧。
使用 @keyframes + animation 的优势在于:浏览器可提前编译动画路径、复用图层、跳过 JS 执行开销。但它不灵活——无法响应鼠标位置、滚动偏移等实时输入。
- 优先用
transform和opacity做 CSS 动画,避免触发布局(layout)和重绘(paint) - 需要交互响应时,用
raf驱动transform,别用left/top - 开启硬件加速的“伪技巧”(如
transform: translateZ(0))已过时,现代浏览器自动优化,乱加反而增加图层管理负担
Performance API 能定位真瓶颈,别靠猜
“动画卡”不等于“JS 慢”。用 DevTools 的 Performance 面板录制后,重点看三栏:Frames(是否稳定 16ms)、Layout(是否频繁触发)、Paint(是否大面积重绘)。但更轻量的方式是用 performance.now() + raf 手动打点:
let lastTime = 0;
function checkFps(timestamp) {
const delta = timestamp - lastTime;
if (delta > 0) console.log(`Frame time: ${delta.toFixed(1)}ms`);
lastTime = timestamp;
requestAnimationFrame(checkFps);
}
requestAnimationFrame(checkFps);
- 持续高于 16.7ms 表示帧超时,需查是否做了同步 layout(如读取
offsetTop后立刻改样式) - 出现大量
Recalculate Style或Layout事件,说明 CSS 选择器太复杂或样式依赖层级过深 - 动画元素没被提升为独立合成层(
will-change: transform可提示,但别滥用)
滚动绑定动画最容易掉帧,要用 passive + 节流
监听 scroll 事件直接驱动动画,是掉帧重灾区:滚动本身高频,而默认事件处理器是 active,会阻塞主线程,强制浏览器同步计算样式和布局。
- 必须用
{ passive: true }注册 scroll 监听器,禁用preventDefault换取流畅滚动 - 不要在
scroll回调里直接改样式,改用raf缓存最新 scrollY,再在下一帧统一更新 - 对视差、吸顶等效果,用
IntersectionObserver替代scroll+getBoundingClientRect,避免强制同步 layout
复杂交互动画往往卡在“以为自己优化了”,其实只是把问题藏得更深。比如加了 will-change 却没关掉不必要的 box-shadow 或 filter,或者用 raf 包裹了耗时函数却忘了它本身不解决计算瓶颈——该拆分任务还得用 setTimeout 切片,该用 Web Worker 的别硬塞进主线程。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











