Signatur
Beschreibung
window.cancelAnimationFrame() ist das Gegenstück zu window.requestAnimationFrame(). Jeder Aufruf von requestAnimationFrame() gibt eine ganzzahlige ID zurück. Mit dieser ID lässt sich der geplante Callback wieder aus der Warteschlange nehmen, solange er noch nicht ausgeführt wurde.
Gebraucht wird die Methode vor allem, um Animationsschleifen anzuhalten, z. B. wenn der Nutzer auf „Stopp“ klickt, eine Komponente aus dem DOM entfernt wird (Aufräumen in Frameworks wie React, Vue oder Web Components) oder eine neue Animation die alte ersetzen soll. Ohne Abbruch laufen Schleifen weiter, verbrauchen CPU/Akku und greifen womöglich auf DOM-Elemente zu, die es nicht mehr gibt.
Wichtig: Bei einer rekursiven Schleife plant jeder Frame einen neuen Callback mit einer neuen ID. Du musst also immer die zuletzt zurückgegebene ID speichern und abbrechen. Die ID eines Callbacks, der schon gelaufen ist, abzubrechen, bringt nichts.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $requestID Pflicht | number | Die ID, die der Aufruf von window.requestAnimationFrame() zurückgegeben hat, der abgebrochen werden soll. Eine ungültige oder schon verbrauchte ID wird still ignoriert. |
Rückgabewert
undefined zurück.Beispiele
Einzelnen geplanten Frame abbrechen
const id = requestAnimationFrame(() => {
console.log('Wird nie ausgegeben');
});
// Noch vor dem nächsten Neuzeichnen abbrechen
cancelAnimationFrame(id);
console.log('Frame abgebrochen, ID:', typeof id);
Animationsschleife mit Start/Stopp-Buttons
const box = document.querySelector('#box');
const startBtn = document.querySelector('#start');
const stopBtn = document.querySelector('#stop');
let rafId = null;
let x = 0;
const step = () => {
x = (x + 2) % 400;
box.style.transform = `translateX(${x}px)`;
// Immer die NEUESTE ID speichern
rafId = requestAnimationFrame(step);
};
startBtn.addEventListener('click', () => {
if (rafId === null) rafId = requestAnimationFrame(step);
});
stopBtn.addEventListener('click', () => {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
});
Zeitlich begrenzte Animation mit Abbruchfunktion
const animate = (durationMs, onFrame) => {
let id;
const start = performance.now();
const tick = (now) => {
const progress = Math.min((now - start) / durationMs, 1);
onFrame(progress);
if (progress < 1) id = requestAnimationFrame(tick);
};
id = requestAnimationFrame(tick);
// Rückgabe: Funktion zum Abbrechen
return () => cancelAnimationFrame(id);
};
const cancel = animate(2000, (p) => {
document.body.style.opacity = String(1 - p);
});
// Nach 500 ms abbrechen, z. B. weil der Nutzer weiternavigiert
setTimeout(() => {
cancel();
console.log('Animation abgebrochen');
}, 500);
Aufräumen in einer Web Component
class Spinner extends HTMLElement {
#rafId = 0;
#angle = 0;
connectedCallback() {
const loop = () => {
this.#angle = (this.#angle + 4) % 360;
this.style.transform = `rotate(${this.#angle}deg)`;
this.#rafId = requestAnimationFrame(loop);
};
this.#rafId = requestAnimationFrame(loop);
}
disconnectedCallback() {
// Schleife stoppen, wenn das Element entfernt wird
cancelAnimationFrame(this.#rafId);
}
}
customElements.define('x-spinner', Spinner);
// Wichtig · Fallstricke
- Neueste ID speichern: In rekursiven Schleifen ändert sich die ID mit jedem Frame. Wer nur die erste ID speichert, kann die Schleife nicht mehr stoppen.
- Abbruch innerhalb des Callbacks: Ruft ein laufender Callback
cancelAnimationFrame()für seine eigene ID auf, passiert nichts, denn er läuft schon. Um die Schleife zu beenden, plane einfach keinen weiteren Frame (z. B. über ein Flag). - Keine Fehler: Ungültige IDs (
undefined,0, verbrauchte IDs) werfen keine Ausnahme. Tippfehler oder falsch gespeicherte IDs bleiben deshalb leicht unbemerkt. - Die IDs gehören zum jeweiligen Fenster bzw. Worker. Eine ID aus einem iframe lässt sich nicht im Elternfenster abbrechen.
- Kompatibilität: Wird von allen modernen Browsern unterstützt, auch in Dedicated Workers (zusammen mit
OffscreenCanvas). Präfixe wiewebkitCancelAnimationFrameodermozCancelAnimationFramebraucht man nur noch für sehr alte Browser. In Node.js gibt es die Methode nicht. - In Hintergrund-Tabs pausieren Browser die
requestAnimationFrame-Callbacks. Ein Abbruch ist dort trotzdem nötig, damit die Schleife nach der Rückkehr zum Tab nicht wieder anläuft.