raf:写动画我可能没那么合适...

823 阅读6分钟

先有问题再有答案

  1. 如何理解过度渲染?
  2. 为什么动画会掉帧?
  3. raf写动画真的是最佳实践嘛?
  4. 高刷新率意味着高性能嘛?
  5. 使用raf在120fps的设备上如何控制频率为60fps?
  6. 关于动画有什么建议?
  7. css动画的执行频率也能控制嘛?

背景

相信大家一定都听过使用raf写动画可以提高动画性能 获得更好的动画体验,raf也通常与高性能绑定在一起,事实真的是这样嘛?

不使用raf有什么问题?

过度渲染

如果使用 setInterval 或 setTimeout 来控制动画的更新频率,可能会导致每 5 毫秒(或其他较短的时间间隔)就进行一次 DOM 操作。然而,浏览器的渲染频率通常是每 16.6 毫秒(即每秒 60 帧)。这意味着在两次浏览器渲染之间,可能会有多次 DOM 操作,这些操作会频繁触发回流和重绘。

多做的渲染是没有意义的,因为浏览器在每次渲染时只会显示最新的状态,之前的中间状态会被丢弃。

频繁的回流和重绘会增加 CPU 和 GPU 的负担,导致页面性能下降,尤其是在移动设备上更为明显。

渲染缺失

与过渡渲染相对的 第二种可能就是渲染缺失发生掉帧行为

可能会因为各种原因如大量事件被触发主线程阻塞导致某些帧没有被正确渲染。这种情况下,渲染频率可能会到20fps 动画会出现卡顿或跳帧,影响用户体验。

raf如何解决的?

帧率同步

rAF 的核心优势在于它与浏览器的 渲染帧率保持同步。这意味着使用 rAF 可以确保动画在每一帧都得到更新,这不仅提高了动画的流畅性,还减少了不必要的计算和渲染开销,从而实现更流畅的动画效果。

高刷问题

但是在高刷新率设备上,rAF 的回调函数会被更频繁地调用。如果动画逻辑复杂或包含大量的 DOM 操作,这些频繁的回调会显著增加 CPU 的负担。

例如我们之前的文章中实现的一个弹幕组件: 来, vue3实现一个高性能的弹幕组件。我们可以分别对比下使用js的raf方案 和 css的transition方案.看下两者有什么区别。

js的raf方案

同样一个组件一样的数据 js使用raf的方式运行 cpu占用率平均3%~5% 录屏2025-01-11 14.59.51.gif

css的transition方案

css的transition动画方式运行 cpu占用率平均不到1% 录屏2025-01-11 15.01.24.gif

结论

在高刷新率设备上,rAF 的回调函数会被更频繁地调用。如果动画逻辑复杂或包含大量的 DOM 操作,这些频繁的回调会显著增加 CPU 的负担

人眼对动画的感知通常在 60fps 左右就已经非常流畅,更高的刷新率并不会带来显著的视觉提升 。因此,在 120fps 的设备上,即使 rAF 以 120fps 的频率调用回调函数,用户也不会感受到额外的流畅度,反而会因为频繁的回调导致 CPU 占用率上升和性能浪费。

  • 其实高刷并不意味着高性能 因为对cpu的使用率会上升 引起手机发热的体验问题。
  • js+raf的方案并不适合执行一些常驻的动画 因为这对cpu的占用是持续的

解决高刷

为了避免在高刷的手机上 更频繁的执行raf的回调 我们实现了帧率控制的能力。fpsLimiter: js实现帧率管理工具。 来解决这个问题。

css动画才是最优解

CSS 动画是相对于 JavaScript + rAF 的最优解,因为它利用了 GPU 加速,减少了 DOM 操作和 JavaScript 执行开销,提供了更流畅的动画和更好的性能。

  • GPU 加速:CSS 动画可以利用 GPU 进行硬件加速,而不是依赖 CPU。这意味着动画的计算和渲染任务可以交给专门的图形处理器,从而减轻 CPU 的负担 。
  • 更高效的渲染:GPU 专为处理图形和动画设计,因此在处理这些任务时比 CPU 更高效 。
  • 减少 DOM 操作:CSS 动画不需要频繁的 JavaScript 回调和 DOM 操作,减少了回流和重绘的次数,从而降低了 CPU 的负担 。
  • 更少的 JavaScript 执行:CSS 动画由浏览器引擎直接处理,不需要 JavaScript 介入,减少了 JavaScript 的执行开销 。
  • 更流畅的动画:由于 GPU 加速和更低的开销,CSS 动画通常比 JavaScript 动画更流畅,尤其是在高刷新率设备上 。
  • 更好的兼容性:现代浏览器对 CSS 动画的支持非常好,可以确保在不同设备和平台上的一致性 。
  • 自动优化:浏览器引擎会自动优化 CSS 动画的执行,例如通过缓存和复用动画帧,减少不必要的计算和渲染 。
  • 更少的内存占用:CSS 动画通常占用更少的内存,因为它们不需要维护复杂的 JavaScript 状态和回调函数 。

所以在两种方案都可以实现同样效果的情况下 请优先使用css做动画...

css动画控频

css动画在120fps的设备上执行的频率是跟随系统的 默认也会有过度消耗的问题

那么css可以做到控制动画的执行频率嘛?

steps

其实是可以的,使用 animation-timing-function 属性中的 steps 关键字来控制动画的执行频率。steps 关键字允许你指定动画在每个关键帧之间的步进数,从而间接控制动画的频率。

假设你希望动画在 1 秒内完成,并且以 50fps 的频率运行。

#myElement {
    width: 100px;
    height: 100px;
    background-color: red;
    position: relative;
    animation: move 1s steps(50) forwards;
}

@keyframes move {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(100px);
    }
}

在这个例子中,steps(50) 表示动画将在 1 秒内分成 50 步,每步大约 20 毫秒。

相关文章