Web Animations/Animationen steuern
Einer der Nachteile von CSS-Animation und Transition ist das Fehlen von Möglichkeiten zur Steuerung.
Dieses Tutorial zeigt, wie man mit der Web Animations API Animationen durch den Benutzer kontrollieren lassen kann. Diese API findet sich nativ in allen Browsern, externe Bibliotheken wie GSAP oder Anime.js werden nicht benötigt.
Dabei animieren wir nicht nur Gimmicks und Show-Effekte, sondern aussagekräftige Inhalte und die Aufmerksamkeit des Benutzers.
Inhaltsverzeichnis
CSS-Animation
Das letzte Beispiel aus dem CSS-Animation-Tutorial zeigt einen Loading-Spinner, der bei :hover pausiert:
#gear {
animation: run 3500ms linear infinite;
}
#gear:hover {
animation-play-state: paused;
}
@keyframes run {
0% {
rotate: 0;
}
100% {
rotate: 1turn;
}
}
Die Eigenschaft animation-play-state wird beim Überfahren mit der Maus auf animation-play-state: paused; gesetzt. Die Animation pausiert, bis sich die Maus wieder entfernt.
Weitere Steuerungsmöglichkeiten wie eine Änderung der Richtung oder Geschwindigkeit sind nicht möglich.
Und das Gleiche mit JavaScript
Gerade, wenn Nutzer Einfluss nehmen, sind Tricks wie das Selektieren mit target ein Verstoß gegen die Trennung von Inhalt, Präsentation und Verhalten. Deshalb nehmen wir jetzt JavaScript zu Hilfe und bauen unseren Loading-Spinner nach:
const animation = gear.animate(
[
{ rotate: "0" },
{ rotate: "1turn" }
],
{
duration: 3500,
iterations: Infinity
}
);
playBtn.addEventListener("click", () => {
animation.play();
});
pauseBtn.addEventListener("click", () => {
animation.pause();
});
In diesem Beispiel wird in einem script-Bereich eine Konstante animation definiert. Sie ruft für gear die Element.animate(keyframes, options)-Methode auf, die ein Animations-Objekt erzeugt.
Dies enthält zwei Parameter:
-
keyframesentsprechen den @keyframes von CSS- Es enthält einen Array der CSS-Eigenschaft(en) – hier nur rotate und die zu verändernden Werte.
(Alternativ wäre auch eine Objektschreibweise möglich, in der CSS-Eigenschaften einen Array der zu animierenden Werte enthalten.)
- Es enthält einen Array der CSS-Eigenschaft(en) – hier nur rotate und die zu verändernden Werte.
- In den
optionsfinden sich die- duration, die Dauer in ms
- iterations, die Anzahl der Wiederholungen.
Wie in CSS-Animation startet eine Web Animation sofort beim Laden. Falls man das nicht will, muss ein animation.pause(); eingefügt werden.
- In JS sind nur Zeitangaben im
msmöglich, in CSS gegen sowohlmsals auchs. - In JS lautet das Schlüsswort für unendliche Wiederholungen
Infinity– in CSSinfinite - In JS verlaufen Animation standardmäßig
linear, in CSS ist der Default für animation-timing-functionease-in. - CSS-Eigenschaften mit Bindestrich werden in JavaScript in CamelCase notiert.
Pausieren bei :hover?
Es gibt kein einfaches Abfragen des :hover-Selektors in JavaScript. Stattdessen können die Maus-Events überprüft werden:
gear.addEventListener("mouseenter", () => animation.pause());
gear.addEventListener("mouseleave", () => animation.play());
Mit AddEventListener werden die mouseenter und mouseleave-Events belauscht. Feuern sie, wird die Animation pausiert oder wieder aufgenommen.
Cards mit WAAPI
Ein klassisches Problem bei CSS-Animationen ist ihre Einbahnstraße.
Sehr oft wünscht man sich: Die gleiche Animation soll vorwärts und rückwärts laufen können.
Mit reinem CSS führt das schnell zu doppeltem Code, mehreren @keyframes oder komplizierten Workarounds.
Ein typisches Beispiel ist eine Karte, die zu einer Detailansicht erweitert wird:
- Erster Klick → die Karte klappt auf
- Zweiter Klick → sie schließt sich wieder
Idealerweise mit derselben Animation – nur rückwärts
Genau an diesem Punkt zeigt die Web Animations API ihre Stärke:
Animationen sind keine starren Deklarationen mehr, sondern steuerbare Objekte, deren Richtung jederzeit geändert werden kann.
Für ein Kräuterlexikon wollen wir Cards anlegen, bei denen der Nutzer auf Wunsch weitere Informationen erhalten kann:
<div class="card">
<header>
<h2>Basilikum<span class="icon">🌱</span></h2>
</header>
<p class="card-teaser"> Ein aromatisches Küchenkraut, perfekt für … </p>
<button class="toggle" aria-expanded="false">Mehr erfahren</button>
<section class="card-details" hidden>
<h3>Pflegehinweise</h3>
…
</section>
</div>
<p id="controls">
<button id="playBtn">Los</button>
<button id="pauseBtn">Pause</button>
<button id="reverseBtn">Zurück</button>
<!-- Nur zu Demo-Zwecken -->
</p>
Wir haben eine Karte mit header, teaser und einem button, der eine section mit Pfleghinweisen aufklappen kann. Sie ist anfänglich mit dem hidden-Attribut ausgeblendet.
Die Karte ist immer dieselbe – nur ihr Informationsgrad verändert sich.
Die Buttons sind eigentlich „unnatürlich“ für echte UIs, hier machen sie die Zeit sichtbar!
details.hidden = false;
const detailsAnimation = details.animate(
[
{ opacity: 0, translate: "0 1em" },
{ opacity: 1, translate: "0 0" }
],
{
duration: 300,
easing: "ease",
fill: "both",
delay: 150
}
);
detailsAnimation.pause();
Das hidden-Attribut wird nicht animiert, sondern nur auf false gesetzt. Stattdessen wird ein Animationsobjekt angelegt, dass im keyframe-Objekt die opacity auf 0 setzt und den Textinhalt mit translate um 1em verschiebt.
Sobald das Animations-Objekt mit detailsAnimation.play(); aufgerufen wird, werden die Werte für opacity und translate animiert und der Textinhalt erscheint.
Wir haben kein Klassen-Geschiebe von button und .card-details, keine doppelten keyframes, sondern bewegen uns auf demselben Zeitstrahl vor und zurück.
Die sorgt für eine bessere separation of concerns:
- Layout → CSS (width, aspect-ratio)
- Bewegung → WAAPI
- Zustände → JS
Sowohl die Keyframes als auch die Timing Options werden in Literal-Schreibweise notiert. Die einzelnen Werte werden durch Kommata getrennt; am Ende einer Aufzählung darf aber kein Komma mehr stehen.
Dieser Umstand sorgt immer wieder für Fehlerquellen und sollte deshalb von jedem Script-Autor jedes Mal peinlich genau überprüft werden.
Wenn man einfach nur Inhalte aufklappen will, eignet sich ein details-Element besser. In unserem Beispiel wollen wir aber denselben Übergang vorwärts und rückwärts abspielen, pausieren und jederzeit fortsetzen können.
Unterbrechung und Zustandsrobustheit
Bisher funktioniert die Karte hervorragend, wenn sich der Benutzer korrekt verhält:
Klicken > Warten > Erneut klicken
Echte Benutzer tun das jedoch nicht! Sobald jemand zweimal schnell hintereinander oder während der Animation klickt, beginnen CSS-Übergänge instabil zu werden. Genau hier glänzt WAAPI.
ToDo (weitere ToDos)
Fazit
Mit diesem Script haben wir einen Player, der eine Animation abspielen, auf Wunsch aber auch stoppen oder rückwärts laufen lassen kann. Im nächsten Kapitel wollen wir anstelle eines Karussells die Bilder nacheinander fließend einblenden.