用requestAnimationFrame写的动画为什么会偶尔卡顿?

Code°子睿 阅读 27

我在用JS写一个简单的位移动画,用requestAnimationFrame控制帧率,但发现动画偶尔会出现卡顿。代码看起来没问题,尝试过把时间间隔改成16ms还是没改善,这是为什么啊?

这是我的代码:


let pos = 0;
function animate(time) {
  pos = time / 50;
  elem.style.left = pos + 'px';
  // 这里是不是哪里写错了?
  requestAnimationFrame(animate(time + 10));
}
requestAnimationFrame(animate);

测试时发现当快速切换标签页再回来,卡顿更严重。明明用了浏览器内置的帧动画API,为什么还会这样?是不是时间计算有问题?

我来解答 赞 7 收藏
二维码
手机扫码查看
1 条解答
闲人芳芳
你的代码里有几个问题需要调整。首先,requestAnimationFrame 的回调函数是由浏览器自动传入时间戳的,你不能手动传参 animate(time + 10) 这样调用,这会导致递归调用栈溢出或者行为异常。正确的写法应该是直接传函数引用。

另外,动画卡顿的原因主要和浏览器的渲染机制有关。当你切换标签页时,浏览器会对非活动标签页中的 requestAnimationFrame 进行降频处理,可能从 60fps 降到个位数 fps,等你切回来时会有一段时间的追赶过程,这就导致了卡顿现象。

通用的做法是通过时间戳来计算真实的时间差,而不是依赖帧间隔假设固定的 16ms。这样可以保证动画在不同设备或标签页切换后依然平滑。下面是改进后的代码:

let pos = 0;
let lastTime = performance.now();
function animate(currentTime) {
// 计算真实时间差
let deltaTime = currentTime - lastTime;
lastTime = currentTime;

// 根据时间差更新位置
pos += deltaTime / 50;
elem.style.left = pos + 'px';

// 正确使用 rAF
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);


这里的关键点是用 performance.now() 获取高精度时间戳,确保动画基于真实时间差来计算,而不是假设每帧间隔固定。这种写法能有效避免因标签页切换或其他性能问题导致的卡顿。

还有一点需要注意,如果页面上有其他耗时任务(比如大量的 DOM 操作或者复杂的计算),也会影响动画流畅度。尽量把这些任务放到 Web Worker 中,或者用 setTimeout 分片执行,给动画留出足够的主线程时间。
点赞
2026-02-18 11:00