Javascript Animationen mit requestAnimationFrame

Javascript Animationen mit request Animation Frame

requestAnimationFrame ruft die Animations-Funktion vor jedem erneuten Rendern (»Refresh«) des Browserfensters auf und erzeugt weiche Übergänge von einem Frame zum nächsten. Anstelle von setInterval oder setTimeout übernimmt der Browser die Schnittstelle und optimiert das Verfahren. Animationen laufen runder, ohne Ruckeln und effizienter.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

requestAnimationFrame – Ablauf

Die Syntax von requestAnimationFrame () gleicht der Syntax von setTimeout (), da requestAnimationFrame wiederholt aufgerufen wird. Nur ein Timer oder Interval wird nicht benötigt.

function animate () {
	…
	req = requestAnimationFrame (animate);
}

let req = requestAnimationFrame (animate);

cancel.addEventListener ("click", () => {
	cancelAnimationFrame (req);
});

setTimeout und setInterval sind altgediente Techniken in Animationen mit Javascript. Sie verändern das animierte Element alle t Millisekunden. Aber diese alten Methoden sind ineffizient. Wenn der Benutzer ein anderes Tab im Browser öffnet, läuft die Animation weiter. requestAnimationFrame ist effizienter.

Wenn der Benutzer zu einem anderen Tab wechselt, kann der Browser die Animation pausieren, um die CPU weniger zu belasten. Schön für unsere mobilen Geräte …

Frame-Rate

Bei Animationen mit setTimeout oder setInterval kann das Script die aktuelle Frequenz der Frames nicht feststellen. requestAnimationFrame wird per Vorgabe 60 mal pro Sekunde aufgerufen – mehr ist selten nötig (oder besser: möglich), denn 60 Hz ist die Refresh-Rate der meisten Browser (oder Zeitrahmen von 16.7ms).

In Animationen mit requestAnimationFrame kann die Browser die Refresh-Rate reduzieren, wenn die Animation in einem Hintergrund-Tab läuft, um den Energieverbrauch zu senken. Statt einen Timeout mit einer Verzögerung von 16.7ms in einer Schleife immer wieder aufzurufen, feuern die Browser ein Event, sobald das System wieder einen Frame rendern kann.

requestAnimationFrame vs CSS-Animationen

CSS-Animationen sind mächtig, aber ihnen fehlen die Ereignisse, die eine Aktion starten oder beenden: CSS erkennt zwar ein Hovern mit der Maus, aber nicht das Touch des Fingers und Mausklicks nur auf wenigen Elementen (wie Radiobuttons). Auch bei komplexen Ketten (z.B. »starte Aktion, wenn eine andere Aktion abgelaufen ist«) bleibt CSS außen vor. Für Aktionen wie Stop, Pause und Resume eignet sich requestAnimationFrame besser.

Für komplexe Animationen haben wir heute auch das Web Animation Api, das eine CSS-ähnliche Notation nutzt, und den Start einer Aktion abhängig vom Zustand anderer Aktionen macht. Dennoch bleibt requestAnimationFrame die Wahl bei canvas-Animationen, denn im canvas-Element herrscht nicht das Document Object Model, sondern canvas basiert auf Pixeln.

Syntax

Wenn der Screen neu gerendert werden soll, wird requestAnimationFrame mit dem Name der Funktion aufgerufen, in der die Veränderungen und Berechnungen durchgeführt werden (aka Callback).

<div class="flex stopper">
	<img id="rad" src="rad.svg" width="200" height="200">
	<img id="eck" src="eck.svg" width="40" height="40">
</div>

<div id="lucky"></div>

<button id="drehen">DREHEN!</button> 
<button id="stopp" disabled="disabled">STOPP</button>
const obj = {
	on: document.querySelector ("#drehen"),
	off: document.querySelector ("#stopp"),
	rad: document.querySelector ("#rad"),
	lucky: document.querySelector ("#lucky"),
	deg: 0,
	repeater: null
}

Alle globalen Variablen der Animation in ein Objekt gesteckt, um nicht mit anderen Variablen zu kollidieren. deg steht für die Umdrehung des Rads in Grad, repeater ist die Referenz auf requestAnimationFrame (), um die Animation zu beenden.

function turn () {
	obj.deg++;
	if (obj.deg > 359) obj.deg = 0;
	obj.rad.style.transform = "rotate(" + obj.deg + "deg)";
	obj.repeater = requestAnimationFrame (turn);
}

obj.on.addEventListener ("click", function () {
	obj.off.removeAttribute ("disabled");
	obj.on.setAttribute ("disabled", "true");
	obj.repeater = requestAnimationFrame (turn);
});

requestAnimationFrame ist keine Schleife und kein Timer, sondern die Funktion turn () muss jedes Mal aufgerufen werden, wenn die Animation etwas ändert.

Der Klick auf DREHEN weckt das Zahlenrad. Der Button DREHEN wird deaktiviert, damit er nicht während der Animation ein weiteres Mal geklickt werden kann. Das disabled-Attribut des STOPP-Buttons kann weg, damit das Rad jederzeit angehalten werden kann.

STOPP beendet die Animation mit cancelAnimationFrame(obj.repeater). Jetzt nur noch mit den aktuellen Wert von deg berechnen, welche Zahl unter dem Dreieck liegt.

obj.off.addEventListener ("click", function () {
	cancelAnimationFrame(obj.repeater);
	obj.on.removeAttribute ("disabled")
	
	switch (true) {
		case (obj.deg < 45):
			obj.lucky.textContent = "5";
			break;
		…
	}
});

Das Manko der Javascript-Animationen

Javascript hat kein Easing von Haus aus, wie wir es von CSS-Transitions und -Animationen kennen. Kein langsames Anlaufen oder Losschießen, kein weiches oder abruptes Ausbremsen. Diese Animationen wirken wie ein Metronom: Tick, Tack, Tick, Tack. Erst jQuery hat diese Mechanismen, die für natürliche Bewegungsabläufe sorgen, eingebracht. Das Easing in jQuery beruht auf den Algorithmen von Robert Penner, die es auf Github für Javascript gibt.

Wer also auf requestAnimationFrame für Animationen – z.B. für Canvas-Animationen – setzt, sollte sich Improved Easing Functions auf Josh On Design ansehen. Umgesetzt auf Introduction to Easing in JavaScript für Javascript.

requestAnimationFrame mit Parametern

request Animation Frame wird mit einer Callback-Funktion aufgerufen. Wenn Parameter übergeben werden, kann die Callback Function auch eine anonyme Funktion sein, die wiederum die benötigten Parameter enthält.

function moveCurtain (curt, height) {
    easing = easeInOutQuart(step, height, -300, 90);
    step++;

    if (easing < 1) {
        cancelAnimationFrame (repeater);
        step = 0;
    } else {
        curt.setAttribute('height',easing);
        repeater = requestAnimationFrame (function () {
            moveCurtain (curt, easing);
        });
    }
}

function.bind() ist ein weiterer Mechanismus, um requestAnimationFrame mit Parametern aufzurufen. bind erzeugt eine neue Funktion, die this als ersten Parameter an bind() übergibt.

Animiert einfache Wellen in versch. Blautönen
function move ( elem, timestamp) {
	
	// Berechne left und y
	
	elem.setAttribute("transform", "translate(" + left + " " + y +")");
	requestAnimationFrame (move.bind (0,elem));
};

for (let i=0; i<waves.length; i++) {
	requestAnimationFrame(move.bind (0, waves[i]));
}

Javascript animate () – Web Animation API

Mit animate () ist neben setTimeout, setInterval und requestAnimationFrame eine weitere Technik für Animationen auf den Plan getreten. Das Web Animation API mit animate() lehnt sich an CSS-Keyframes-Animationen an und eignet sich besonders für die Ablaufsteuerung von komplexen Objekten. requestAnimationFrame bleibt weiterhin die Basis für Canvas-Animationen.