Animation mit requestAnimationFrame

Sehr tolle Erklärung in deutsch MediaEvent
Bei Selfhtml Wiki wird der Schwerpunkt auf den Timestamp und Zeitpunkt Berechnungen gelegt.
Siehe auch die alternative Möglichkeit Web Animation Api um CSS Eigenschaften zu animieren.

Hat man mehrere Anweisungen, die in einer oder mehreren Zeitschleifen ausgeführt werden und zusätzlich noch weitere Events durch Usereingaben, könnte das zu Verzögerungen führen, da die CPU überlastet wird durch das ständige Rendern der Ansicht.

requestAnimationFrame liefert die Lösung, denn das Render- Intervall wird vom Browser gesteuert. Hiermit wird alles gleichzeitig und so effizient wie möglich gerendert. Außerdem werden die Aktualisierung in einem anderen Tab-Browserfenster gestoppt.Bei setInterval oder setTimeout laufen die Animationen in allen Tabs des Browsers weiter.

HTML

<input name="meintextfeld" type="text" id="meintextfeld" class="textfeld">

Script

var i=1;

function zahlen() {
    i++;
    document.getElementById("meintextfeld").value = i;
}

function renderScene(){
    requestAnimationFrame(renderScene);
    zahlen();
}
renderScene();

Beispiel / Farbanination Beispiel / Camel Beispiel (weitere Camel Beispiele hier)

Pendelbewegung

Pendelbewegung In meinen alten Animate Tipps (ehemals Flash Actionscript3) finden sich viele Beispiele für programmierte Bewegungen und animierte Linien, die man leicht auf CSS oder Canvas übertragen kann.

Im Beispiel wird der Wert multi bei jedem Frame um 0.03 erhöht. Durch Math.sin() wird damit ein Wert übergeben der zwischen -1 und +1 , in einer Sinuskurve hoch und runter geht.

rota = Math.sin(multi += 0.03) * 90;
pendel.style.transform = "rotate(" + rota + "deg)";      

Kreisbewegung

Kreisbewegung Beispiel Sinus und Cosinus kann man auch dazu einsetzen eine Kreisbewegung mit x- und y-Koordinate zu programmieren, da Cosinus entgegengesetzt zu Sinus verläuft.

i wird ständig erhöht. Der Wert bestimmt die Drehgeschwindigkeit. Zum Radius wird ein Versatzwert angegeben, der den Mittelpunkt der Drehung auf den Mittelpunkt der Seite legt. Der Wert -50 wird eingesetzt, da sonst der Drehpunkt des Bildes in der linken oberen Ecke des Bildes liegen würde.

i += 0.01;
posY = Math.sin(i) * radius + window.innerHeight/2 -50; 
posX = Math.cos(i) * radius + window.innerWidth/2 -50;                
moon.style.left =  posX + "px";
moon.style.top =  posY + "px";

Kreisbewegung mit Timestamp Klickt man im obigen Beisiel mehrmals auf den Startbutton, wird die Drehbewegung schneller, weil der Wert von i doppelt so schnell ansteigt. Setzt man für i den Bruchteil des Timestamp ein, geschieht das nicht, weil der Timestamp in allen requestAnimationFrames gleich ist. i = timestamp / 2000; mehr dazu unten.

Sinusbewegung

Flieger In diesem Beispiel bewegt sich der Flieger von rechts nach links, solange seine Position größer ist als -250, anderfalls wird er an den rechten Rand gesetzt. Die vertikale Position yPos wird mit Math.sin() berechnet, anhand eines Wertes der sich ständig erhöht, dem Timestamp. Sinus liefert immer eine Zahl zwischen -1 und 1. Multipliziert man den Wert bekommt man eine schöne Sinuskurvenbewegung.

i = timestamp / 2000;
posY = Math.sin(i) * radius + 200;
if (posX < -250) {
    posX = window.innerWidth + 20;
} else {
    posX -= 2;
}
flyer.style.left = posX + "px";
flyer.style.top = posY + "px";

 


RequestAnimationFrame starten und stoppen

Will man requestAnimationFrame starten und stoppen, muss man es einer Variablen übergeben.

requestId = window.requestAnimationFrame(loop);

Dann kann man sie folgendermaßen über die Variable löschen:

window.cancelAnimationFrame(requestId);

Im folgenden habe ich mehrere Beispiele erstellt, die Schritt für Schritt immer komplexer werden. Bitte Quellcode untersuchen

Beispiel 1, Beispiel 2, Beispiel 3, Beispiel 4, Beispiel 5, Beispiel 6

Info des letzten Beispiels Animation starten und stoppen

Im folgenden gibt es einen Button der zwischen starten und stoppen hin und her schaltet. Das geschieht mit Hilfe der Variablen requestId die mit undefined intialisiert wird. In der Funktion startStop() wird in einer if Struktur diese Variable abgefragt. Auch wenn undefined kein boolscher Wert ist, so liefert undefined doch den Wert false. siehe dazu Boolscher Datentyp Casting

Durch das Ausrufezeichen am Anfang wird der Wert false in true umgekehrt und somit wird requestId das Objekt requestAnimationFrame(renderScene) zugwiesen. Dadurch wird renderScene() in Schleife aufgerufen.

Klickt man das nächste mal auf den Button liefert requestId in der if Struktur true und somit wird unter else, die Animation gestoppt und requestId wieder auf undefined gesetzt. F12 console.log(Boolean(requesId));

Weist man der Funktion, die mit requestAnimationFrame aufgerufen wird, einen Parameter zu, so liefert dieser einen Timestamp.

Beispiel 6

HTML

<input type="btn" value="Start Stop">  
<div id="output"></div>

Script

        var btn = document.getElementById("btn");
        var output = document.getElementById("output");     
        btn.addEventListener("click", startStop);
        
        var requestId = undefined;
        
        function startStop(){
               if (!requestId) {
                   requestId = window.requestAnimationFrame(renderScene);
                   btn.textContent = "Stop";
                }else{
                    window.cancelAnimationFrame(requestId);
                    requestId = undefined; 
                    btn.textContent = "Start";
                }
        }

        function renderScene(time){
             requestId = window.requestAnimationFrame(renderScene); 
            output.innerHTML = time;     
        }

Timestamp

siehe auch

Beispiel 1 Timestamp Schrittdauer

Der Parameter den man der Callback-Funktion die mit requestAnimationFrame aufgerufen wird übergibt liefert einen timestamp. Das ist die Zeit in Millisekunden, die seit einem Bezugszeitpunkt (z.B. dem Laden der Seite) vergangen ist. Mit diesem Timestamp kann man die Zeit berechnen, die seit dem Start der Callback Funktion vergangen ist, oder man ermittelt die Zeit zwischen den einzelnen Aufrufen oder Frames.

Im folgendem Beispiel wird in der Variablen startZeit der Startzeitpunkt hinterlegt, wenn die Callback-Funktion animate() gestartet wird. Wenn requestAnimationFrame(animate) gestoppt wird, wird auch startZeit wieder auf undefined gesetzt, so dass beim erneuten Starten startZeit wieder den neuen Wert bekommt. Diese startZeit wird von dem aktuellen timestamp subtrahiert und man erhält quasi die aktuelle Zeit oder Dauer der Schleife.

Die Variable vorigeZeit bekommt in jedem Frame den aktuellen timestamp. Bevor man im nächsten Frame den Wert wieder neu zuweist, hat man den Timestamp des vorigen Frames. Die Subtraktion vom aktuellen Timestamp ergibt die Zeit zwischen 2 Frames timestamp - vorigeZeit

Weil der Timestamp gebraucht wird, wird animate() nicht direkt über den Startbutton aufgerufen.

HTML

<button id="startBtn">Start</button>
<button id="stopBtn">Stop</button>
<p id="output"></p>

Javascript

        var startBtn = document.getElementById("startBtn");
        var stopBtn = document.getElementById("stopBtn");
        var output = document.getElementById("output");      
        var requestId;
        var startZeit = undefined;
        var vorigeZeit = undefined;
        
       startBtn.addEventListener('click', startAnimation);
       stopBtn.addEventListener('click', stopAnimation);     


    function animate(timestamp) {
        if (startZeit === undefined) {    // beim ersten Aufruf ist startZeit undefined
            startZeit = timestamp;        // Dann timestamp als Startzeit  speichern
        }
        var schrittDauer = timestamp - vorigeZeit; //vorigeZeit ist timestamp des vorigen Frames
        var gesamtZeit = Math.floor(timestamp - startZeit); //gesamtZeit seit Start
        vorigeZeit = timestamp; //wird beim nächsten Aufruf gebraucht.
        requestID = requestAnimationFrame(animate); 
        output.innerHTML = schrittDauer+"<br>"+gesamtZeit;
    }
    
    function startAnimation(){
        requestID = window.requestAnimationFrame(animate);
    }
        
    function stopAnimation(){
        if (requestID) {
            cancelAnimationFrame(requestID);
        } 
        requestID = undefined;
        startZeit = undefined;
    }

requestAnimationFrame in einem Objekt nutzen

Wenn man requestAnimationFrame als Methode in einem Objekt nutzen will, muss man eine Lösung finden auf this zuzugreifen.

In folgenden Beispielen erstelle ich ein Objekt, welches von Image erbt und mit requestAnimationFrame animiert wird. Es ist ähnlich meiner Car Animationen.

Beispiel7 / Beispiel8

Hier noch ein weiteres Beispiels welches eine Abwandlung der Serie benutzerdefiniertes HTML Element ist

Beispiel Camel


weiter

Javascript Tipps