2020年10月1日

当浏览器限制 requestAnimationFrame 时

Matt Perry

requestAnimationFrame (rAF) 是一个浏览器 API,它允许在设备显示器的下一个可用帧之前执行代码。

requestAnimationFrame(timestamp => {
  // Do stuff
})

大多数 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) 页面说,使用以下标头提供内容将允许您的页面访问高分辨率计时器

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

高分辨率计时器 === 流畅动画。

结论

requestAnimationFrame 只是一种请求 - 一个请求。是否批准该请求由浏览器自行决定,正如我们所见,在各种情况下都可能不被批准。

这是一种权衡。通过降低用户的体验质量,浏览器可以延长电池寿命,或将用户隐藏在广告公司面前。

Web 体验的质量目前处于历史最低水平,因此在我看来,看到任何让它变得更糟的事情都令人遗憾。

为了使用这些浏览器的开发人员的理智,我至少可以要求的是,当强制执行这些限制技术时,应在性能选项卡中标记出来。这将为我们提供可共享和可重复的重现步骤,供我们自己和用户在遇到这种奇怪行为时遵循,因为下一个优化总是在拐角处...

阅读更多

Vue 版 Motion 简介

Motion 终于登陆 Vue,完整配备了变体、滚动、布局动画以及您从 Framer Motion 中喜爱的一切。

Motion 终于登陆 Vue,完整配备了变体、滚动、布局动画以及您从 Framer Motion 中喜爱的一切。

揭秘:React 的实验性动画 API

React 正在试验基于 View Transition API 的新动画 API。它是如何工作的?它能做什么?我们在这篇博文中揭示一切。

React 正在试验基于 View Transition API 的新动画 API。它是如何工作的?它能做什么?我们在这篇博文中揭示一切。

如何为您的 Framer 站点添加 cmd-k 搜索快捷方式

默认情况下,Framer 搜索组件不支持 cmd-k 键盘快捷键。以下是如何将其添加到您的 Framer 站点。

默认情况下,Framer 搜索组件不支持 cmd-k 键盘快捷键。以下是如何将其添加到您的 Framer 站点。

Framer Motion 现在是独立的,推出 Motion

Framer Motion 现在是独立的。推出 Motion,一个用于 React 和所有 JavaScript 环境的新动画库。这对您意味着什么。

Framer Motion 现在是独立的。推出 Motion,一个用于 React 和所有 JavaScript 环境的新动画库。这对您意味着什么。

您仍然需要 Framer Motion 吗?

自 Framer Motion 发布以来的五年中,CSS 动画 API 取得了长足的进步。您仍然需要使用 Framer Motion 吗?

自 Framer Motion 发布以来的五年中,CSS 动画 API 取得了长足的进步。您仍然需要使用 Framer Motion 吗?

Motion 的实现离不开我们杰出的赞助商。

保持联系

订阅以获取最新消息和更新。

保持联系

订阅以获取最新消息和更新。

保持联系

订阅以获取最新消息和更新。