js requestAnimationFrame

2026-05-10 07:00:34 1626阅读 0评论

动画掉帧?别再只用 setInterval 了,requestAnimationFrame 才是正解

做前端开发久了,肯定遇到过这种尴尬:精心调优的加载动画或拖拽效果,在 Mac 上丝滑无比,一旦放到旧款安卓手机或者高分屏上,立马掉帧严重,甚至出现画面撕裂。排查半天才发现,问题不出在算法复杂度,而是基础动画逻辑还在用 setTimeoutsetInterval

这就像是开车,定时任务好比是你自己踩着油门不管车速,浏览器重绘却像限速路标。两者步调不一致,必然卡顿。而 requestAnimationFrame(简称 rAF)的核心价值,就在于它主动告诉浏览器:“我要开始干活了,请在下一次重绘前调用我。”

为什么它更顺滑?

浏览器的渲染机制本质是“帧”。大多数屏幕刷新率是 60Hz,意味着每秒钟有 60 次机会更新画面,每次间隔约 16.6 毫秒。

传统的定时器完全无法感知这个节奏。如果 setInterval 设置的间隔是 10 毫秒,浏览器还没完成上一帧渲染,你的回调就已经准备好了,导致浏览器被迫跳过某些帧或合并操作;反之如果设为 20 毫秒,又会出现画面停顿。

使用 requestAnimationFrame 时,浏览器会自动调整回调函数的调用频率,确保在每个垂直同步信号之前执行。这不仅保证了动画流畅度,还能自动适配高刷屏设备。

实际开发中的坑

很多教程只教你怎么启动动画,却很少提如何安全地停止,这也是新手容易埋雷的地方。

想象一下,你在写一个弹窗动画组件。用户点击打开,触发了 rAF 循环。这时候用户迅速关闭弹窗并跳转到了新页面。如果不手动清理,之前的动画回调依然会在后台继续运行,直到被垃圾回收机制强制终止。这不仅浪费 CPU 资源,严重时还会引发内存泄漏。

正确的做法是持有回调 ID,并在组件销毁时清除。看下面这段示例代码:

let animationId = null;

function startAnimation() {
  if (animationId) return; // 防止重复启动

  const animate = (time) => {
    // 执行具体的样式更新逻辑
    element.style.transform = `translateX(${position}px)`;

    position++;
    if (position < 100) {
      animationId = requestAnimationFrame(animate);
    } else {
      cancelAnimationFrame(animationId); // 达到目标后手动结束
      animationId = null;
    }
  };

  animationId = requestAnimationFrame(animate);
}

function stopAnimation() {
  if (animationId) {
    cancelAnimationFrame(animationId); // 关键步骤:及时清理
    animationId = null;
  }
}

这里有个细节值得注意:必须在下次请求时传递回调,而不是直接把函数扔给浏览器。这样可以保证你有权随时通过返回的 ID 进行干预。

另一个隐藏优势:省电模式

除了流畅度,rAF 还有一个常被忽略的特性:当标签页被切换至后台最小化时,浏览器会自动暂停它的执行。

相比之下,如果你使用 setInterval,哪怕用户把页面最小化了,计时器依然在后台跑,白白消耗电量,导致笔记本风扇狂转。对于移动端 H5 页面或长时间运行的 Web 应用,这一点至关重要。利用浏览器的这一特性,本质上是在帮用户体验减负。

避免滥用

虽然 rAF 很强大,但也不能无脑用在所有场景。如果你的业务逻辑纯粹是计算数据而不涉及 DOM 更新或 Canvas 绘制,直接用普通函数即可。毕竟调用 rAF 本身也有一点微小的开销。

另外,在处理滚动监听等高频率事件时,不要直接绑在 scroll 事件里调用 rAF,因为滚动触发频率极高,可能会导致回调堆积。建议配合节流(throttle)思想,只在需要计算布局或视觉效果时才启用帧动画。

结语

技术选型没有绝对的高低,只有合不合适。但在涉及视觉反馈和交互体验的领域,requestAnimationFrame 已经逐渐成为行业标准。它不再是可选项,而是性能优化的底线。

从简单的进度条到复杂的视差滚动,掌握它的原理与清理机制,能让你的代码在保持流畅的同时,更加健壮节能。下一次写动画时,不妨试着放下定时器,让浏览器牵着你的手走。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
验证码
评论列表 (暂无评论,1626人围观)

还没有评论,来说两句吧...

目录[+]