Window: requestAnimationFrame() 方法

基线 广泛可用

此功能已成熟,可在许多设备和浏览器版本中使用。它从 2015年7月.

报告反馈

window.requestAnimationFrame() 方法告诉浏览器您希望执行动画。它请求浏览器在下次重绘之前调用用户提供的回调函数。

回调函数的调用频率通常与显示刷新率匹配。最常见的刷新率是 60hz(每秒 60 个周期/帧),尽管 75hz、120hz 和 144hz 也被广泛使用。为了提高性能和延长电池寿命,在后台选项卡或隐藏的 <iframe> 中运行时,大多数浏览器都会暂停 requestAnimationFrame() 调用。

注意:如果您想为另一帧设置动画,您的回调函数必须再次调用 requestAnimationFrame()requestAnimationFrame() 是单次性的。

语法

警告:务必始终使用第一个参数(或其他获取当前时间的方法)来计算动画在帧中将进行多少——否则,动画在高刷新率屏幕上会运行得更快。有关执行此操作的方法,请参阅以下示例。
requestAnimationFrame(callback)

js

参数

回调

时间戳

一个 DOMHighResTimeStamp,表示上一帧渲染的结束时间(基于自 时间原点 经过的毫秒数)。时间戳是一个十进制数,以毫秒为单位,但最小精度为 1 毫秒。对于 Window 对象(不是 Workers),它等于 document.timeline.currentTime。此时间戳在运行在同一个代理(所有相同来源的窗口,更重要的是,相同来源的 iframe)上的所有窗口之间共享——这使得可以跨多个 requestAnimationFrame 回调同步动画。时间戳值也类似于在回调函数开始时调用 performance.now(),但它永远不会相同。

当由 requestAnimationFrame() 排队的多个回调开始在一帧中触发时,即使在计算每个先前回调的工作量期间已经过去了时间,每个回调也会接收到相同的时间戳。

返回值

一个 long 整数,请求 ID,它唯一标识回调列表中的条目。这是一个非零值,但您不能对它的值做任何其他假设。您可以将此值传递给 window.cancelAnimationFrame() 以取消刷新回调请求。

示例

在本例中,元素动画持续 2 秒(2000 毫秒)。该元素以 0.1px/ms 的速度向右移动,因此它的相对位置(以 CSS 像素为单位)可以用动画开始以来的经过时间(以毫秒为单位)来计算,方法是 0.1 * elapsed。元素的最终位置是其初始位置右侧 200px(0.1 * 2000)。

警告:务必始终使用第一个参数(或其他获取当前时间的方法)来计算动画在帧中将进行多少——否则,动画在高刷新率屏幕上会运行得更快。有关执行此操作的方法,请参阅以下示例。
const element = document.getElementById("some-element-you-want-to-animate");
let start;

function step(timeStamp) {
  if (start === undefined) {
    start = timeStamp;
  }
  const elapsed = timeStamp - start;

  // Math.min() is used here to make sure the element stops at exactly 200px
  const shift = Math.min(0.1 * elapsed, 200);
  element.style.transform = `translateX(${shift}px)`;
  if (shift < 200) {
    previousTimeStamp = timeStamp;
    requestAnimationFrame(step);
  }
}

requestAnimationFrame(step);

以下三个示例说明了设置时间零点(用于计算每帧动画进度的基线)的不同方法。如果您想与外部时钟同步,例如 BaseAudioContext.currentTime,则可用的最高精度是单个帧的持续时间,即 60hz 时为 16.67ms。回调的时间戳参数表示上一帧的结束,因此您新计算的值将在下一帧中最早被渲染。

此示例等到第一个回调执行后才设置 zero。如果您的动画在开始时跳到一个新值,您必须以这种方式构建它。如果您不需要与任何外部内容(如音频)同步,那么建议使用这种方法,因为某些浏览器在第一次调用 requestAnimationFrame() 和第一次调用回调函数之间存在多帧延迟。

警告:务必始终使用第一个参数(或其他获取当前时间的方法)来计算动画在帧中将进行多少——否则,动画在高刷新率屏幕上会运行得更快。有关执行此操作的方法,请参阅以下示例。
let zero;
requestAnimationFrame(firstFrame);
function firstFrame(timeStamp) {
  zero = timeStamp;
  animate(timeStamp);
}
function animate(timeStamp) {
  const value = (timeStamp - zero) / duration;
  if (value < 1) {
    element.style.opacity = value;
    requestAnimationFrame((t) => animate(t));
  } else element.style.opacity = 1;
}

此示例使用 document.timeline.currentTime 在第一次调用 requestAnimationFrame 之前设置零值。document.timeline.currentTimetimeStamp 参数对齐,因此零值等效于第 0 帧的时间戳。

警告:务必始终使用第一个参数(或其他获取当前时间的方法)来计算动画在帧中将进行多少——否则,动画在高刷新率屏幕上会运行得更快。有关执行此操作的方法,请参阅以下示例。
const zero = document.timeline.currentTime;
requestAnimationFrame(animate);
function animate(timeStamp) {
  const value = (timeStamp - zero) / duration; // animation-timing-function: linear
  if (value < 1) {
    element.style.opacity = value;
    requestAnimationFrame((t) => animate(t));
  } else element.style.opacity = 1;
}

此示例使用 performance.now() 而不是回调的时间戳值来设置动画。您可以使用此方法来实现稍微更高的同步精度,尽管额外的精度是可变的,并没有很大程度的提高。注意:此示例不允许您可靠地同步动画回调。

警告:务必始终使用第一个参数(或其他获取当前时间的方法)来计算动画在帧中将进行多少——否则,动画在高刷新率屏幕上会运行得更快。有关执行此操作的方法,请参阅以下示例。
const zero = performance.now();
requestAnimationFrame(animate);
function animate() {
  const value = (performance.now() - zero) / duration;
  if (value < 1) {
    element.style.opacity = value;
    requestAnimationFrame((t) => animate(t));
  } else element.style.opacity = 1;
}

规范

规范
HTML 标准
# dom-animationframeprovider-requestanimationframe

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅