CSS, HTML und Javascript mit {stil}

Javascript Animationen: window.setTimeout() und window.setInterval()

Javascript Animationen mit setTimeout Kleine animierte Monster, die aber auch erst bei einem Hover loslaufen. Und nicht in IE oder Edge.

Javascript Animationen werden mit window.setTimeout() und window.setInterval() – mit Timern – umgesetzt. Javascript Timer sind keine Events, aber fühlen sich wie Events an.

Animationen können zum Verständnis von Webseiten und Web-Apps beitragen – z.B. durch das Verlangsamen der Reaktion auf Benutzeraktionen. Oder sie sind einfach zur Unterhaltung da …

Für Animationen werden meist Libraries oder ganzen Frameworks eingesetzt, aber die Grundlagen von Animationen mit Javascript sind die Voraussetzung für das Verständnis der komplexen Logik hinter Bewegungsabläufen und Reaktionen.

Die Grundidee der Animation mit Javascript: Mach ein Element Bild für Bild durch einen weichen Übergang sichtbar oder unsichtbar, bewege es oder verändere sein Aussehen langsam oder mit einer kleinen Verzögerung. Dabei entsteht eine Sequenz von Einzelbildern oder Frames, die schnell genug aufeinander folgen, dass die Bewegung fließend und natürlich wirkt.

Framerate – Bilder pro Sekunde

Damit der Übergang weich und langsam wirkt, muss die Animation in einer Schleife mit einer Framerate ablaufen – z.B. ein Element 100 mal ein Stück bewegen, um es über eine Strecke zu transportieren.

So tragen Javascript-Animationen wie Lightboxen, weiches Scrollen oder die Accordeon-Animation von langen Navigationslisten zum Verständnis von Aktionen und Zusammenhängen bei.

Animierter Timer mit SVG und Javascript 6

setTimeout

Javascript window.setTimeout(action, timeout) gibt an, dass Anweisungen nach einer bestimmten Zeit timeout (angegeben in Millisekunden) ausgeführt werden.

let ani = setTimeout ( action, timeout );
     |                    |     |
     |                    |     +---> Zeit in Millisekunden
     |                    |           bis zum Aufruf der Anweisungen
     |                    |  
     |                    +---> Anweisung oder Funktionsaufrufe
     +---> optionale Referenz, mit der
           setTimeout abgebrochen werden kann

setTimeout(action, n) startet Anweisungen mit einer Verzögerung von timeout Millisekunden.

    time = 6;
    function countdown () {
        time -= 1;
        number.firstChild.nodeValue = time;
        if (time > 0) {
            let ani = setTimeout( countdown, 1000);
        }
    }
    countdown();

Javascript setTimeout() führt die Anweisungen exakt einmal aus (und der Timer ist nicht besonders exakt) und dann nie wieder. Darum wird setTimeout hier im Beispiel innerhalb der Funktion countdown wiederholt aufgerufen, bis die Bedingung (time > 0) nicht mehr stimmt.

setTimeout handelt asynchron

Die Verzögerung bewirkt nicht, dass die Anweisungen alle n Millisekunden aufgerufen werden, sondern dass die Funktion einmal mit einer Verzögerung von n Millisekunden aufgerufen wird.

Das komplette Javascript stopt auch nicht, um die Ausführung der Anweisungen abzuwarten. Das Script läuft weiter, denn setTimeout() ist asynchron. Das Programm wird weiter abgearbeitet, während der Timer noch schläft und auf sein vorprogrammiertes Erwachen wartet.

clearTimeout()

Um die Anweisung wiederholt durchzuführen, muss das Skript wiederholt aufgerufen werden – z.B. rekursiv wie im Beispiel oder in einer Schleife.

Die Funktion setTimeout() gibt einen einfachen Wert zurück – einen Verweis auf den Timeout. Die Referenz auf setTimeout() wird benötigt, um den Timeout abzubrechen und die Ausführung der Anweisungen zu unterbinden.

Javascript clearTimeout() cancelt den Aufruf von setTimeout().

setTimeout() wird eher selten für Animationen benutzt, sondern für einfache verzögerte Aktionen, da setTimeout() die Aktion nur einmal durchführt. Statt selber Schleifen oder rekursive Funktionen einzusetzen, greift man bei Animationen mit schrittweisen Änderungen lieber zu setInterval().

Animation mit setInterval()

Anders als mit setTimeout(action,timeout), das Anweisungen nach einer Verzögerung von timeout Millisekunden einmal ausgeführt, werden Anweisungen mit Javascript setInterval( action,interval) alle interval Millisekunden wiederholt.

  • setInterval führt die Anweisungen genau alle 1000 / 2000 … Millisekunden durch
  • setTimeout wartet 1000 / 2000 … Millisekunden, führt die Funktion aus (und braucht dafür ein paar Millisekunden) und wartet dann auf den nächsten Timeout. Die Zeitspanne zwischen zwei Frames ist also einen Tick größer als 1000 / 2000 … Millisekunden.
let id = setInterval(function() {
    /* Zeige das aktuelle Bild */
    if (/* fertig */) clearInterval(id)       
}, 10);
Einfache Animation mit setInterval
<button id="runit">Run Bunny</button>
<div id="bunny"><img … ></div>
…
document.getElementById('runit').onclick = function () {
   let left = 0; 
   let runbunny = setInterval (function () {
      left += 5;
      bunny.setAttribute('style','left:' + left + 'px');
      if (left > width) { 
         clearInterval (runbunny);
         bunny.setAttribute('style','left:0px');
      }
   }, 10);
}

setInterval mit closures

Sowohl setTimeout als auch setInterval können auch mit einer anonymen Funktion oder »Closure« aufgerufen werden. Das hält den Scriptcode besser zusammen und ersetzt eine externe Funktion.

let runbunny = setInterval (function(){…}, interval);
       +                        +             +
       |                        |             |
       +-- Referenz             |             |
                                |             +-- Verzögerung
                                |                 
                                +-- anonyme Funktion 

clearInterval

Javascript clearInterval(action,interval) regelt oder cancelt eine Funktion, die wiederholt mit einer Verzögerung aufgeweckt wird.

Eine Verzögerung von 100 Millisekunden bedeutet übrigens nicht, dass der Aufruf der Anweisungen tatsächlich alle 100 Millisekunden ausgeführt wird, sondern 100 Millisekunden, nachdem die Anweisungen durchgeführt wurden. Die Durchführung der Anweisungen kostet ebenfalls Zeit.

setInterval / setTimeout mit Parametern

Wenn die aufgerufene Funktion selber Parameter hat, könnten die Parameter auch in setInterval bzw. setTimeout übergeben werden. Diese Möglichkeit gab es immer schon, aber sie wurde nie von Internet Explorer unterstützt und geriet in Vergessenheit.

6
let optcounter = document.getElementById('optdown');
let optnumber = document.getElementById('optnumber');
let optcircle = document.getElementById('optcircle');

startbutton.onclick =
counter.onclick = function () {
   function countdown (time,hue) {
      time -= 1;
      hue += 60;
      number.firstChild.nodeValue = time;
      optcircle.setAttribute('stroke','hsl(' + hue + ',85%,60%)');
      optnumber.setAttribute('fill','hsl(' + hue + ',85%,60%)');
      if (time > 0) {
         setTimeout( countdown, 1000, time, hue);
      }
   }
   countdown(6, 30);
}

IE9 gibt noch einen Fehler zurück. Ab IE 10 können setTimeout und setInterval mit Parametern aufgerufen werden.

Die Funktion könnte auch mitsamt Parametern in Hochkommas an setInterval übergeben werden. Dann wird die Funktion – auf gleiche Weise wie eine einzelne Anweisung – evaluiert.

let timer = setInterval("slideshow(opt1,opt2)",500);

Das gilt aber als ausgesprochen unschicklich und ist unsicher (»eval«). Stattdessen kann setInterval mit einer anonymen Funktion aufgerufen werden.

let myanimation = setInterval (function () {
	… 
}, time);
timer = setInterval ( 'elem.style.display="block";', 10 );
timer = setInterval ( slideshow, 5000 )
timer = setInterval (function() { … }, 1000 );
timer = setInterval (function() { funcA (opt, opt); }, 3000 );
document.getElementById('runbaby').onclick = function () {
   let monsters = document.querySelectorAll ('.monster');
   let numMonsters = monsters.length;
   let left = [0, -40, -80];
	
   for (let i=0; i<numMonsters; i++) {
      moveMonster (monsters[i], left[i]);
   }

   function moveMonster (obj,j) {
      let repeater = setInterval (function () {
         j += 1;
         obj.setAttribute ('style','left: ' + j + 'px');
         if (j>width) {
            clearInterval(repeater);
            obj.setAttribute ('style','left: 0px');
         }
      },10);
   }
}

Easing

Das hat Animationen mit Flash so elegant gemacht, wird bei Animationen mit jQuery mitgeliefert, CSS macht es mit transition und keyframes – aber hat Javascript immer schon gefehlt: Easing – Beschleunigen und langsames Auslaufen von Bewegungen. Erst durch Easing kommt ein natürlicher Bewegungsablauf zustande.

Die meisten Javascript-Animationen verändern CSS-Eigenschaften – CSS transition und Keyframe-Animationen bringen Easing-Optionen mit.

Wenn Easing-Funktionen wie easeIn, easeInOut oder easeInOutQuart nicht via CSS oder jQuery eingebracht werden, gibt es eine Portierung des Urvaters der Easing-Funktionen (Robert Penner) von Kirupa Chinnathambi.

easeIn(currentIteration, begin, change, duration);

change = finish - begin

CSS transition statt Javascript

Die Kombination aus CSS3 transition, CSS Keyframe-Animationen und Javascript vereinfacht heute Animationen und macht komplexe Abläufe möglich. Neue Javascript Methoden wie classList triggern Aktionen, denn CSS hat nur wenige Events, die eine Animation auslösen.

Der Ruf, dass reine CSS Animationen und Transformationen performanter als Javascript wären, rührt aus jQuery-Animationen: jQuery war nicht für Animationen gedacht.