Komplexität orchestrieren mit der Webanimations-API

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Es gibt viel zu viele Optionen in der Webanimations-API, um sie so einfach aufzugreifen. Zu lernen, wie das Timing funktioniert und wie man die Wiedergabe mehrerer Animationen gleichzeitig steuert, bildet eine solide Grundlage, auf der Sie Ihre Projekte aufbauen können.

Es gibt keinen Mittelweg zwischen einfachen Übergängen und komplexen Animationen. Entweder sind Sie mit dem, was CSS-Übergänge und -Animationen bieten, zufrieden, oder Sie brauchen plötzlich alle Leistung, die Sie bekommen können. Die Webanimations-API bietet Ihnen viele Tools zum Arbeiten mit Animationen. Aber man muss wissen, wie man damit umgeht. Dieser Artikel führt Sie durch die wichtigsten Punkte und Techniken , die Ihnen helfen können, mit komplexen Animationen umzugehen und gleichzeitig flexibel zu bleiben.

Bevor wir in den Artikel eintauchen, ist es wichtig, dass Sie mit den Grundlagen der Webanimations-API und JavaScript vertraut sind. Um es deutlich zu machen und eine Ablenkung vom vorliegenden Problem zu vermeiden, sind die bereitgestellten Codebeispiele einfach. Es wird nichts Komplexeres geben als Funktionen und Objekte. Als nette Einstiegspunkte in Animationen selbst würde ich MDN als allgemeine Referenz, Daniel C. Wilsons hervorragende Serie und CSS Animations vs. Web Animations API von Ollie Williams vorschlagen. Wir werden nicht durchgehen, wie Effekte definiert und abgestimmt werden, um das gewünschte Ergebnis zu erzielen. In diesem Artikel wird davon ausgegangen, dass Sie Ihre Animationen definiert haben und Ideen und Techniken benötigen, um damit umzugehen.

Wir beginnen mit einem Überblick über Schnittstellen und deren Zweck. Dann schauen wir uns das Timing und die Kontrollebenen an, um zu definieren, was, wann und wie lange. Danach lernen wir, wie man mehrere Animationen als eine behandelt, indem man sie in Objekte verpackt. Das wäre ein guter Anfang auf dem Weg zur Verwendung der Webanimations-API.

Schnittstellen

Die Webanimations-API gibt uns eine neue Dimension der Kontrolle. Davor hatten CSS-Übergänge und -Animationen, obwohl sie eine leistungsstarke Möglichkeit zum Definieren von Effekten boten, immer noch einen einzigen Betätigungspunkt . Wie ein Lichtschalter war es entweder an oder aus. Sie könnten mit Verzögerungen und Beschleunigungsfunktionen spielen, um recht komplexe Effekte zu erzeugen. Ab einem bestimmten Punkt wird es jedoch umständlich und schwierig, damit zu arbeiten.

Die Web Animations API verwandelt diesen einzelnen Betätigungspunkt in eine vollständige Kontrolle über die Wiedergabe . Der Lichtschalter wird mit einem Schieberegler zum Dimmschalter. Wenn Sie möchten, können Sie daraus das ganze Smart-Home-Ding machen, denn zusätzlich zur Wiedergabesteuerung können Sie jetzt Effekte zur Laufzeit definieren und ändern. Sie können jetzt Effekte an den Kontext anpassen oder einen Animationseditor mit Echtzeitvorschau implementieren.

Wir beginnen mit der Animationsschnittstelle. Um ein Animationsobjekt zu erhalten, können wir die Methode Element.animate verwenden. Sie geben ihm Keyframes und Optionen und es spielt Ihre Animation sofort ab. Außerdem gibt es eine Animation zurück. Sein Zweck ist die Steuerung der Wiedergabe.

Stellen Sie es sich wie einen Kassettenspieler vor, wenn Sie sich an diese erinnern. Ich bin mir bewusst, dass einige der Leser möglicherweise nicht damit vertraut sind, was es ist. Es ist unvermeidlich, dass jeder Versuch, reale Konzepte zur Beschreibung abstrakter Computerdinge anzuwenden, schnell auseinanderfallen wird. Aber lassen Sie sich davon beruhigen – einen Leser, der nicht weiß, wie schön es ist, ein Band mit einem Bleistift zurückzuspulen –, dass Leute, die wissen, was ein Kassettenrekorder ist, am Ende dieses Artikels noch mehr verwirrt sein werden.

Stellen Sie sich eine Kiste vor. Es hat einen Schlitz, in den die Kassette passt, und es hat Tasten zum Abspielen, Stoppen und Zurückspulen. Genau das ist die Animationsschnittstelleninstanz – eine Box, die definierte Animationen enthält und Möglichkeiten bietet, mit ihrer Wiedergabe zu interagieren. Du gibst ihm etwas zum Spielen und er gibt dir die Kontrolle zurück.

Die Steuerelemente, die Sie erhalten, ähneln bequem denen, die Sie von Audio- und Videoelementen erhalten. Sie sind Play- und Pause -Methoden und die aktuelle Zeiteigenschaft. Mit diesen drei Steuerelementen können Sie alles erstellen, wenn es um die Wiedergabe geht.

Die Kassette selbst ist ein Paket, das einen Verweis auf das animierte Element, die Definition von Effekten und Optionen enthält, die unter anderem das Timing beinhalten. Und das ist der KeyframeEffect . Unser Kassettenband enthält alle Aufnahmen und Informationen über die Länge der Aufnahmen. Ich überlasse es der Vorstellungskraft des älteren Publikums, all diese Eigenschaften mit den Komponenten einer physischen Kassette in Einklang zu bringen. Was ich Ihnen zeigen werde, ist, wie es im Code aussieht.

Wenn Sie eine Animation über Element.animate , verwenden Sie eine Verknüpfung, die drei Dinge tut. Es erstellt eine KeyframeEffect Instanz. Es wird in eine neue Animation eingefügt. Es beginnt sofort mit der Wiedergabe.

 const animation = element.animate(keyframes, options);

Lassen Sie uns es aufschlüsseln und den entsprechenden Code sehen, der dasselbe tut.

 const animation = new Animation( // (2) new KeyframeEffect(element, keyframes, options) // (1) ); animation.play(); (3)

Nimm die Kassette (1), lege sie in einen Player (2) und drücke dann die Play-Taste (3).

Wenn Sie wissen, wie es hinter den Kulissen funktioniert, können Sie die Definition von Keyframes trennen und entscheiden, wann sie abgespielt werden. Wenn Sie viele Animationen zu koordinieren haben, kann es hilfreich sein, sie alle zuerst zu sammeln, damit Sie wissen, dass sie spielbereit sind. Sie spontan zu generieren und zu hoffen, dass sie im richtigen Moment mit dem Spielen beginnen, ist nicht etwas, worauf Sie hoffen möchten. Es ist zu einfach, den gewünschten Effekt durch Ziehen um ein paar Frames zu unterbrechen. Bei einer langen Sequenz häuft sich der Luftwiderstand, was zu einem nicht überzeugenden Erlebnis führt.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Zeitliche Koordinierung

Wie in der Komödie ist Timing alles in Animationen. Damit ein Effekt funktioniert, um ein bestimmtes Gefühl zu erzielen, müssen Sie in der Lage sein, die Art und Weise, wie sich die Eigenschaften ändern, fein abzustimmen. Es gibt zwei Zeitsteuerungsebenen, die Sie in der Webanimations-API steuern können.

Auf der Ebene einzelner Objekte haben wir offset . Offset gibt Ihnen die Kontrolle über das Timing einzelner Eigenschaften . Indem Sie ihm einen Wert zwischen null und eins zuweisen, definieren Sie, wann jeder Effekt einsetzt. Wenn er weggelassen wird, ist er gleich null.

Sie erinnern sich vielleicht an @keyframes in CSS, wie Sie Prozentsätze anstelle von from / to verwenden können. Das ist offset , aber geteilt durch Hundert. Der Wert von offset ist ein Teil der Dauer einer einzelnen Iteration .

Mit dem offset können Sie Keyframes innerhalb eines KeyframeEffect KeyframeEffect . Ein relativer Zahlenversatz stellt sicher, dass alle Ihre Keyframes unabhängig von der Dauer oder der Wiedergaberate im selben Moment relativ zueinander beginnen.

Wie bereits erwähnt, ist offset ein Teil der Duration . Jetzt möchte ich, dass Sie meine Fehler und den Zeitverlust diesbezüglich vermeiden. Es ist wichtig zu verstehen, dass die Dauer einer Animation nicht dasselbe ist wie die Gesamtdauer einer Animation. Normalerweise sind sie gleich und das könnte Sie verwirren und was mich definitiv verwirrt hat.

Die Dauer ist die Zeit in Millisekunden , die eine Iteration zum Abschluss benötigt. Sie entspricht standardmäßig der Gesamtdauer. Sobald Sie eine Verzögerung hinzufügen oder die Anzahl der Iterationen in einer Animationsdauer erhöhen, wird Ihnen die gewünschte Anzahl nicht mehr angezeigt. Das ist wichtig zu verstehen, um es zu Ihrem Vorteil zu nutzen.

Wenn Sie eine Keyframe-Wiedergabe in einem größeren Kontext wie der Medienwiedergabe koordinieren müssen, müssen Sie Timing-Optionen verwenden. Die Gesamtdauer der Animation vom Start bis zum „beendeten“ Ereignis in der folgenden Gleichung:

 delay + (iterations × duration) + end delay

Sie können es in der folgenden Demo in Aktion sehen:

Siehe den Stift [Was ist die tatsächliche Dauer einer Animation?] (https://codepen.io/smashingmag/pen/VwWWrzz) von Kirill Myshkin.

Sehen Sie sich den Stift an Was ist die tatsächliche Dauer einer Animation? von Kirill Myschkin.

Dadurch können wir mehrere Animationen im Kontext von Medien mit fester Länge ausrichten . Unter Beibehaltung der gewünschten Animationsdauer könnten Sie sie am Anfang mit delay und am Ende mit delayEnd „auffüllen“, um sie in einen Kontext mit längerer Dauer einzubetten. Wenn Sie darüber nachdenken, würde eine delay in diesem Sinne wie der Versatz in Keyframes wirken. Denken Sie daran, dass die Verzögerung in Millisekunden eingestellt ist, sodass Sie sie möglicherweise in einen relativen Wert umwandeln möchten.

Eine weitere Timing-Option, die beim Ausrichten der Animation helfen würde, ist iterationStart . Es legt die Startposition einer Iteration fest. Nehmen Sie die Poolball-Demo. Durch Anpassen des iterationStart -Schiebereglers können Sie die Startposition des Balls und die Drehung festlegen, zum Beispiel können Sie ihn so einstellen, dass er von der Mitte des Bildschirms zu springen beginnt und die Zahl im letzten Frame in der Kamera gerade ist.

Siehe Pen [Tweak interationStart](https://codepen.io/smashingmag/pen/qBjjVPR) von Kirill Myshkin.

Siehe Pen Tweak interationStart von Kirill Myshkin.

Steuern Sie mehrere als eine

Als ich an einem Animationseditor für eine Präsentations-App arbeitete, musste ich mehrere Animationen für ein einzelnes Element auf einer Zeitachse anordnen. Mein erster Versuch war, offset zu verwenden, um meine Animation am richtigen Startpunkt auf einer Zeitachse zu platzieren.

Das erwies sich schnell als der falsche Weg, offset zu verwenden. In Bezug auf diese spezielle Benutzeroberfläche bedeutet das Bewegen von Animationen auf der Zeitachse, dass sie ihre Startposition verschieben, ohne die Dauer der Animation zu ändern. Mit offset bedeutete das, dass ich mehrere Dinge ändern musste, den offset selbst und auch den offset der Closing-Eigenschaft, um sicherzustellen, dass sich die Dauer nicht ändert. Die Lösung erwies sich als zu komplex, um sie zu verstehen.

Das zweite Problem kam mit der transform Eigenschaft. Aufgrund der Tatsache, dass es mehrere charakteristische Änderungen an einem Element darstellen kann, kann es schwierig werden, es so zu machen, wie Sie es möchten. Wenn man diese Eigenschaften unabhängig voneinander ändern möchte, könnte es noch schwieriger werden. Die Änderung der Skalenfunktion beeinflusst alle nachfolgenden Funktionen. Hier ist, warum das passiert.

Die Transform-Eigenschaft kann mehrere Funktionen in einer Sequenz als Wert annehmen. Je nach Reihenfolge der Funktion ändert sich das Ergebnis. scale nehmen und translate . Manchmal ist es praktisch, die translate in Prozent zu definieren, also relativ zur Größe eines Elements. Angenommen, Sie möchten, dass ein Ball genau drei eigene Durchmesser hoch springt. Je nachdem, wo Sie die Skalierungsfunktion platzieren – vor oder nach der translate – ändert sich das Ergebnis von drei Höhen der Originalgröße oder der skalierten.

Dies ist ein wichtiges Merkmal der transform . Sie benötigen es, um eine ziemlich komplexe Transformation zu erreichen. Aber wenn Sie diese Transformationen benötigen, um unterschiedlich und unabhängig von anderen Transformationen eines Elements zu sein, kommt es Ihnen in die Quere.

Es gibt Fälle, in denen Sie nicht alle Effekte in einer transform unterbringen können. Da kann es schnell zu viel werden. Besonders wenn Ihre Keyframes von verschiedenen Stellen stammen, müssten Sie eine sehr komplexe Zusammenführung einer transformierten Zeichenfolge haben. Sie können sich kaum auf einen automatischen Mechanismus verlassen, da die Logik nicht einfach ist. Außerdem könnte es schwierig werden zu verstehen, was einen erwartet. Um dies zu vereinfachen und flexibel zu bleiben, müssen wir diese in verschiedene Kanäle aufteilen.

Eine Lösung besteht darin, unsere Elemente in div s zu verpacken , die jeweils separat animiert werden können, z. B. ein div zum Positionieren auf der Leinwand, ein weiteres zum Skalieren und ein drittes zum Drehen. Damit vereinfachen Sie nicht nur die Definition von Animationen enorm, sondern eröffnen sich auch die Möglichkeit, ggf. unterschiedliche Transformationsursprünge zu definieren.

Es könnte scheinen, dass die Dinge mit diesem Trick außer Kontrolle geraten. Dass wir die Anzahl der Probleme, die wir zuvor hatten, vervielfachen. Als ich diesen Trick zum ersten Mal entdeckte, verwarf ich ihn als zu viel. Ich dachte, ich könnte nur sicherstellen, dass meine transform aus allen Teilen in der richtigen Reihenfolge in einem Stück zusammengestellt wird . Es bedurfte einer weiteren transform , um die Dinge zu komplex für die Verwaltung und bestimmte Dinge unmöglich zu machen. Mein Compiler für transform brauchte immer mehr Zeit, um richtig zu werden, also gab ich auf.

Es stellte sich heraus, dass die Steuerung der Wiedergabe mehrerer Animationen nicht so schwierig ist, wie es zunächst scheint. Erinnerst du dich an die Kassettenrecorder-Analogie von Anfang an? Was wäre, wenn Sie Ihren eigenen Player verwenden könnten, der eine beliebige Anzahl von Kassetten aufnimmt? Darüber hinaus können Sie diesem Player so viele Schaltflächen hinzufügen, wie Sie möchten.

Der einzige Unterschied zwischen dem Aufrufen der play einer einzelnen Animation und einer Reihe von Animationen besteht darin, dass Sie iterieren müssen. Hier ist der Code, den Sie für jede Methode von Animation verwenden können:

 // To play just call play on all of them animations.forEach((animation) => animation.play());

Wir werden dies verwenden, um alle möglichen Funktionen für unseren Player zu erstellen.

Lassen Sie uns diese Box erstellen, die die Animationen enthalten würde, und sie abspielen. Sie können diese Boxen auf jede geeignete Weise erstellen. Um es klarer zu machen, zeige ich Ihnen ein Beispiel dafür, wie man es mit einer Funktion und einem Objekt macht. Die createPlayer Funktion nimmt eine Reihe von Animationen, die synchron abgespielt werden sollen. Es gibt ein Objekt mit einer Single play Methode zurück.

 function createPlayer(animations) { return Object.freeze({ play: function () { animations.forEach((animation) => animation.play()); } }); }

Das reicht aus, um die Funktionalität zu erweitern. Lassen Sie uns die Methoden pause und currentTime hinzufügen.

 function createPlayer(animations) { return Object.freeze({ play: function () { animations.forEach((animation) => animation.play()); }, pause: function () { animations.forEach((animation) => animation.pause()); }, currentTime: function (time = 0) { animations.forEach((animation) => animation.currentTime = time); } }); }

Der createPlayer mit diesen drei Methoden gibt Ihnen genug Kontrolle, um eine beliebige Anzahl von Animationen zu orchestrieren . Aber schieben wir es ein bisschen weiter. Machen wir es so, dass unser Player nicht nur beliebig viele Kassetten aufnehmen kann, sondern auch andere Player.

Wie wir bereits gesehen haben, ähnelt die Animation den Medienschnittstellen. Mit dieser Ähnlichkeit könnten Sie alle möglichen Dinge in Ihren Player packen. Um dies zu berücksichtigen, optimieren wir die Methode currentTime so, dass sie sowohl mit Animationsobjekten als auch mit Objekten funktioniert, die von createPlayer .

 function currentTime(time = 0) { animations.forEach(function (animation) { if (typeof animation.currentTime === "function") { animation.currentTime(time); } else { animation.currentTime = time; } }); }

Der Player, den wir gerade erstellt haben, ermöglicht es Ihnen, die Komplexität mehrerer div s für Single-Element-Animationskanäle zu verbergen. Diese Elemente könnten in einer Szene gruppiert werden. Und jede Szene könnte Teil von etwas Größerem sein. All das konnte mit dieser Technik erreicht werden.

Um die Timing-Demo zu demonstrieren, habe ich alle Animationen auf drei Spieler aufgeteilt. Die erste ist die Steuerung der Wiedergabe der Vorschau auf der rechten Seite. Die zweite kombiniert die Sprunganimation aller Umrisse der Bälle links und der in der Vorschau.

Der dritte schließlich ist ein Spieler, der Positionsanimationen der Bälle in einem linken Container kombiniert . Dieser Spieler lässt die Bälle in einer kontinuierlichen Demonstration der Animation mit ungefähr 60 Bildern pro Sekunde Scheiben ausbreiten.

Fazit

Webschnittstellen wie die Webanimations-API legen uns bestimmte Dinge offen, die Browser die ganze Zeit gemacht haben. Browser wissen, wie man schnell rendert, indem sie Arbeit an die GPU weitergeben. Mit der Web Animations API haben wir die Kontrolle darüber. Auch wenn diese Steuerung etwas fremd oder verwirrend erscheinen mag, bedeutet dies nicht, dass ihre Verwendung auch verwirrend sein sollte. Mit einem Verständnis für Timing und Wiedergabesteuerung verfügen Sie über Tools, um diese API an Ihre Bedürfnisse anzupassen. Sie sollten in der Lage sein, zu definieren, wie komplex es sein soll.

Weiterführende Lektüre

  • „Praktische Techniken zum Entwerfen von Animationen“, Sarah Drasner
  • „Entwerfen mit reduzierter Bewegung für Bewegungsempfindlichkeiten“, Val Head
  • „Eine alternative Sprach-UI zu Sprachassistenten“, Ottomatias Peura
  • „Entwerfen besserer Tooltips für mobile Benutzeroberflächen“, Eric Olive