Optimieren Sie die Animationen Ihrer Webapp
Kurzfassung: Mit dem Animation Worklet können Sie imperative Animationen schreiben, die ausgeführt werden mit der ursprünglichen Framerate des Geräts für diese zusätzliche flüssige Wiedergabe. Ihre Animationen gegen Hauptthread-Verzögerungen sicherer machen und verlinkbar sind anstatt durch die Zeit zu scrollen. Das Animations-Worklet befindet sich in Chrome Canary (hinter dem „Experimentelle Webplattform-Funktionen“ ) und wir planen einen Ursprungstest für Chrome 71. Sie können es als heute Progressive Enhancement.
Noch eine Animations-API?
Nein. Es ist eine Erweiterung dessen, was wir bereits haben, und das aus gutem Grund! Fangen wir am Anfang an. Wenn Sie ein DOM-Element im Web animieren möchten Sie haben heute 2 1⁄2 Auswahlmöglichkeiten: CSS-Übergänge für einfache A-B-Übergänge, CSS-Animationen für potenziell zyklische, komplexere zeitbasierte Animationen und die Web Animations API (WAAPI) für fast beliebig komplexe Animationen. Die Unterstützungsmatrix von WAAPI sieht ziemlich düster aus, aber er ist auf dem Weg nach oben. Bis dahin gibt es eine Polyfill
Was all diese Methoden gemeinsam haben, ist, dass sie zustandslos sind und zeitgesteuert sind. Aber einige der Effekte, die Entwickler versuchen, sind weder zeitgesteuert oder zustandslos sein. Das berüchtigte Parallaxe-Scroller-Symbol beispielsweise ist, „scroll-gesteuert“. Die Implementierung eines leistungsfähigen Parallaxe-Scrollers im Web ist heute erstaunlich schwierig.
Und was ist mit Zustandslosigkeit? Denken Sie an die Adressleiste von Chrome unter Android, Beispiel. Wenn Sie nach unten scrollen, scrollen Sie aus dem sichtbaren Bereich. Die Wenn Sie nach oben scrollen, wird die Seite wieder angezeigt, selbst wenn Sie weiter unten auf dieser Seite. Die Animation hängt nicht nur von der Scrollposition, sondern auch vom die vorherige Scroll-Richtung. Es ist zustandsorientiert.
Ein weiteres Problem ist die Gestaltung von Bildlaufleisten. Sie sind bekanntermaßen nicht anpassbar nicht stilistisch genug. Wie gehe ich vor, wenn eine Nyankatze als Bildlaufleiste verwendet werden soll? Für welche Technik Sie sich auch entscheiden, das Erstellen einer benutzerdefinierten Bildlaufleiste leistungsstark oder einfach ist.
Der Punkt ist, dass all diese Dinge unangenehm und schwer bis unmöglich sind.
effizient implementieren können. Die meisten basieren auf Ereignissen und/oder
requestAnimationFrame
, die möglicherweise eine Bildrate von 60 fps bietet, auch wenn das Display
bei 90 fps, 120 fps oder höher laufen und einen Bruchteil eurer
für den Hauptthread-Rahmen.
Animation Worklet erweitert die Funktionen des Animationsstacks diese Art von Effekten leichter zu machen. Bevor wir loslegen, sollten wir sicherstellen, über die Grundlagen von Animationen.
Eine Einführung in Animationen und Zeitachsen
WAAPI und Animation Worklet nutzen umfangreiche Zeitleisten, damit Sie Animationen und Effekte nach Ihren Wünschen orchestrieren. Dieser Abschnitt bezieht sich auf eine kurze Auffrischung oder Einführung in Zeitachsen und deren Funktionsweise mit Animationen.
Jedes Dokument hat document.timeline
. Sie beginnt bei 0, wenn das Dokument
erstellt und zählt die Millisekunden seit dem Start des vorhandenen Dokuments. Alle von
die Animationen eines Dokuments relativ zu dieser Zeitachse.
Sehen wir uns zur Verdeutlichung dieses WAAPI-Snippet an.
const animation = new Animation(
new KeyframeEffect(
document.querySelector('#a'),
[
{
transform: 'translateX(0)',
},
{
transform: 'translateX(500px)',
},
{
transform: 'translateY(500px)',
},
],
{
delay: 3000,
duration: 2000,
iterations: 3,
}
),
document.timeline
);
animation.play();
Wenn wir animation.play()
aufrufen, verwendet die Animation das currentTime
-Element der Zeitachse
als Startzeit auswählen. Unsere Animation hat eine Verzögerung von 3.000 ms, was bedeutet,
Die Animation beginnt (oder wird "aktiv"), wenn die Zeitachse `startTime erreicht .
- 3.000
. After that time, the animation engine will animate the given element from the first keyframe (
translateX(0)), through all intermediate keyframes (
translateX(500px)) all the way to the last keyframe (
translateY(500px)) in exactly 2000ms, as prescribed by the
durationoptions. Since we have a duration of 2000ms, we will reach the middle keyframe when the timeline's
currentTimeis
startTime + 3.000 + 1.000and the last keyframe at
startTime + 3.000 + 2.000“. Der Punkt ist, über die Zeitachse, wo wir uns in der Animation befinden.
Sobald die Animation den letzten Keyframe erreicht hat, springt sie zurück zum ersten Keyframe.
Keyframe und starten die nächste Iteration der Animation. Dieser Vorgang wiederholt einen
seit iterations: 3
festgelegt wurde. Wenn wir möchten, dass die Animation
und nie aufhören, würden wir iterations: Number.POSITIVE_INFINITY
schreiben. Hier ist die
Ergebnis des Codes
oben.
WAAPI ist extrem leistungsstark und bietet noch viele weitere Funktionen wie Easing, Start-Offsets, Keyframe-Gewichtungen und Füllverhalten, für den Anwendungsbereich dieses Artikels. Wenn Sie mehr erfahren möchten, empfehlen wir Ihnen, diesen Artikel zu CSS-Animationen auf CSS-Tricks zu lesen.
Animations-Worklet schreiben
Nachdem wir nun das Konzept des Zeitplans kennen, können wir damit beginnen, Animations-Worklet und wie man damit die Zeitleisten ändern kann Die Animation Die Worklet API basiert nicht nur auf WAAPI, sondern ist – im Sinne des Expandable Web – ein untergeordnetes Primitiv, das wird erläutert, wie WAAPI funktioniert. In Bezug auf die Syntax sind sie sehr ähnlich:
Animations-Worklet | WAAPI |
---|---|
new WorkletAnimation( 'passthrough', new KeyframeEffect( document.querySelector('#a'), [ { transform: 'translateX(0)' }, { transform: 'translateX(500px)' } ], { duration: 2000, iterations: Number.POSITIVE_INFINITY } ), document.timeline ).play(); |
new Animation( new KeyframeEffect( document.querySelector('#a'), [ { transform: 'translateX(0)' }, { transform: 'translateX(500px)' } ], { duration: 2000, iterations: Number.POSITIVE_INFINITY } ), document.timeline ).play(); |
Der Unterschied besteht im ersten Parameter, dem Namen des Worklet-Objekts. auf der diese Animation basiert.
Funktionserkennung
Chrome ist der erste Browser, in dem diese Funktion verfügbar ist. Sie müssen also sicherstellen,
erwartet nicht nur AnimationWorklet
. Bevor Sie also die
Worklet nutzen, sollte ermittelt werden, ob der Browser des Nutzers
AnimationWorklet
durch eine einfache Prüfung:
if ('animationWorklet' in CSS) {
// AnimationWorklet is supported!
}
Worklet laden
Worklets sind ein neues Konzept, das von der Houdini-Taskforce eingeführt wurde, um viele einfacher zu erstellen und zu skalieren. In diesem Video gehen wir auf später etwas später, aber der Einfachheit halber sollten Sie sie als günstig und einfache Threads wie Worker.
Wir müssen sicherstellen, dass ein Worklet mit dem Namen "Passthrough" bevor Sie die Animation deklarieren:
// index.html
await CSS.animationWorklet.addModule('passthrough-aw.js');
// ... WorkletAnimation initialization from above ...
// passthrough-aw.js
registerAnimator(
'passthrough',
class {
animate(currentTime, effect) {
effect.localTime = currentTime;
}
}
);
Was passiert hier? Wir registrieren einen Kurs als Animationskünstler mithilfe der
AnimationWorklet-Aufruf registerAnimator()
mit dem Namen "Passthrough".
Das ist der Name, den wir oben im WorkletAnimation()
-Konstruktor verwendet haben. Sobald die
abgeschlossen ist, wird das von addModule()
zurückgegebene Promise aufgelöst und
können wir mit diesem Worklet Animationen erstellen.
Die Methode animate()
der Instanz wird für jeden Frame aufgerufen.
der Browser rendern möchte, wobei die currentTime
der Animationszeitachse übergeben wird
sowie den Effekt, der gerade verarbeitet wird. Wir haben nur eine
KeyframeEffect
und wir verwenden currentTime
, um die
localTime
. Daher wird dieser Animationsfilm auch „Passthrough“ genannt. Mit diesem Code für
wie das Worklet, die WAAPI und das AnimationWorklet oben
wie Sie in den
Demo ansehen.
Zeit
Der currentTime
-Parameter der animate()
-Methode ist der currentTime
-Wert des
an den WorkletAnimation()
-Konstruktor übergeben. In der vorherigen
haben wir gerade die Zeit
für den Effekt übergeben. Aber da dies
JavaScript-Code und Zeit sparen 💫
function remap(minIn, maxIn, minOut, maxOut, v) {
return ((v - minIn) / (maxIn - minIn)) * (maxOut - minOut) + minOut;
}
registerAnimator(
'sin',
class {
animate(currentTime, effect) {
effect.localTime = remap(
-1,
1,
0,
2000,
Math.sin((currentTime * 2 * Math.PI) / 2000)
);
}
}
);
Wir nehmen den Math.sin()
von currentTime
und ordnen diesen Wert neu
Bereich [0; 2000] angegeben. Dies ist der Zeitraum, für den unser Effekt definiert ist. Jetzt
sie ganz anders aussieht, ohne
die Keyframes oder die Optionen
der Animation geändert haben. Der Worklet-Code kann
beliebig komplex sind und es Ihnen ermöglicht, programmatisch zu definieren, welche Effekte
in welcher Reihenfolge und in welchem Ausmaß gespielt wird.
Optionen statt Optionen
Sie können ein Worklet wiederverwenden und seine Nummern ändern. Aus diesem Grund Mit dem WorkletAnimation-Konstruktor können Sie ein Optionsobjekt an das Worklet übergeben:
registerAnimator(
'factor',
class {
constructor(options = {}) {
this.factor = options.factor || 1;
}
animate(currentTime, effect) {
effect.localTime = currentTime * this.factor;
}
}
);
new WorkletAnimation(
'factor',
new KeyframeEffect(
document.querySelector('#b'),
[
/* ... same keyframes as before ... */
],
{
duration: 2000,
iterations: Number.POSITIVE_INFINITY,
}
),
document.timeline,
{factor: 0.5}
).play();
In diesem Beispiel werden beide Animationen mit demselben Code gesteuert, aber mit unterschiedlichen Optionen.
Gib deine Region ein!
Wie ich bereits angedeutet habe, soll das Animations-Worklet
zustandsorientierte Animationen. Animations-Worklets dürfen den Status beibehalten. Eine
Kernfunktionen von Worklets besteht darin, dass sie in eine andere
oder sogar zerstört werden, um Ressourcen zu sparen,
Bundesstaat. Um Zustandsverlust zu vermeiden, bietet das Animations-Worklet einen Hook, der
wird aufgerufen, bevor ein Worklet gelöscht wird, mit dem Sie einen Status zurückgeben können.
-Objekt enthält. Dieses Objekt wird an den Konstruktor übergeben, wenn das Worklet
neu erstellt. Bei der ersten Erstellung ist dieser Parameter undefined
.
registerAnimator(
'randomspin',
class {
constructor(options = {}, state = {}) {
this.direction = state.direction || (Math.random() > 0.5 ? 1 : -1);
}
animate(currentTime, effect) {
// Some math to make sure that `localTime` is always > 0.
effect.localTime = 2000 + this.direction * (currentTime % 2000);
}
destroy() {
return {
direction: this.direction,
};
}
}
);
Jedes Mal, wenn Sie diese Demo aktualisieren, haben Sie eine
in welche Richtung sich das Quadrat dreht. Wenn der Browser entfernt wird
zu einem anderen Thread migrieren, gäbe es einen weiteren
Math.random()
ruft beim Erstellen auf, was zu einer plötzlichen Änderung von
Richtung. Um das zu vermeiden, geben wir die Animationen
zufällig ausgewählte Richtung als state und verwendet ihn im Konstruktor, falls angegeben.
Einbinden in das Raum-Zeit-Kontinuum: ScrollTimeline
Wie im vorherigen Abschnitt gezeigt wurde, können wir mit AnimationWorklet
programmatisch definieren, wie sich eine Verzögerung des Zeitplans auf die Auswirkungen der
Animation. Bisher war unsere Zeitachse jedoch immer document.timeline
, was
erfasst die Zeit.
ScrollTimeline
eröffnet neue Möglichkeiten und ermöglicht Ihnen, Animationen zu fördern
durch Scrollen statt durch Zeit. Wir werden unsere allererste
"Passthrough" Worklet dafür
Demo ansehen:
new WorkletAnimation(
'passthrough',
new KeyframeEffect(
document.querySelector('#a'),
[
{
transform: 'translateX(0)',
},
{
transform: 'translateX(500px)',
},
],
{
duration: 2000,
fill: 'both',
}
),
new ScrollTimeline({
scrollSource: document.querySelector('main'),
orientation: 'vertical', // "horizontal" or "vertical".
timeRange: 2000,
})
).play();
Anstatt document.timeline
zu übergeben, erstellen wir eine neue ScrollTimeline
.
Wahrscheinlich vermutest du es: ScrollTimeline
verbraucht keine Zeit, aber die
Die Scroll-Position von scrollSource
, um den currentTime
im Worklet festzulegen. Das
ganz nach oben (oder links) gescrollt ist, bedeutet currentTime = 0
, während
ganz nach unten (oder nach rechts) gescrollt wird, wird für currentTime
Folgendes festgelegt:
timeRange
Wenn Sie durch das Feld scrollen,
Demo können Sie
die Position der roten Box.
Wenn Sie ein ScrollTimeline
mit einem Element erstellen, das nicht scrollt,
currentTime
der Zeitachse beträgt NaN
. Besonders beim responsiven Design
Denken Sie daran, dass Sie immer auf NaN
als currentTime
vorbereitet sein sollten. Oft ist es
ist sinnvoll,
einen Wert von 0 zu verwenden.
Lange Zeit wurde schon lange versucht, Animationen mit der Scrollposition zu verbinden, aber nie wirklich erreicht wurde (abgesehen von kniffligen Behelfslösungen mit CSS3D). Mit dem Animations-Worklet können diese Effekte einfach implementiert werden und gleichzeitig leistungsstark sind. Hier einige Beispiele: einen Parallaxe-Scrolleffekt wie diesen demo zeigt, dass es mit nur wenigen Zeilen eine scrollgesteuerte Animation definieren.
Details
Worklets
Worklets sind JavaScript-Kontexte mit einem isolierten Bereich und einer sehr kleinen API. Oberfläche. Die kleine API-Oberfläche ermöglicht eine aggressivere Optimierung insbesondere auf Low-End-Geräten. Außerdem sind Worklets nicht an in einer bestimmten Ereignisschleife, kann jedoch bei Bedarf zwischen Threads verschoben werden. Dies ist besonders wichtig für AnimationWorklet.
Compositor-NSync
Sie wissen, dass bestimmte CSS-Eigenschaften schnell animiert werden können, während andere nicht. Einige Eigenschaften müssen nur etwas auf der GPU bearbeitet werden, damit sie animiert sind, während andere den Browser dazu zwingen, das Layout des gesamten Dokuments neu zu erstellen.
Wie in vielen anderen Browsern gibt es in Chrome einen Prozess namens „Compositor“. die Aufgabe ist – und ich mache hier viel einfacher – das Anordnen von Ebenen und Texturen erstellen und den Bildschirm dann so regelmäßig wie möglich aktualisieren. idealerweise so schnell wie der Bildschirm aktualisiert werden kann (normalerweise 60 Hz). Je nachdem, CSS-Eigenschaften animiert werden, benötigt der Browser möglicherweise nur das Symbol compositor funktioniert, während andere Eigenschaften ein Layout ausführen müssen, Operation, die nur der Hauptthread ausführen kann. Je nachdem, welche Properties möchten, ist Ihr Animations-Worklet entweder an das Haupt- oder oder in einem separaten Thread synchron mit dem Compositor ausgeführt werden.
Schlag ans Handgelenk
Normalerweise gibt es nur einen Compositor-Prozess, der potenziell Tabs zu erstellen, da die GPU eine stark umkämpfte Ressource ist. Wenn der Compositor irgendwie blockiert wird, bricht der gesamte Browser zum Stillstand und reagiert nicht mehr auf Nutzereingabe. Dies muss um jeden Preis vermieden werden. Was passiert, wenn Ihre Worklet kann die Daten, die der Compositor benötigt, nicht rechtzeitig bereitstellen, damit der Frame zu sehen ist. gerendert?
In diesem Fall darf das Worklet – gemäß Spezifikation – „slip“. Er fällt hinterher und darf die Daten des letzten Frames wiederverwenden, die Framerate hoch halten. Visuell sieht das nach einer Verzögerung aus, aber die große Der Unterschied besteht darin, dass der Browser immer noch auf Nutzereingaben reagiert.
Fazit
AnimationWorklet hat viele Facetten und bietet viele Vorteile im Web. Die offensichtlichen Vorteile sind mehr Kontrolle über Animationen und neue Fahrmöglichkeiten. und Animationen, um ein neues Level visueller Fidelity im Web zu schaffen. Die APIs können Sie außerdem dafür sorgen, dass Ihre App resistenter gegen Verzögerungen ist, während Zugriff auf alle neuen Vorteile zu erhalten.
Animation Worklet befindet sich in Canary und wir planen einen Ursprungstest mit Chrome 71 Wir freuen uns auf Ihre tollen neuen Weberlebnisse und was wir verbessern können. Es gibt auch eine Polyfill mit derselben API, aber nicht der Leistungsisolierung.
Denken Sie daran, dass CSS-Übergänge und CSS-Animationen weiterhin gültig sind. und kann für einfache Animationen viel einfacher sein. Wenn Sie jedoch AnimationWorklet unterstützt Sie dabei.