Canvas动画帧率控制应优先使用requestAnimationFrame(rAF)配合时间戳动态节流,精准锁定目标FPS;其次可用帧计数器实现整数倍降帧;需结合visibilityState避免隐藏页资源浪费;慎用setInterval/setTimeout模拟rAF。

Canvas动画的帧率控制,核心在于让requestAnimationFrame(rAF)真正按需执行,而不是“能刷多快刷多快”。盲目调用setTimeout或固定间隔循环反而容易失步、掉帧或耗电。下面几种方法兼顾精度、兼容性和实用性。
用时间戳做动态节流,精准锁定目标FPS
rAF本身不保证帧率,但每次回调都带高精度时间戳。我们可据此判断是否该渲染下一帧:
- 记录上一帧渲染时间,计算已过毫秒数
- 设定目标帧间隔(如60FPS → 1000/60 ≈ 16.67ms)
- 仅当经过时间 ≥ 帧间隔时才执行绘制,并更新时间戳
这样既不阻塞主线程,又能平滑逼近目标帧率,即使页面后台运行或设备性能波动,也能自适应减速。
用计数器实现简单整数倍降帧(如30FPS、20FPS)
若只需粗略控制(如把60FPS降到30FPS),可用帧计数代替时间计算:
立即学习“前端免费学习笔记(深入)”;
- 声明一个
frameCount = 0和目标间隔(如2表示每2帧渲染1次) - rAF回调中
frameCount++,仅当frameCount % interval === 0时绘图 - 重置计数器或继续累加均可,逻辑轻量无时间误差累积
适合UI反馈类动画(如粒子淡入、进度条),对时序精度要求不高,代码简洁易维护。
结合visibilityState避免隐藏页浪费资源
用户切走标签页时,浏览器通常会降低rAF频率甚至暂停,但部分动画逻辑(如数据更新、音频同步)仍可能运行。主动监听页面可见性更可靠:
- 监听
document.visibilityState变化 - 不可见时暂停rAF循环(保存状态、停止调用
requestAnimationFrame) - 重新可见时重置时间戳/计数器,再恢复动画
这不仅省电,还能防止切回时因积压大量未执行帧导致卡顿或跳变。
慎用setInterval/setTimeout模拟rAF
虽然setInterval(fn, 16)看似能锁60FPS,但实际问题明显:
- 定时器延迟不可控,尤其在JS繁忙时严重滞后
- 与屏幕刷新周期不同步,易引发撕裂或抖动
- 无法被浏览器优化(如节流、后台降频),功耗更高
除非兼容极老环境(IE9及以下),否则不建议用它替代rAF。真要兜底,可用performance.now() + rAF组合模拟,而非单纯依赖定时器。


















