2020年10月1日
当浏览器限制 requestAnimationFrame 时
Matt Perry
requestAnimationFrame
(rAF) 是一个浏览器 API,它允许在设备显示器的下一个可用帧之前执行代码。
大多数 JavaScript 动画库使用此 API 来更改 DOM 元素、3D 模型或 canvas 上下文的视觉属性,以在 Web 上创建动画效果。
然而,并不能保证此方法会在每一帧之前运行。浏览器可能会选择以较低的间隔运行 requestAnimationFrame
,这会导致动画卡顿。
看看这两个方框
如果您的浏览器没有遇到我们将在本文中探讨的情况之一,则顶部方框将以 60fps 的帧率流畅地动画。第二个方框人为地以顶部方框一半的帧率运行。您可以看到帧率减半对动画流畅度的影响有多么明显。
由于浏览器可能选择限制 requestAnimationFrame
的情况并不总是显而易见或一致,因此调试这些动画可能是一项令人费解的体验。
如果您正在浏览此页面,您很可能是通过 Google 搜索而来,试图了解为什么您的 JavaScript 动画会卡顿。我猜您可能像我曾经一样,并排坐着两部相同的 iPhone,运行相同的 JavaScript 动画,一部以预期的 60fps 运行,另一部则以卡顿的 30fps 运行。
您没有疯。是您的浏览器有问题。让我们看看浏览器在何时以及为何限制 requestAnimationFrame
。
iOS 在低功耗模式下限制 rAF
发现这一点真是一次对理智的考验。在旧网站上调试动画时,我注意到我手机上的所有动画都很卡顿。
怀疑是垃圾回收或其他导致动画性能低下的原因,我尝试在我的 MacBook 上进行调试。内存消耗方面没有异常。我将 CPU 节流设置为最大。仍然是 60fps。
我把我的手机放在我伴侣的手机旁边,相同的操作系统,相同的品牌和型号。她的手机上是 60fps,我的手机上明显是 30fps。
我开始怀疑是渲染问题。但随后我在我的屏幕上发现了这个

切换低功耗模式的开启和关闭对动画的流畅度产生了立竿见影的效果。我找到了一个 Webkit 问题证实了 iOS 在低功耗模式下限制 requestAnimationFrame
,以及所有 CSS 动画.
这是一个聪明的省电技巧,也是无数个例子之一,说明了为什么针对移动浏览器进行开发是一次可怕的、令人沮丧的体验。
iOS 应用程序开发者被赋予了能力和责任来响应低功耗模式。然而,Webkit 典型的固执是导致 Battery Status API 现在被弃用.
Safari 在跨域 iframe 中限制 rAF
框架顶部的示例在跨域 iframe 中运行。“跨域”本质上意味着该 iframe 正在从与本网站不同的域提供内容。
如果您使用的是 Safari,您可能已经注意到即使是第一个方框也没有非常流畅地运行。返回并在 iframe 内单击或点击。动画应该开始按预期运行。
这个Webkit 工单没有提供这种行为的原因。推测一下,iframe 通常用于广告。广告会大量消耗您的 CPU 周期,并且经常使用非常明显的引人注目的动画。
因此,这种限制是为了防止广告耗尽您的电池电量。在单击/点击时帧率不受限制(但不是鼠标/touchstart 🙄),因为这被认为是用户有意接受该内容的信号。
在广告拦截器的时代,这种解决方案感觉是错误的方法。理想情况下,iOS 应该默认对广告采取更积极的立场,然后再撤销此更改。
让 Web 变得不那么糟糕,而不是更糟糕。
Firefox 为了隐私功能而彻底降低 JavaScript 时间精度
我最初是通过关于Firefox 中动画滞后的工单了解到这一点的。跨浏览器和设备的 Firefox 用户都在报告相同的问题,但我无法在任何操作系统上重现它。
事实证明,Firefox 有一个名为 resistFingerprinting
的反跟踪隐私设置,如果启用,会将 JavaScript 时间精度降低到 100 毫秒.
这个 Bugzilla 工单详细说明了 Mozilla 将 performance.now()
的精度降低到仅 2 毫秒(!)的后果。这被认为低到足以在游戏计算中造成混乱,因此 100 毫秒肯定低到足以破坏动画。
我们之前看到的限制情况仅使帧率减半。100 毫秒足以吞噬整整六帧动画!提供给 requestAnimationFrame
的回调接收最新帧的时间戳,在动画库中,这用于计算正确的视觉输出。如果时间戳不准确,动画将是该时间戳的视觉体现。
幸运的是,有一个潜在的修复方案。关于 performance.now()
的 MDN (RIP) 页面说,使用以下标头提供内容将允许您的页面访问高分辨率计时器
高分辨率计时器 === 流畅动画。
结论
requestAnimationFrame
只是一种请求 - 一个请求。是否批准该请求由浏览器自行决定,正如我们所见,在各种情况下都可能不被批准。
这是一种权衡。通过降低用户的体验质量,浏览器可以延长电池寿命,或将用户隐藏在广告公司面前。
Web 体验的质量目前处于历史最低水平,因此在我看来,看到任何让它变得更糟的事情都令人遗憾。
为了使用这些浏览器的开发人员的理智,我至少可以要求的是,当强制执行这些限制技术时,应在性能选项卡中标记出来。这将为我们提供可共享和可重复的重现步骤,供我们自己和用户在遇到这种奇怪行为时遵循,因为下一个优化总是在拐角处...