Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Verwendung der Web Animations API

Die Web Animations API ermöglicht es uns, Animationen zu erstellen und deren Wiedergabe mit JavaScript zu steuern. Dieser Artikel wird Sie mit unterhaltsamen Demos und Tutorials, die von "Alice im Wunderland" inspiriert sind, in die richtige Richtung lenken.

Lernen Sie die Web Animations API kennen

Die Web Animations API öffnet das Animations-Engine des Browsers für Entwickler und Manipulationen durch JavaScript. Diese API wurde entwickelt, um sowohl CSS-Animationen als auch CSS-Übergänge zu implementieren und lässt Raum für zukünftige Animationseffekte. Es ist eine der leistungsfähigsten Möglichkeiten, im Web zu animieren, da der Browser seine eigenen internen Optimierungen ohne Tricks, Zwang oder Window.requestAnimationFrame() vornehmen kann.

Mit der Web Animations API können wir interaktive Animationen aus Stylesheets nach JavaScript verlagern und Präsentation von Verhalten trennen. Wir müssen uns nicht mehr auf DOM-lastige Techniken wie das Schreiben von CSS-Eigenschaften und das Scoping von Klassen auf Elemente verlassen, um die Wiedergaberichtung zu steuern. Und im Gegensatz zu rein deklarativem CSS ermöglicht JavaScript es uns, Werte von Eigenschaften bis hin zu Dauern dynamisch festzulegen. Für den Aufbau benutzerdefinierter Animationsbibliotheken und die Erstellung interaktiver Animationen könnte die Web Animations API das perfekte Werkzeug sein. Schauen wir uns an, was sie leisten kann!

Diese Seite enthält eine Reihe von Beispielen, die die Web Animations API nutzen und von Alice im Wunderland inspiriert sind. Diese Beispiele werden mit freundlicher Genehmigung von Rachel Nabors erstellt und geteilt. Die vollständige Suite von Beispielen ist auf CodePen verfügbar; hier präsentieren wir die für unsere Dokumentation relevanten.

Schreiben von CSS-Animationen mit der Web Animations API

Eine der bekannteren Möglichkeiten, um in die Web Animations API einzusteigen, ist der Start mit etwas, das die meisten Webentwickler schon einmal verwendet haben: CSS-Animationen. CSS-Animationen haben eine vertraute Syntax, die sich gut zu Demonstrationszwecken eignet.

Die CSS-Version

Hier ist eine fallende Animation, die in CSS geschrieben wurde und Alice zeigt, wie sie in das Kaninchenloch fällt, das nach Wunderland führt:

Beachten Sie, dass sich der Hintergrund bewegt, Alice sich dreht und ihre Farbe sich versetzt zu ihrer Drehung ändert. In diesem Tutorial konzentrieren wir uns nur auf Alice. Sie können den vollständigen Quellcode überprüfen, indem Sie auf "Play" im Codeblock klicken. Hier ist das vereinfachte CSS, das Alices Animation steuert:

css
#alice {
  animation: alice-tumbling infinite 3s linear;
}

@keyframes alice-tumbling {
  0% {
    color: black;
    transform: rotate(0) translate3d(-50%, -50%, 0);
  }
  30% {
    color: #431236;
  }
  100% {
    color: black;
    transform: rotate(360deg) translate3d(-50%, -50%, 0);
  }
}

Dies ändert Alices Farbe und ihre Transformationsdrehung über 3 Sekunden mit einer konstanten (linearen) Rate und wiederholt sich unendlich. Im @keyframes-Block sehen wir, dass sich Alices Farbe 30 % des Weges durch jede Schleife (etwa nach 0,9 Sekunden) von Schwarz zu einem tiefen Burgunderrot ändert und dann bis zum Ende der Schleife wieder zurück wechselt.

Verschiebung zu JavaScript

Jetzt versuchen wir die gleiche Animation mit der Web Animations API zu erstellen.

Darstellung von Keyframes

Das Erste, was wir brauchen, ist die Erstellung eines Keyframe Object entsprechend unserem CSS-@keyframes-Block:

js
const aliceTumbling = [
  { transform: "rotate(0) translate3d(-50%, -50%, 0)", color: "black" },
  { color: "#431236", offset: 0.3 },
  { transform: "rotate(360deg) translate3d(-50%, -50%, 0)", color: "black" },
];

Hier verwenden wir ein Array, das mehrere Objekte enthält. Jedes Objekt repräsentiert einen Key aus dem ursprünglichen CSS. Im Gegensatz zu CSS muss die Web Animations API jedoch nicht explizit über die Prozentsätze entlang der Animation für jedes erscheinende Key informiert werden. Sie wird die Animation automatisch in gleiche Teile basierend auf der Anzahl der angegebenen Keys unterteilen. Das bedeutet, dass ein Keyframe-Objekt mit drei Keys den mittleren Key 50 % des Weges durch jede Schleife der Animation abspielt, sofern nicht anders angegeben.

Wenn wir explizit den Versatz eines Keys von den anderen Keys festlegen möchten, können wir einen Offset direkt im Objekt spezifizieren, getrennt von der Deklaration durch ein Komma. Im obigen Beispiel, um sicherzustellen, dass Alices Farbänderung bei 30% (nicht 50%) erfolgt, geben wir offset: 0.3 an.

Derzeit sollten mindestens zwei Keyframes spezifiziert sein (die Start- und Endzustände der Animationssequenz darstellend). Wenn Ihre Keyframe-Liste nur einen Eintrag hat, kann Element.animate() in einigen Browsern einen NotSupportedError DOMException werfen, bis sie aktualisiert werden.

Zusammenfassend sind die Keys standardmäßig gleichmäßig verteilt, es sei denn, Sie spezifizieren einen Offset auf einem Key. Praktisch, nicht wahr?

Darstellung von Timingeigenschaften

Wir müssen auch ein Objekt von Timingeigenschaften erstellen, das den Werten in Alices Animation entspricht:

js
const aliceTiming = {
  duration: 3000,
  iterations: Infinity,
};

Hier sind einige Unterschiede zu erkennen, wie äquivalente Werte in CSS repräsentiert werden:

  • Zum einen erfolgt die Dauer in Millisekunden anstelle von Sekunden — 3000 nicht 3s. Wie bei setTimeout() und Window.requestAnimationFrame(), akzeptiert die Web Animations API nur Millisekunden.
  • Das Andere, was Sie bemerken werden, ist, dass es iterations und nicht iteration-count heißt.

Hinweis: Es gibt eine Reihe kleiner Unterschiede zwischen der in CSS-Animationen verwendeten Terminologie und der in Web-Animationen verwendeten. Beispielsweise verwendet Web Animations nicht den String "infinite", sondern stattdessen das JavaScript-Schlüsselwort Infinity. Und anstelle von timing-function verwenden wir easing. Hier geben wir keinen easing-Wert an, weil, im Gegensatz zu CSS-Animationen, bei denen der Standardwert der animation-timing-function ease ist, bei der Web Animations API das Standard-easing linear — also das, was wir hier wollen.

Die Einzelteile zusammenfügen

Jetzt ist es Zeit, sie beide mit der Methode Element.animate() zusammenzuführen:

js
document.getElementById("alice").animate(aliceTumbling, aliceTiming);

Und boom: Die Animation beginnt zu spielen:

Die animate()-Methode kann auf jedem DOM-Element aufgerufen werden, das mit CSS animiert werden könnte. Und sie kann auf verschiedene Weisen geschrieben werden. Anstatt Objekte für Keyframes und Timingeigenschaften zu erstellen, könnten wir ihre Werte direkt übergeben, so wie hier:

js
document.getElementById("alice").animate(
  [
    { transform: "rotate(0) translate3d(-50%, -50%, 0)", color: "black" },
    { color: "#431236", offset: 0.3 },
    { transform: "rotate(360deg) translate3d(-50%, -50%, 0)", color: "black" },
  ],
  {
    duration: 3000,
    iterations: Infinity,
  },
);

Noch mehr, wenn wir nur die Dauer der Animation und nicht ihre Iterationen angeben wollten (standardmäßig iteriert eine Animation einmal), könnten wir einfach die Millisekunden allein übergeben:

js
document.getElementById("alice").animate(
  [
    { transform: "rotate(0) translate3d(-50%, -50%, 0)", color: "black" },
    { color: "#431236", offset: 0.3 },
    { transform: "rotate(360deg) translate3d(-50%, -50%, 0)", color: "black" },
  ],
  3000,
);

Wiedergabesteuerung mit play(), pause(), reverse() und updatePlaybackRate()

Während wir CSS-Animationen mit der Web Animations API schreiben können, liegt der wahre Nutzen der API in der Manipulation der Wiedergabe der Animation. Die Web Animations API bietet mehrere nützliche Methoden zur Steuerung der Wiedergabe. Lassen Sie uns einen Blick auf das Pausieren und Spielen von Animationen im "Follow the White Rabbit"-Beispiel werfen:

In diesem Beispiel hat der weiße Hase eine Animation, die ihn in ein Kaninchenloch lässt. Sie wird nur ausgelöst, wenn der Benutzer darauf klickt.

Animationen pausieren und abspielen

Wir können den Hasen wie gewohnt mit der animate()-Methode animieren:

js

Die Methode Element.animate() wird sofort ausgeführt, nachdem sie aufgerufen wurde. Um zu verhindern, dass der Kuchen sich selbst isst, bevor der Benutzer die Möglichkeit hatte, darauf zu klicken, rufen wir sofort nach der Definition Animation.pause() darauf auf, wie folgt:

js

Hinweis: Alternativ können Sie rabbitDownAnimation mit dem Animation()-Konstruktor definieren, das nicht spielt, bis Sie play() aufrufen.

Wir können jetzt die Methode Animation.play() verwenden, um sie auszuführen, wann immer wir bereit sind. Speziell wollen wir es mit einer Klickaktion verknüpfen. Wir können dies über folgendes erreichen:

js

Wenn ein Benutzer auf den Hasen klickt oder mit dem Finger darauf drückt, können wir jetzt downHeGoes aufrufen, um alle Animationen abzuspielen.

Andere nützliche Methoden

Zusätzlich zum Pausieren und Abspielen können wir die folgenden Animation-Methoden verwenden:

  • Animation.finish() springt zum Ende der Animation.
  • Animation.cancel() bricht die Animation ab und entfernt deren Effekte.
  • Animation.reverse() setzt die Wiedergaberate (Animation.playbackRate](/de/docs/Web/API/Animation/playbackRate)) der Animation auf einen negativen Wert, sodass sie rückwärts läuft.

Schauen wir uns zuerst playbackRate an — eine negative playbackRate lässt eine Animation rückwärts laufen. In Durch den Spiegel reist Alice zu einer Welt, in der sie laufen muss, um an Ort und Stelle zu bleiben — und doppelt so schnell laufen muss, um sich vorwärts zu bewegen! Im Beispiel "Red Queen's Race" laufen Alice und die Rote Königin, um an Ort und Stelle zu bleiben:

Da kleine Kinder im Gegensatz zu automatisierten Schachfiguren schnell ermüden, verlangsamt sich Alice ständig. Wir können dies tun, indem wir ein Abklingen auf ihre playbackRate der Animation einstellen. Wir verwenden updatePlaybackRate() anstelle von playbackRate direkt zu setzen, da dies ein reibungsloses Update erzeugt:

js
setInterval(() => {
  // Make sure the playback rate never falls below .4
  if (redQueenAlice.playbackRate > 0.4) {
    redQueenAlice.updatePlaybackRate(redQueenAlice.playbackRate * 0.9);
  }
  adjustBackgroundPlayback();
}, 1000);

Aber wenn man auf sie klickt oder tippt, werden sie durch Multiplikation ihrer playbackRate beschleunigt:

js
function goFaster() {
  // But you can speed them up by giving the screen a click or a tap.
  redQueenAlice.updatePlaybackRate(redQueenAlice.playbackRate * 1.1);
  adjustBackgroundPlayback();
}

document.addEventListener("click", goFaster);
document.addEventListener("touchstart", goFaster);

Auch die Hintergrundelemente haben playbackRates, die beim Klick oder Tap beeinflusst werden. Ihre Wiedergaberaten werden aus Alice's abgeleitet, siehe unten. Was passiert, wenn Sie Alice und die Rote Königin doppelt so schnell laufen lassen? Was passiert, wenn sie langsamer werden?

js
/* Alice tires so easily! 
  Every so many seconds, reduce their playback rate so they slow a little. 
*/
const sceneries = [
  foreground1Movement,
  foreground2Movement,
  background1Movement,
  background2Movement,
];

function adjustBackgroundPlayback() {
  // If Alice and the Red Queen are running at a speed of 0.8–1.2,
  // the background doesn't move.
  // But if they fall under 0.8, the background slides backwards
  if (redQueenAlice.playbackRate < 0.8) {
    sceneries.forEach((anim) => {
      anim.updatePlaybackRate(-redQueenAlice.playbackRate / 2);
    });
  } else if (redQueenAlice.playbackRate > 1.2) {
    sceneries.forEach((anim) => {
      anim.updatePlaybackRate(redQueenAlice.playbackRate / 2);
    });
  } else {
    sceneries.forEach((anim) => {
      anim.updatePlaybackRate(0);
    });
  }
}
adjustBackgroundPlayback();

Animation Styles beibehalten

Ein häufiger Anwendungsfall beim Animieren von Elementen ist, den Endzustand der Animation nach deren Abschluss beizubehalten. Eine manchmal verwendete Methode dafür ist das Setzen des fill mode der Animation auf forwards. Es wird jedoch nicht empfohlen, Fill-Modi zu verwenden, um den Effekt einer Animation unbegrenzt beizubehalten, aus zwei Gründen:

  • Der Browser muss den Zustand der Animation aufrechterhalten, während sie noch aktiv ist, sodass die Animation weiterhin Ressourcen verbraucht, obwohl sie nicht mehr animiert. Beachten Sie, dass dies etwas dadurch gemildert wird, dass der Browser Füllanimations automatisch entfernt.
  • Von Animationen angewendete Stile haben eine höhere Priorität in der Cascade als angegebene Stile, sodass es schwierig sein kann, sie nach Bedarf zu überschreiben.

Ein besserer Ansatz ist die Verwendung der Methode Animation.commitStyles(). Dies schreibt die berechneten Werte der aktuellen Stile der Animation in das style-Attribut ihres Zielelements, danach kann das Element normal umgestylt werden.

Automatische Entfernung von Fill-Animationen

Es ist möglich, eine große Anzahl von Animationen auf demselben Element auszulösen. Wenn sie unbegrenzt sind (d.h. forward-filling), kann dies zu einer riesigen Animationsliste führen, die ein Speicherleck verursachen könnte. Aus diesem Grund entfernen Browser automatisch füllende Animationen, nachdem sie durch neuere Animationen ersetzt wurden, es sei denn, der Entwickler gibt ausdrücklich an, sie zu behalten.

Animationen werden entfernt, wenn alle folgenden Bedingungen erfüllt sind:

  • Die Animation füllt (Ihr fill ist forwards, wenn sie vorwärts läuft, backwards, wenn sie rückwärts läuft, oder both).
  • Die Animation ist beendet. (Beachten Sie, dass sie aufgrund des fill trotzdem noch in Kraft ist.)
  • Die Zeitleiste der Animation ist monoton steigend. (Dies ist immer wahr für DocumentTimeline; andere Zeitleisten wie scroll-timeline können rückwärts laufen.)
  • Die Animation wird nicht durch deklarative Markup wie CSS gesteuert.
  • Jeder Stileffekt des AnimationEffect der Animation wird durch eine andere Animation überschrieben, die ebenfalls alle obigen Bedingungen erfüllt. (Typischerweise, wenn zwei Animationen die gleiche Stileigenschaft des gleichen Elements setzen, überschreibt die zuletzt erstellte die andere.)

Die ersten vier Bedingungen stellen sicher, dass ohne Eingreifen durch JavaScript-Code der Effekt der Animation sich nie ändern oder enden wird. Die letzte Bedingung stellt sicher, dass die Animation nie tatsächlich den Stil eines Elements beeinflussen wird: sie wurde vollständig ersetzt.

Wenn die Animation automatisch entfernt wird, wird das remove-Ereignis der Animation ausgelöst.

Um zu verhindern, dass der Browser Animationen automatisch entfernt, rufen Sie die Methode persist() der Animation auf.

Die Eigenschaft replaceState der Animation wird removed sein, wenn die Animation entfernt wurde, persisted, wenn Sie persist() auf die Animation angewendet haben, oder active, andernfalls.

Informationen aus Animationen herausholen

Stellen Sie sich andere Möglichkeiten vor, wie wir playbackRate verwenden könnten, z. B. zur Verbesserung der Barrierefreiheit für Benutzer mit vestibulären Störungen, indem wir ihnen ermöglichen, Animationen auf einer gesamten Website zu verlangsamen. Das ist mit CSS ohne Neuberechnung der Dauern in jeder CSS-Regel unmöglich, aber mit der Web Animations API können wir die Methode Document.getAnimations verwenden, um jede Animation auf der Seite zu durchlaufen und ihre playbackRates zu halbieren, so wie hier:

js
document.getAnimations().forEach((animation) => {
  animation.updatePlaybackRate(animation.playbackRate * 0.5);
});

Mit der Web Animations API müssen Sie nur eine kleine Eigenschaft ändern!

Ein weiteres Problem, das mit CSS-Animationen allein schwer zu lösen ist, ist die Schaffung von Abhängigkeiten von Werten, die von anderen Animationen bereitgestellt werden. Im Beispiel des "Growing and Shrinking Alice"-Spiels haben Sie vielleicht bemerkt, dass die Dauer des Kuchens etwas merkwürdig ist:

js
document.getElementById("eat-me-sprite").animate([], {
  duration: aliceChange.effect.getComputedTiming().duration / 2,
});

Um zu verstehen, was hier passiert, schauen wir uns die Animation von Alice an:

js
const aliceChange = document
  .getElementById("alice")
  .animate(
    [
      { transform: "translate(-50%, -50%) scale(.5)" },
      { transform: "translate(-50%, -50%) scale(2)" },
    ],
    {
      duration: 8000,
      easing: "ease-in-out",
      fill: "both",
    },
  );

Alices Animation lässt sie von der Hälfte ihrer Größe auf das Doppelte ihrer Größe über 8 Sekunden anwachsen. Dann pausieren wir sie:

js
aliceChange.pause();

Wenn wir sie am Anfang ihrer Animation pausiert gelassen hätten, würde sie bei der Hälfte ihrer vollen Größe beginnen, als ob sie die ganze Flasche schon ausgetrunken hätte! Wir wollen den "Abspielkopf" ihrer Animation in der Mitte setzen, also ist sie schon zur Hälfte fertig. Wir könnten dies tun, indem wir ihre Animation.currentTime auf 4 Sekunden einstellen, so wie hier:

js
aliceChange.currentTime = 4000;

Aber während wir an dieser Animation arbeiten, könnten wir Alices Dauer oft ändern. Wäre es nicht besser, wenn wir ihre currentTime dynamisch einstellen, damit wir nicht zwei Änderungen gleichzeitig vornehmen müssen? Tatsächlich können wir dies tun, indem wir auf die Eigenschaft Animation.effect von aliceChange verweisen, die ein Objekt mit allen Details des/der Effekte(s), die auf Alice aktiv sind, zurückgibt:

js
aliceChange.currentTime = aliceChange.effect.getComputedTiming().duration / 2;

effect ermöglicht es uns, auf die Keyframes und Timingeigenschaften der Animation zuzugreifen — aliceChange.effect.getComputedTiming() zeigt auf Alices Timing-Objekt — das enthält ihre duration. Wir können ihre Dauer halbieren, um den Mittelpunkt ihrer Animationszeitleiste zu erhalten, und sie auf normale Größe setzen. Jetzt können wir ihre Animation in beide Richtungen umkehren und abspielen, um sie wachsen oder schrumpfen zu lassen!

Und wir können dasselbe tun, wenn wir die Dauer von Kuchen und Flasche einstellen:

js
const drinking = document
  .getElementById("liquid")
  .animate([{ height: "100%" }, { height: "0" }], {
    fill: "forwards",
    duration: aliceChange.effect.getComputedTiming().duration / 2,
  });
drinking.pause();

Jetzt sind alle drei Animationen mit nur einer Dauer verknüpft, die wir leicht an einem Ort ändern können.

Wir können auch die Web Animations API verwenden, um die aktuelle Zeit der Animation zu ermitteln. Das Spiel endet, wenn Ihnen der Kuchen zum Essen oder die Flasche leer geht. Welche Vignette den Spielern präsentiert wird, hängt davon ab, wie weit Alice in ihrer Animation war, ob sie zu groß wird und nicht mehr durch die kleine Tür passen kann oder zu klein ist und nicht den Schlüssel zur Tür erreichen kann. Wir können herausfinden, ob sie am großen oder kleinen Ende ihrer Animation ist, indem wir die currentTime ihrer Animation erhalten und sie durch ihre activeDuration teilen:

js
const endGame = () => {
  // get Alice's timeline's playhead location
  const alicePlayhead = aliceChange.currentTime;
  const aliceTimeline = aliceChange.effect.getComputedTiming().activeDuration;

  // stops Alice's and other animations
  stopPlayingAlice();

  // depending on which third it falls into
  const aliceHeight = alicePlayhead / aliceTimeline;

  if (aliceHeight <= 0.333) {
    // Alice got smaller!
    // …
  } else if (aliceHeight >= 0.666) {
    // Alice got bigger!
    // …
  } else {
    // Alice didn't change significantly
    // …
  }
};

Callbacks und Promises

CSS-Animationen und -Übergänge haben ihre eigenen Ereignis-Listener, und diese sind auch mit der Web Animations API möglich:

  • onfinish ist der Ereignishandler für das finish-Ereignis und kann manuell mit finish() ausgelöst werden.
  • oncancel ist der Ereignishandler für das cancel-Ereignis und kann mit cancel() ausgelöst werden.

Hier setzen wir die Callbacks für den Kuchen, die Flasche und Alice, um die Funktion endGame auszulösen:

js
// When the cake or bottle runs out
nommingCake.onfinish = endGame;
drinking.onfinish = endGame;

// Alice reaches the end of her animation
aliceChange.onfinish = endGame;

Noch besser, die Web Animations API bietet auch ein finished Promise, das erfüllt wird, wenn die Animation abgeschlossen ist, oder abgelehnt, wenn sie abgebrochen wird.

Schlussfolgerung

Dies sind die grundlegenden Funktionen der Web Animations API. Jetzt sollten Sie bereit sein, "ins Kaninchenloch" des Animierens im Browser zu springen und bereit sein, Ihre eigenen Animationsexperimente zu schreiben!

Siehe auch