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

View in English Always switch to English

CSS- und JavaScript-Animationsperformance

Animationen sind entscheidend für eine angenehme Benutzererfahrung in vielen Anwendungen. Es gibt viele Möglichkeiten, Webanimationen zu implementieren, wie beispielsweise CSS Übergängen/Animationen oder auf JavaScript basierende Animationen (mit requestAnimationFrame()). In diesem Artikel analysieren wir die Leistungsunterschiede zwischen CSS-basierten und JavaScript-basierten Animationen.

CSS-Übergänge und -Animationen

Sowohl CSS-Übergänge als auch -Animationen können verwendet werden, um Animationen zu schreiben. Sie haben jeweils ihre eigenen Einsatzszenarien:

  • CSS Übergänge bieten eine einfache Möglichkeit, Animationen zwischen dem aktuellen Stil und einem End-CSS-Zustand zu erstellen, z. B. einem ruhenden Button-Zustand und einem Hover-Zustand. Selbst wenn sich ein Element mitten in einem Übergang befindet, beginnt der neue Übergang sofort aus dem aktuellen Stil, anstatt zum End-CSS-Zustand zu springen. Siehe Verwendung von CSS-Übergängen für weitere Details.
  • CSS Animationen hingegen ermöglichen es Entwicklern, Animationen zwischen einer Menge von Anfangseigenschaften und einem endgültigen Satz zu erstellen, anstatt zwischen zwei Zuständen. CSS-Animationen bestehen aus zwei Komponenten: einem Stil, der die CSS-Animation beschreibt, und einer Reihe von Schlüsselbildern, die die Start- und Endzustände des Animationsstils sowie mögliche Zwischenpunkte anzeigen. Siehe Verwendung von CSS-Animationen für weitere Details.

In Bezug auf die Leistung gibt es keinen Unterschied zwischen der Implementierung einer Animation mit CSS-Übergängen oder -Animationen. Beide werden in diesem Artikel unter demselben CSS-basierten Dach klassifiziert.

requestAnimationFrame

Die requestAnimationFrame() API bietet eine effiziente Möglichkeit, Animationen in JavaScript zu erstellen. Die Callback-Funktion der Methode wird vom Browser vor dem nächsten Umzeichnen bei jedem Frame aufgerufen. Im Vergleich zu setTimeout()/setInterval(), die einen spezifischen Verzögerungsparameter benötigen, ist requestAnimationFrame() wesentlich effizienter. Entwickler können eine Animation erstellen, indem sie den Stil eines Elements bei jedem Aufruf der Schleife ändern (oder das Canvas-Zeichnen aktualisieren, oder was auch immer).

Hinweis: Wie CSS-Übergänge und -Animationen pausiert requestAnimationFrame(), wenn der aktuelle Tab in den Hintergrund geschoben wird.

Für weitere Details lesen Sie animating with JavaScript from setInterval to requestAnimationFrame.

Leistungsvergleich:
Übergänge vs. requestAnimationFrame

Die Tatsache ist, dass die Leistung von CSS-basierten Animationen in den meisten Fällen fast dieselbe ist wie die von JavaScript-Animationen — zumindest in Firefox. Einige JavaScript-basierte Animationsbibliotheken, wie GSAP und Velocity.JS, behaupten sogar, bessere Leistungen als native CSS-Übergänge/-Animationen erzielen zu können. Dies kann auftreten, weil CSS-Übergänge/-Animationen die Stilelemente im Haupt-UI-Thread vor jedem Umzeichnungsereignis neu abtasten, was fast dasselbe ist wie das Proben von Stilelementen über einen requestAnimationFrame()-Callback, der ebenfalls vor dem nächsten Umzeichnen ausgelöst wird. Wenn beide Animationen im Haupt-UI-Thread stattfinden, gibt es leistungstechnisch keinen Unterschied.

In diesem Abschnitt führen wir Sie durch einen Leistungstest in Firefox, um zu sehen, welche Animationsmethode insgesamt besser zu sein scheint.

Aktivierung der FPS-Tools

Bevor Sie das Beispiel durchgehen, aktivieren Sie bitte zuerst die FPS-Tools, um die aktuelle Bildrate zu sehen:

  1. Geben Sie in der URL-Leiste about:config ein; klicken Sie auf die Schaltfläche Ich werde vorsichtig sein, versprochen!, um den Konfigurationsbildschirm zu betreten. Warnbildschirm, dass das Ändern von Einstellungen riskant sein kann, mit einer Schaltfläche zur Annahme der Risiken.
  2. Suchen Sie in der Suchleiste nach der Einstellung layers.acceleration.draw-fps.
  3. Doppelklicken Sie auf den Eintrag, um den Wert auf true zu setzen. Jetzt sehen Sie drei kleine violette Kästchen in der oberen linken Ecke des Firefox-Fensters. Die erste Box repräsentiert FPS. Die Eingabe des Suchbegriffs filtert die Optionen. Nur die layers.acceleration.draw-fps-Einstellung wird angezeigt und ist auf true gesetzt. Drei Zahlen (001, 001 und 108) erscheinen in der oberen linken Ecke des Browsers und überlagern die Benutzeroberfläche.

Durchführung des Leistungstests

Anfangs im unten gezeigten Test werden insgesamt 1000 <div>-Elemente durch CSS-Animation transformiert.

js
const boxes = [];
const button = document.getElementById("toggle-button");
const boxContainer = document.getElementById("box-container");
const animationType = document.getElementById("type");

// create boxes
for (let i = 0; i < 1000; i++) {
  const div = document.createElement("div");
  div.classList.add("css-animation");
  div.classList.add("box");
  boxContainer.appendChild(div);
  boxes.push(div.style);
}

let toggleStatus = true;
let rafId;
button.addEventListener("click", () => {
  if (toggleStatus) {
    animationType.textContent = " requestAnimationFrame";
    for (const child of boxContainer.children) {
      child.classList.remove("css-animation");
    }
    rafId = window.requestAnimationFrame(animate);
  } else {
    window.cancelAnimationFrame(rafId);
    animationType.textContent = " CSS animation";
    for (const child of boxContainer.children) {
      child.classList.add("css-animation");
    }
  }
  toggleStatus = !toggleStatus;
});

const duration = 6000;
const translateX = 500;
const rotate = 360;
const scale = 1.4 - 0.6;
let start;
function animate(time) {
  if (!start) {
    start = time;
    rafId = window.requestAnimationFrame(animate);
    return;
  }

  const progress = (time - start) / duration;
  if (progress < 2) {
    let x = progress * translateX;
    let transform;
    if (progress >= 1) {
      x = (2 - progress) * translateX;
      transform = `translateX(${x}px) rotate(${
        (2 - progress) * rotate
      }deg) scale(${0.6 + (2 - progress) * scale})`;
    } else {
      transform = `translateX(${x}px) rotate(${progress * rotate}deg) scale(${
        0.6 + progress * scale
      })`;
    }

    for (const box of boxes) {
      box.transform = transform;
    }
  } else {
    start = null;
  }
  rafId = window.requestAnimationFrame(animate);
}

Die Animation kann durch Klicken auf die Umschalttaste auf requestAnimationFrame() umgeschaltet werden.

Versuchen Sie jetzt, beide auszuführen, und vergleichen Sie die FPS für jede (die erste violette Box). Sie sollten sehen, dass die Leistung von CSS-Animationen und requestAnimationFrame() sehr nahe beieinander liegt.

Animation außerhalb des Haupt-Threads

Selbst angesichts der obigen Testergebnisse würden wir argumentieren, dass CSS-Animationen die bessere Wahl sind. Aber wie? Der Schlüssel ist, dass solange die Eigenschaften, die wir animieren möchten, keinen Reflow/Neuzeichnungen auslösen (lesen Sie CSS triggers für weitere Informationen), können wir diese Probenoperationen aus dem Haupt-Thread heraus bewegen. Die häufigste Eigenschaft ist das CSS-transform. Wenn ein Element als Layer gefördert wird, kann die Animation von transform-Eigenschaften auf der GPU erfolgen, was zu einer besseren Leistung/Effizienz führt, insbesondere auf mobilen Geräten. Weitere Details finden Sie in OffMainThreadCompositing.

Um die OMTA (Off Main Thread Animation) in Firefox zu aktivieren, können Sie about:config aufrufen und nach layers.offmainthreadcomposition.async-animations suchen. Wechseln Sie seinen Wert auf true.

Die Eingabe des Suchbegriffs filtert die Optionen. Nur die layers.offmainthreadcomposition.async-animations-Einstellung wird angezeigt und ist auf true gesetzt. Die drei Zahlen in der oberen linken Ecke des Browsers, oberhalb der Benutzeroberfläche, haben sich auf 005, 003 und 108 erhöht.

Nach der Aktivierung von OMTA versuchen Sie, den obigen Test erneut auszuführen. Sie sollten sehen, dass die FPS der CSS-Animationen jetzt erheblich höher sind.

Hinweis: In Nightly/Developer Edition sollte OMTA standardmäßig aktiviert sein, sodass Sie die Tests möglicherweise andersherum durchführen müssen (zuerst mit aktivierter OMTA testen, dann deaktivieren, um ohne OMTA zu testen).

Zusammenfassung

Browser sind in der Lage, Renderflows zu optimieren. Zusammenfassend sollten wir immer versuchen, unsere Animationen soweit möglich mit CSS-Übergängen/-Animationen zu erstellen. Wenn Ihre Animationen wirklich komplex sind, müssen Sie möglicherweise auf JavaScript-basierte Animationen zurückgreifen.