Signatur
Beschreibung
requestAnimationFrame() ist im Kontext eines Dedicated Workers (self in einem mit new Worker() gestarteten Skript) verfügbar und funktioniert wie window.requestAnimationFrame(): Die übergebene Funktion wird genau einmal aufgerufen, bevor der Browser das nächste Bild darstellt. Sie ist also kein Dauer-Timer – wer eine fortlaufende Animation will, muss innerhalb des Rückrufs erneut requestAnimationFrame() aufrufen.
Hauptzweck ist das Rendern auf einem OffscreenCanvas, das per canvas.transferControlToOffscreen() an den Worker übergeben wurde. So laufen aufwendige Zeichen- oder WebGL-Operationen außerhalb des Hauptthreads, ohne dass die Bildrate von blockierendem UI-Code abhängt. Die Bildrate richtet sich nach der Bildwiederholfrequenz des Anzeigegeräts (meist 60 Hz, bei 120-Hz-Displays entsprechend höher).
Der Rückruf erhält einen DOMHighResTimeStamp, der den Zeitpunkt des Frames beschreibt. Diesen sollte man für zeitbasierte Animationen verwenden (Delta-Zeit), statt pro Frame einen festen Schritt anzunehmen. Mit dem zurückgegebenen Handle lässt sich die Anfrage über cancelAnimationFrame() wieder zurückziehen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (timestamp: DOMHighResTimeStamp) => void | Funktion, die vor dem nächsten Neuzeichnen aufgerufen wird. Erhält als einziges Argument einen Zeitstempel in Millisekunden (vergleichbar mit performance.now()). |
Rückgabewert
long), die an cancelAnimationFrame() übergeben werden kann, um den Rückruf abzubrechen.Beispiele
OffscreenCanvas an einen Worker übergeben und animieren
// main.js
const canvas = document.querySelector('#bühne');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('render-worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
// render-worker.js
let ctx;
let x = 0;
const zeichne = (timestamp) => {
const { width, height } = ctx.canvas;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = 'tomato';
ctx.fillRect(x, height / 2 - 20, 40, 40);
x = (x + 2) % width;
self.requestAnimationFrame(zeichne); // nächsten Frame anfordern
};
self.onmessage = ({ data }) => {
ctx = data.canvas.getContext('2d');
self.requestAnimationFrame(zeichne);
};
Zeitbasierte Bewegung mit Delta-Zeit
// render-worker.js
let ctx;
let letzterZeitpunkt = null;
let winkel = 0;
const GRAD_PRO_SEKUNDE = 90;
const frame = (timestamp) => {
if (letzterZeitpunkt === null) letzterZeitpunkt = timestamp;
const delta = (timestamp - letzterZeitpunkt) / 1000; // Sekunden
letzterZeitpunkt = timestamp;
winkel = (winkel + GRAD_PRO_SEKUNDE * delta) % 360;
const { width, height } = ctx.canvas;
ctx.resetTransform();
ctx.clearRect(0, 0, width, height);
ctx.translate(width / 2, height / 2);
ctx.rotate((winkel * Math.PI) / 180);
ctx.fillRect(-25, -25, 50, 50);
self.requestAnimationFrame(frame);
};
self.onmessage = ({ data }) => {
ctx = data.canvas.getContext('2d');
self.requestAnimationFrame(frame);
};
Animation per Nachricht starten und stoppen
// render-worker.js
let ctx;
let anfrageId = 0;
let farbwert = 0;
const schleife = () => {
farbwert = (farbwert + 1) % 360;
ctx.fillStyle = `hsl(${farbwert} 80% 50%)`;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
anfrageId = self.requestAnimationFrame(schleife);
};
self.onmessage = ({ data }) => {
if (data.canvas) ctx = data.canvas.getContext('2d');
if (data.befehl === 'start' && !anfrageId) {
anfrageId = self.requestAnimationFrame(schleife);
} else if (data.befehl === 'stop') {
self.cancelAnimationFrame(anfrageId);
anfrageId = 0;
console.log('Animation angehalten');
}
};
// main.js: worker.postMessage({ befehl: 'stop' });
// Wichtig · Fallstricke
- Nur in Dedicated Workers: In
SharedWorkerundServiceWorkerexistiert die Methode nicht. Vor der Nutzung prüfen:if ('requestAnimationFrame' in self) { … }, sonst aufsetTimeout(cb, 16)ausweichen. - Kompatibilität: Chromium seit Version 69, Firefox seit 97, Safari erst seit 16.4. Ältere Safari-Versionen unterstützen zudem kein
OffscreenCanvas-2D-Kontext. - Einmaliger Aufruf: Der Rückruf läuft nur einmal – für Schleifen muss er sich selbst erneut registrieren. Mehrfaches Registrieren pro Frame (z. B. in jedem
onmessage) führt zu mehreren parallelen Schleifen und doppelter Geschwindigkeit. - Drosselung: Ist das zugehörige Dokument ausgeblendet (Hintergrund-Tab), werden Frames pausiert oder stark gedrosselt. Animationslogik sollte daher mit Delta-Zeit statt mit Frame-Zählern rechnen.
- Lang laufender Code im Rückruf blockiert den Worker-Thread und verursacht Ruckeln – aber nicht mehr den Hauptthread.