DedicatedWorkerGlobalScope: requestAnimationFrame() 方法

Baseline 已廣泛支援

此功能已成熟,並可在許多裝置和瀏覽器版本上執行。自 2023 年 3 月以來,它已在各種瀏覽器中可用。

注意:此功能僅在 專用 Web Worker 中可用。

DedicatedWorkerGlobalScope 介面的 requestAnimationFrame() 方法用於告知瀏覽器您希望請求一個動畫幀,並在下一次重繪前呼叫一個使用者提供的回撥函式。

回撥函式的呼叫頻率通常與顯示器的重新整理率相匹配。最常見的重新整理率是 60 Hz(每秒 60 個週期/幀),但 75 Hz、120 Hz 和 144 Hz 也被廣泛使用。在大多數瀏覽器中,當執行在後臺標籤頁或隱藏的 <iframe> 中時,requestAnimationFrame() 呼叫會被暫停,以提高效能和電池壽命。

呼叫 requestAnimationFrame() 方法只會安排一次對回撥函式的呼叫。如果您想動畫下一幀,您的回撥函式必須再次呼叫 requestAnimationFrame()

警告: 請務必始終使用第一個引數(或獲取當前時間的其他方法)來計算動畫在每一幀中將推進多少——否則,動畫在重新整理率較高的螢幕上會執行得更快。有關實現方法,請參見下方的示例。

呼叫 requestAnimationFrame() 方法需要當前 worker 擁有一個關聯的 window。這意味著當前 worker 必須由 window 或一個同樣擁有關聯 window 的專用 worker 建立。

語法

js
requestAnimationFrame(callback)

引數

回撥

當需要更新動畫以準備下一次重繪時要呼叫的函式。這個回撥函式會接收一個單一的引數

時間戳

一個 DOMHighResTimeStamp,指示前一幀渲染的結束時間(基於自 time origin 以來的毫秒數)。時間戳是一個十進位制數,單位是毫秒,但最小精度為 1 毫秒。時間戳值也類似於在回撥函式開始時呼叫 performance.now(),但它永遠不會是相同的值。

requestAnimationFrame() 排隊的多個回撥在同一幀中開始觸發時,每個回撥都會收到相同的時間戳,儘管在計算每個前置回撥的工作負載期間已經過去了時間。

返回值

一個 long 整數值,即請求 ID,它唯一標識了回撥列表中的條目。這是一個非零值,但您不能對此做任何其他假設。您可以將此值傳遞給 cancelAnimationFrame() 來取消重新整理回撥請求,取消操作必須在同一個 worker 中進行。

異常

NotSupportedError DOMException

如果當前 worker 不支援該方法,則會丟擲此異常。

示例

這是一個完整的示例,展示瞭如何在具有 OffscreenCanvas 的專用 worker 中使用 requestAnimationFrame()

HTML 應包含

html
<canvas width="100" height="100"></canvas>

它應該連結到以下 JavaScript

js
const worker = new Worker("worker.js");

// Transfer canvas control to the worker
const offscreenCanvas = document
  .querySelector("canvas")
  .transferControlToOffscreen();

// Start the animation
worker.postMessage(
  {
    type: "start",
    canvas: offscreenCanvas,
  },
  [offscreenCanvas],
);

// Stop the animation after 5 seconds
setTimeout(() => {
  worker.postMessage({
    type: "stop",
  });
}, 5000);

worker.js

js
let ctx;
let pos = 0;
let animationId;
let isRunning = false;
let lastTime = 0;

function draw(currentTime) {
  if (!isRunning) return;

  // Calculate delta time for smooth animation
  if (lastTime === 0) lastTime = currentTime;
  const deltaTime = (currentTime - lastTime) / 1000;
  lastTime = currentTime;

  // Clear and draw the moving rectangle
  ctx.clearRect(0, 0, 100, 100);
  ctx.fillRect(pos, 0, 10, 10);
  pos += 50 * deltaTime; // Move 50 pixels per second

  // Loop the animation
  if (pos > 100) pos = -10;

  animationId = self.requestAnimationFrame(draw);
}

self.addEventListener("message", (e) => {
  if (e.data.type === "start") {
    const transferredCanvas = e.data.canvas;
    ctx = transferredCanvas.getContext("2d");
    isRunning = true;
    lastTime = 0;
    animationId = self.requestAnimationFrame(draw);
  }
  if (e.data.type === "stop") {
    isRunning = false;
    if (animationId) {
      self.cancelAnimationFrame(animationId);
    }
  }
});

在主執行緒中,我們首先使用 HTMLCanvasElement.transferControlToOffscreen()<canvas> 元素的控制權轉移給一個 OffscreenCanvas,並向 worker 傳送一條 "start" 訊息,以啟動其工作,並附帶 offscreen canvas。

在 worker 檔案 (worker.js) 中,我們處理動畫邏輯。當收到 "start" 訊息時,worker 開始動畫,使矩形從左向右移動。收到 "stop" 訊息時,它將停止動畫。

最後,主執行緒可以在延遲後向 worker 傳送 "stop" 訊息以停止動畫,從而在停止前允許動畫可見。

規範

規範
HTML
# dom-animationframeprovider-requestanimationframe

瀏覽器相容性

另見