Javascript für CSS-Animationen

Animierte Roboter: CSS Animation restart und animation play state

Ohne besonderes auslösendes Event (z.B. hover oder focus) starten CSS Keyframes-Animationen automatisch, wenn das Dokument im Browserfenster geladen wird.

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

Keyframes-Animation erneut starten

CSS bietet nur wenige Events, die eine Animation auslösen und kann Animationen nur begrenzt neu starten. Erst Javascript fügt komplexe Animationen zusammen und kann CSS Keyframes Animationen neu starten oder die Animation auslösen, wenn die Animation in den Viewport kommt.

CSS-Animationen haben ihre Grenzen, die aber schon mit einer kleinen Dosis Javascript aufgehoben werden. Eine der Grenzen von CSS Keyframes-Animationen:

Wenn eine CSS-Animation beim Laden der Seite ausgeführt und abgelaufen ist, kann sie nicht ohne Weiteres erneut gestartet werden.

.animated-sky {
   height: 1360px;
   background-image: 
      linear-gradient(to bottom, hsl(200,60%,25%) 0%, 
                           hsl(200,60%,55%) 20%, 
                           hsl(200,60%,90%) 30%,
                           hsl(200,60%,90%) 50%,
                           hsl(200,60%,25%) 80%,
                           hsl(200,60%,55%) 100%); 
   animation: daytime 15s;
}

@keyframes daytime {
   from { top: 0}
   to   { top: -1000px}
}

/* So funktioniert der Neustart der Animation nicht */
.container:hover .animated-sky {
   animation: daytime 15s;
}

Um die Animation jetzt bei einem :hover neu zu starten, braucht sie eine weitere, identische @keyframes-Regel mit einem anderen Namen.

@keyframes night {
	from { top: 0}
	to   { top: -1000px}
}

.touch .animated-sky,
.container:hover .animated-sky {
	animation: night 15s;
}

Von Hover zu Touchstart

Das Hovern der Maus hat keine Entsprechung auf Touchscreens: Es gibt kein :touch. Es ist nicht einmal möglich, die Reaktion vorauszusagen: Vielleicht reagiert der Touchscreen auf CSS :hover, vielleicht nicht. Vielleicht friert der Effekt auf dem Touchscreen ein. :hover ist für Benutzer mit der Maus immer noch ein intuitiver Auslöser, aber :hover allein reicht nicht.

<script>
const container = document.querySelector(".container");
container.ontouchstart = function () {
   this.classList.add("touch");
}
</script>

Dafür war also die kleine Zeile .touch .animated-sky im CSS der Animation …

Restart: CSS-Animation mit Javascript erneut starten

»In order to restart an animation, it must be removed then reapplied.« – CSS Animations Level 1

Die einfachste Technik zum erneuten Start einer CSS Keyframes-Animation ist wohl das Löschen und erneute Einfügen der animierten Elemente. Javascript cloneNode kopiert ein Element mit allen Child Nodes:

CSS bewegt
CSS rotiert
CSS blendet ein
CSS blendet aus
restart

Eine ganz einfache CSS-Animation:

HTML Markup der Animation
<div class="textani">
   <div class="ani t0">CSS bewegt</div>
   <div class="ani t1">CSS rotiert</div>
   <div class="ani t2">CSS blendet ein</div>
   <div class="ani t3">CSS blendet aus</div>
   <div id="trestart">restart</div>
</div>
Das CSS der Keyframes-Animation:
.textani { 
   width: 300px; height: 200px; 
   overflow: hidden; position: relative; 
}

.ani { position: absolute; }
	
.t0 { top: 30px; animation: tmovedown 2s }
.t1 { top: 60px; animation: tmoveup 2s }
.t2 { top: 90px; animation: tmoveleft 2s }
.t3 { top: 120px; animation: tmoveright 2s }

@keyframes tmovedown {
    from { transform: translateY(-200px)}
    to { transform: translateY(0px)}
}
	
#trestart { 
    position: absolute; 
    bottom:0; right: 0; 
}
Javascript: CSS Keyframes-Animation restart
<script>
document.getElementById('trestart').onclick = function () {
    const clone = document.querySelector('.textani').cloneNode();
    const ani = document.querySelectorAll('.ani');
    const len = ani.length;
    for (let i=0; i<len; i++) {
        let clone = ani[i].cloneNode(true);
        ani[i].parentNode.replaceChild(clone, ani[i]);
    }
}
</script>

Javascript startet die komplette Animation bei einem Click auf den Button durch Klonen aller Elemente der Animation und Ersetzen des Originals durch den Klone – also Löschen und Clone einsetzen.

Das wars im einfachsten Fall.

CSS Animation mit animation-play-state starten und stoppen

animation-play-state : paused versetzt eine Animation in eine Wartezustand, bis ein Event mit animation-play-state: running die Sequenz der Keyframes auslöst.

Eine Animation mit animation-play-state: paused wird zwar schon beim Laden der Seite gestartet, sie pausiert aber sofort.

#banner { 
    position: relative; 
}

.ball { 
    position: absolute;
    animation: rolerball 4s alternate infinite paused; 
}
 
@keyframes rolerball {
    from {transform: translateX(0) rotate(0deg)}
    to {transform: translateX(200%) rotate(720deg)}
}

Wird die Animation durch :hover gestartet, läuft sie solange die Maus über dem Element hovert. Sobald die Maus das animerte Element verlässt, springt das Element ruckzuck abrupt zurück auf seine Position vor der Animation.

In vielen Situationen wirkt es natürlicher, wenn die Animation an der Stelle stehenbleibt, an der die Maus das Element verlässt.

let banner = document.querySelector('#banner');

banner.onmouseover = 
banner.ontouchstart = function (evt) {
    this.children[0].style.animationPlayState = 'running';
}
banner.onmouseout = 
banner.ontouchend = function (evt) {
    this.children[0].style.animationPlayState = 'paused';
}

Das spielt die Animation ab, solange die Maus über dem Banner hovert. Verlässt sie das Banner, pausiert die Animation und wird beim nächsten Hovern an dieser Stelle wieder aufgenommen.

Starten mit mouseover und touch

Der CSS-Pseudo-Selektor :hover löst eine CSS-transition oder Keyframe-Animation aus. Auf Touch-Screens gibt es kein zuverlässiges hover. Das touch-Event gibt es wiederum nicht für's CSS.

Das lösen ein paar Zeilen Javascript:

let box = document.querySelectorAll('.box');
for (let i=0; i<box.length; i++) {
	box[i].onmouseover = 
	box[i].ontouch = function () {
		this.classList.add('toggle');
		this.firstElementChild.setAttribute('style','opacity:1');
	}
	box[i].onmouseout = 
	box[i].ontouchend = function () {
		this.classList.remove('toggle');
		this.firstElementChild.setAttribute('style','opacity:0.3');
	}
}

Javascript classList schaltet eine Klasse hinzu oder entfernt sie. classList wird allerdings erst ab IE 10 unterstützt. Wenn IE 9 noch mitgezogen werden muss, gibt es ein classList-Polyfil auf Github.

CSS Animation im Browserfester sichtbar?

Eine Animation starten, wenn sie in den Viewport kommt – das war früher das Ding für weit ausholende Plugins oder Libraries wie jQuery. Der Javascript Intersection Observer wirkt in allen modernen Browsern (außer IE11, der nicht mehr modern und ad acta gelegt ist), ist ein Leichtgewicht und belastet das Scrollen nicht.

CSS Animation im Browserfenster sichtbar? css transition

Die Animation selbst ruht auf einer einfachen CSS-Animation.

.stift{
    animation: shift 3s;
}

@keyframes shift {
    from { transform: translateY(600px) }
    to   { transform: translateY(0px) }
}

Der Stift hat allerdings keine CSS-Klasse stift – die kommt erst später ins Spiel.

<div class="inview">
	…
	<g id="stift">
	…
	</g>
	…
</div>

Das Script beobachtet das Element mit der CSS-Klasse inview (let inview = document.querySelector(".inview")), legt die Optionen für den Start der Animation fest: root: null, rootMargin: "0px". threshold ist die Überschneidung des Elements mit dem sichtbaren Ausschnitt des Browserfensters. Mit threshold=1 würde die Animation erst starten, wenn das Element vollständig zu sehen ist.

Erst wenn das Element inview ins Browserfenster gescrollt wird (observed.isIntersecting), fügt das Script dem Stift die Klasse .stift hinzu.

<script>
let inview = document.querySelector(".inview");

const options = {
	root: null,
	rootMargin: "0px",
	threshold: 0.7,
};

const callback = function (entries, observer) {
	let observed = entries[0];
	if (observed.isIntersecting) {
		document.querySelector("#stift").classList.add("stift");
	}
}

const observer = new IntersectionObserver (callback, options);

if (inview) {
	observer.observe (inview);
}
</script>

IE / Edge: SVG-Animation mit CSS Keyframes

Animationen von SVG-Elementen mit CSS Keyframes (und Transitions ebenso) funktionieren in IE bis einschließlich IE11 nur bei CSS-Eigenschaften wie opacity und fill, nicht aber mit transform und rotate. Dieser Einschränkung unterliegen auch SVG-Animationen mit jQuery.

SVG mit @keyframes

Mit Chrome, Safari, Firefox und auch mit Microsoft Edge funktionieren CSS Transitions und Keyframes-Animationen – auch in externen CSS-Dateien und können viele Javascript-Animationen ersetzen.