Animations-Worklet von Houdini

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 thedurationoptions. Since we have a duration of 2000ms, we will reach the middle keyframe when the timeline'scurrentTimeisstartTime + 3.000 + 1.000and the last keyframe atstartTime + 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.