Zum Inhalt springen
Core Web Vitals Spezialisten
Frontend

Flüssige Animationen: Ruckeln im Frontend stoppen

Flüssige Animationen ohne Ruckeln: das 16,7-ms-Frame-Budget, warum nur transform und opacity günstig sind und wie Sie Frontend-Jank in den DevTools aufspüren.

12 Min. Lesezeit FrontendAnimationenRenderingCompositor60fps

Eine Website kann blitzschnell laden und sich trotzdem zäh anfühlen. Sobald ein Menü aufklappt, eine Liste scrollt oder ein Karussell gleitet, entscheidet nicht die Ladezeit über den Eindruck, sondern die Renderleistung Bild für Bild. Ruckeln - im Fachjargon Jank - entsteht, wenn der Browser ein neues Bild nicht innerhalb seines knappen Zeitfensters fertigstellt. Bei 60 Bildern pro Sekunde bleiben je Frame nur 16,7 Millisekunden (web.dev). Dieser Artikel zeigt, warum lediglich zwei CSS-Eigenschaften günstig auf dem Compositor-Thread laufen, wie Layout-Thrashing entsteht und wie sich Jank in den DevTools gezielt aufspüren lässt. Während der Beitrag zu Interaction to Next Paint die Reaktionszeit auf Eingaben behandelt, geht es hier um die visuelle Glätte von Animationen und Scrollen - also um Layout, Paint und Composite.

Ruckelfrei bei 60 fps - das 16,7-ms-Frame-BudgetEin Frame: von JavaScript bis zum fertigen PixelJavaScriptStyleLayoutPaintCompositeBrowser-Reserve ~6 ms10 ms sicheres Budget16,7 ms = 1 FrameWelche CSS-Eigenschaft welche Arbeit auslöstGünstig: nur Compositingtransform, opacityStyleLayoutPaintCompositekein Layout, kein Paint - stabil bei 60 fpsArbeit nur auf dem Compositor-ThreadTeuer: voller Durchlauftop, left, width, height, box-shadowStyleLayoutPaintCompositejede Änderung: Layout + Paint + Compositeverpasst leicht das 16,7-ms-BudgetGleichmäßige Frames (60 fps) gegen Ruckeln (verpasste Frames)60 fpsRuckelnVier Hebel gegen Jankwill-change gezielt | Lesen dann Schreiben | requestAnimationFrame | DevTools Performance-Panel

Das 16,7-ms-Budget: warum 60 fps so wenig Zeit lassen

Ein typischer Bildschirm aktualisiert sein Bild 60-mal pro Sekunde. Der Browser muss also alle 16,7 Millisekunden ein fertiges Bild liefern - das ergibt sich aus 1000 Millisekunden geteilt durch 60 (web.dev). Von diesem Fenster geht jedoch ein Teil für die interne Verwaltung des Browsers ab, sodass in der Praxis nur rund 10 Millisekunden pro Frame für die eigene Arbeit bleiben (Chrome for Developers). Wird dieses Budget überschritten, kann der Frame nicht rechtzeitig fertig werden: Das Bild bleibt stehen, springt oder zittert - der Effekt, den Nutzer als Ruckeln wahrnehmen. Auf modernen Displays mit 120 Hertz halbiert sich das Fenster sogar auf rund 8,3 Millisekunden, was die Anforderung weiter verschärft (Chrome for Developers).

Anders als bei der Ladezeit zählt hier nicht ein einzelner Zeitpunkt, sondern die Gleichmäßigkeit über viele Frames hinweg. Eine Animation mit 60 fps wirkt flüssig, weil jeder Frame pünktlich erscheint. Fällt schon jeder dritte Frame aus, weil eine Berechnung zu lange dauert, sinkt die wahrgenommene Bildrate spürbar und die Bewegung wirkt hakelig. Der Browser verwirft dann Frames, um wieder aufzuholen - sichtbar als Sprung. Genau deshalb ist die Renderleistung eine eigene Disziplin der Frontend-Optimierung, die sich nicht durch schnelleres Laden allein lösen lässt.

Ladezeit und Renderglätte sind zwei Baustellen

Eine Seite kann einen sehr guten LCP-Wert haben und trotzdem beim Scrollen ruckeln. Die Ladezeit misst, wie schnell der erste Eindruck steht; die Renderglätte misst, ob jede folgende Bewegung im 16,7-ms-Takt bleibt. Beide Ziele erfordern unterschiedliche Maßnahmen - dieser Beitrag konzentriert sich auf die zweite Baustelle, die Arbeit pro Frame.

Die Rendering-Pipeline: von JavaScript bis zum Pixel

Damit aus HTML und CSS sichtbare Pixel werden, durchläuft der Browser eine feste Abfolge von Schritten - die sogenannte Rendering-Pipeline (web.dev). Am Anfang steht die Ausführung von JavaScript, das oft die visuelle Aenderung anstößt. Danach folgen vier Phasen: Style berechnet, welche CSS-Regeln für jedes Element gelten. Layout ermittelt Größe und Position jedes Elements - eine Aenderung hier kann die Geometrie weiter Teile der Seite beeinflussen. Paint füllt die Pixel mit Farben, Text und Bildern. Composite setzt die einzelnen Ebenen schließlich zum fertigen Bild zusammen (web.dev). Entscheidend ist die Reihenfolge: Je früher in dieser Kette eine Aenderung ansetzt, desto mehr Folgearbeit löst sie aus.

Style

Der Browser bestimmt, welche CSS-Regeln für jedes Element gelten - Farbe, Größe, Sichtbarkeit und mehr. Aenderungen an Selektoren oder vererbten Werten stoßen diese Phase an.

Layout

Hier werden Größe und Position jedes Elements berechnet. Weil sich Elemente gegenseitig beeinflussen, kann eine einzige Aenderung die Geometrie weiter Teile der Seite neu anstoßen - die teuerste Phase.

Paint

Der Browser füllt die Pixel: Text, Farben, Rahmen, Schatten und Bilder werden in Ebenen gezeichnet. Große oder komplexe Flächen kosten hier spürbar Zeit.

Composite

Die fertig gezeichneten Ebenen werden zum sichtbaren Bild zusammengesetzt. Dieser Schritt läuft besonders effizient und kann Teile der Arbeit der Grafikkarte überlassen.

Der günstigste Weg durch die Pipeline vermeidet also sowohl Layout als auch Paint und beschränkt sich auf das Compositing (web.dev). Wer eine Bewegung so gestaltet, dass sie nur die letzte Phase berührt, spart dem Browser in jedem einzelnen Frame die teure Neuberechnung der Geometrie und das Neuzeichnen der Pixel. Das ist der Kern jeder ruckelfreien Animation - und der Grund, warum die Wahl der animierten Eigenschaft so viel wichtiger ist als die Anzahl der Animationen.

Nur transform und opacity: der Compositor-Thread

Es gibt genau zwei Eigenschaften, deren Aenderung der Browser allein auf der Compositing-Stufe verarbeiten kann: transform und opacity (web.dev). Voraussetzung ist, dass das bewegte Element auf einer eigenen Compositor-Ebene liegt. Alle anderen Eigenschaften ziehen früher in der Pipeline Arbeit nach sich - entweder ein neues Paint oder, noch teurer, einen kompletten Layout-Durchlauf. Wer eine Karte beim Hover anheben will, sollte sie deshalb über transform: translateY() verschieben statt über die Eigenschaft top, und eine Ein- oder Ausblendung über opacity statt über Sichtbarkeitstricks umsetzen.

CSS-EigenschaftLöst ausVerarbeitet aufKosten pro Frame
transformnur CompositeCompositor-Threadsehr günstig
opacitynur CompositeCompositor-Threadsehr günstig
color, background-colorPaint + CompositeMain Threadmittel
box-shadow, border-radiusPaint + CompositeMain Threadmittel bis hoch
width, height, paddingLayout + Paint + CompositeMain Threadhoch
top, left, marginLayout + Paint + CompositeMain Threadhoch

Der Grund für diesen Unterschied liegt in der Architektur des Browsers. Das Compositing kann auf einem eigenen Thread und mit Unterstützung der Grafikkarte laufen, unabhängig vom stark belasteten Main Thread. transform und opacity verändern nur, wie eine bereits gezeichnete Ebene positioniert oder durchsichtig dargestellt wird - der Inhalt der Ebene selbst bleibt unverändert (web.dev). Eine Animation von width oder top zwingt den Browser dagegen, in jedem Frame die Geometrie neu zu berechnen und die betroffenen Bereiche neu zu zeichnen. Bei komplexen Seiten reicht das oft aus, um das 10-Millisekunden-Budget zu sprengen und sichtbares Ruckeln zu erzeugen. Auch der Beitrag zur Vermeidung von Layout Shift zeigt, wie eng Geometrie-Aenderungen und visuelle Störungen zusammenhängen.

compositor-vs-layout.css
/* Teuer: erzwingt Layout und Paint in jedem Frame */
.karte-langsam {
  transition: top 200ms ease, width 200ms ease;
}
.karte-langsam:hover {
  top: -8px;
  width: 320px;
}

/* Guenstig: laeuft auf dem Compositor-Thread */
.karte-schnell {
  transition: transform 200ms ease;
}
.karte-schnell:hover {
  transform: translateY(-8px) scale(1.03);
}

will-change gezielt statt flächendeckend

Damit eine Animation ausschließlich das Compositing berührt, muss das Element auf einer eigenen Ebene liegen. Der Browser legt solche Ebenen normalerweise selbst an, sobald er sie braucht. Mit der CSS-Eigenschaft will-change lässt sich dem Browser vorab mitteilen, dass sich eine bestimmte Eigenschaft ändern wird, damit er die Ebene rechtzeitig vorbereitet. Das kann den ersten Frame einer Animation glätten, weil die Vorbereitung nicht mehr mitten in die Bewegung fällt. Der Nutzen kippt jedoch schnell ins Gegenteil, wenn will-change zu großflächig eingesetzt wird.

will-change ist ein letztes Mittel, kein Standard

Laut MDN Web Docs sollte will-change sparsam und als letztes Mittel gegen bestehende Performance-Probleme eingesetzt werden - nicht vorbeugend (MDN Web Docs). Jede so erzeugte Ebene kostet Speicher und Verwaltungsaufwand. Eine Regel wie * { will-change: transform; } über alle Elemente erzeugt unzählige Ebenen und kann die Leistung auf speicherarmen Geräten verschlechtern statt verbessern (MDN Web Docs).
will-change-gezielt.js
// will-change kurz vor der Animation setzen und danach wieder entfernen
const panel = document.querySelector('.panel');

panel.addEventListener('pointerenter', () => {
  panel.style.willChange = 'transform';
});

panel.addEventListener('transitionend', () => {
  // Ebene wieder freigeben - spart Speicher
  panel.style.willChange = 'auto';
});

Der richtige Umgang ist also punktgenau: will-change nur auf die wenigen Elemente anwenden, die sich tatsächlich bewegen, und die Ebene nach der Animation wieder freigeben. Bei kurzen, seltenen Übergängen genügt oft der Verzicht auf will-change ganz, weil der Browser die Ebene ohnehin rechtzeitig anlegt. Ob eine Promotion sich lohnt, lässt sich in den DevTools an der Zahl der Compositor-Ebenen ablesen - dazu weiter unten mehr.

Layout-Thrashing vermeiden: erst lesen, dann schreiben

Selbst wenn Animationen sauber auf transform setzen, kann JavaScript den Main Thread ausbremsen. Ein häufiges Muster ist das erzwungene synchrone Layout (forced synchronous layout): Der Code ändert einen Stil und liest unmittelbar danach eine Layout-Eigenschaft wie offsetWidth oder getBoundingClientRect aus (web.dev). Weil der Browser für die Antwort das aktuelle Layout kennen muss, berechnet er es sofort neu, statt bis zum nächsten Frame zu warten. Passiert das mehrfach in schneller Folge - etwa in einer Schleife -, spricht man von Layout-Thrashing: Der Browser rechnet dieselbe Geometrie wieder und wieder neu und verliert dabei kostbare Millisekunden.

lesen-dann-schreiben.js
// Layout-Thrashing: Lesen und Schreiben wechseln sich ab (langsam)
elemente.forEach((el) => {
  const breite = el.offsetWidth;         // Lesen erzwingt Layout
  el.style.width = breite + 20 + 'px';   // Schreiben macht Layout ungueltig
});

// Besser: erst alle Lesezugriffe, dann alle Schreibzugriffe
const breiten = elemente.map((el) => el.offsetWidth); // nur Lesen
requestAnimationFrame(() => {
  elemente.forEach((el, i) => {
    el.style.width = breiten[i] + 20 + 'px';           // nur Schreiben
  });
});

Die Lösung besteht darin, Lese- und Schreibzugriffe zu trennen: zuerst alle Messwerte einsammeln, dann alle Aenderungen vornehmen (web.dev). So muss der Browser das Layout höchstens einmal statt einmal pro Element neu berechnen. Bei umfangreichen DOM-Strukturen zahlt sich zusätzlich eine schlanke Struktur aus - je weniger Knoten der Browser bei einem Layout-Durchlauf berücksichtigen muss, desto schneller ist er fertig. Wie sich das erreichen lässt, beschreibt der Beitrag zum Reduzieren der DOM-Größe.

Die Faustregel: Batch statt Wechsel

Ordnen Sie DOM-Zugriffe so an, dass alle Lesevorgänge zusammen und alle Schreibvorgänge zusammen laufen. Ein einziger Wechsel zwischen Schreiben und Lesen mitten in einer Schleife kann ein erzwungenes Layout auslösen und aus einer schnellen Interaktion eine ruckelnde machen. Das Prinzip ist einfach zu merken und einer der wirksamsten Hebel gegen Jank auf dem Main Thread.

requestAnimationFrame: im Takt des Bildschirms animieren

Wer eine Bewegung in JavaScript steuert, sollte sie mit requestAnimationFrame an die Bildwiederholung des Displays koppeln. Der Browser ruft die übergebene Funktion genau vor dem nächsten Neuzeichnen auf und synchronisiert sie mit der Bildrate (MDN Web Docs). Das hat zwei Vorteile: Die Animation läuft im richtigen Takt - bei 60 Hz rund alle 16,7 Millisekunden, bei 120 Hz entsprechend häufiger -, und der Browser pausiert die Aufrufe automatisch, sobald der Tab in den Hintergrund wechselt. Der klassische Ansatz mit setInterval oder setTimeout kennt die Bildrate nicht und liefert daher entweder zu viele oder zu wenige Aktualisierungen, was sich als Ruckeln zeigt.

Für Effekte, die vom Scrollen abhängen, gilt eine verwandte Regel: Die eigentliche visuelle Aenderung gehört in einen requestAnimationFrame-Rückruf, während der Scroll-Listener selbst nur den aktuellen Wert festhält. So läuft die teure Arbeit höchstens einmal pro Frame statt bei jedem einzelnen Scroll-Ereignis. Ergänzend halten passive Event-Listener den Scroll-Pfad frei, weil der Browser nicht auf ein mögliches Abbrechen des Standardverhaltens warten muss. In Kombination sorgen diese Techniken dafür, dass auch datenintensive Oberflächen flüssig scrollen.

Flüssigkeit entsteht nicht dadurch, dass man mehr rechnet, sondern dadurch, dass man im richtigen Moment und mit den richtigen Eigenschaften rechnet. Der Bildschirm gibt den Takt vor - unsere Aufgabe ist es, jeden Frame vor Ablauf des Budgets fertig zu haben.

Aus der Projektarbeit zur Frontend-Performance

Jank in den DevTools aufspüren

Ruckeln lässt sich nicht erraten, sondern muss gemessen werden. Das Performance-Panel der Browser-Entwicklerwerkzeuge zeichnet einen kompletten Ablauf auf und zeigt in der Frames-Spur, welche Frames zu lange gebraucht haben. Rote Markierungen und ungewöhnlich lange Balken kennzeichnen verpasste Frames. Im Main-Thread-Bereich verraten lange Blöcke, in welcher Phase die Zeit verloren geht - dunkle Blöcke für Layout, andere für Paint. Besonders wertvoll sind die Hinweise auf ein erzwungenes Layout (forced reflow), die das Werkzeug direkt an der verursachenden Stelle anzeigt (Chrome for Developers).

  • Das Performance-Panel öffnen und während der ruckelnden Interaktion eine Aufzeichnung starten
  • In der Frames-Spur nach roten Markierungen und langen Balken suchen - sie kennzeichnen verpasste Frames
  • Lange Layout- und Paint-Blöcke im Main-Thread-Bereich der jeweiligen Phase zuordnen
  • Warnungen zu erzwungenem Layout (forced reflow) im Detailbereich beachten (Chrome for Developers)
  • Im Rendering-Werkzeug Paint Flashing aktivieren, um unnötig neu gezeichnete Bereiche sichtbar zu machen
  • Die Ebenen-Ansicht prüfen und die Zahl der Compositor-Ebenen im Blick behalten

Ergänzend hilft das Rendering-Werkzeug: Mit aktiviertem Paint Flashing hebt der Browser jeden neu gezeichneten Bereich farbig hervor - blinkt bei einer Animation die halbe Seite, wird zu viel neu gezeichnet. Ein FPS-Zähler zeigt die aktuelle Bildrate in Echtzeit. Für Animationen, die unnötig Layout oder Paint erzwingen, meldet auch das Lighthouse-Audit eine entsprechende Warnung zu nicht-komposierten Animationen (Chrome for Developers). Diese Werkzeuge zusammen machen aus einem diffusen Ruckeln eine konkrete, adressierbare Ursache - der Ausgangspunkt jeder belastbaren Performance-Analyse.

Ein typischer Entruckelungs-Pfad in der Praxis

Ein wiederkehrendes Muster verdeutlicht, wie die Maßnahmen zusammenspielen. Ausgangslage: Ein Online-Shop zeigt beim Aufklappen des Mega-Menüs und beim Hover über Produktkacheln ein deutliches Ruckeln. Die Aufzeichnung im Performance-Panel weist verpasste Frames aus, sobald die Kacheln animieren. Die Ursache: Der Hover-Effekt bewegt die Kacheln über die Eigenschaft top und blendet gleichzeitig einen weichen box-shadow ein - beides erzwingt Layout beziehungsweise Paint in jedem Frame (Projekterfahrung). Bei einer langen Produktliste summiert sich diese Arbeit, bis das Frame-Budget reißt.

Der Umbau setzt an drei Punkten an. Erstens wird der Hover-Effekt von top auf transform: translateY() umgestellt und der Schatten über eine bereits vorhandene, per opacity ein- und ausgeblendete Schatten-Ebene gelöst, sodass die Bewegung nur noch das Compositing berührt. Zweitens erhält die aktive Kachel kurz vor der Animation ein gezieltes will-change und gibt es danach wieder frei. Drittens wird eine scrollabhängige Effektberechnung in einen requestAnimationFrame-Rückruf verschoben. In der Summe läuft die Interaktion wieder gleichmäßig im 60-fps-Takt - ohne dass eine Funktion entfernt wurde (Projekterfahrung). Vergleichbare Ausgangslagen begegnen uns quer durch Shop- und Website-Projekte, etwa bei der Shopware-Performance oder bei WordPress-Bremsen.

Renderglätte und Interaktivität ergänzen sich

Ruckelfreie Animationen und schnelle Reaktionszeiten hängen zusammen, sind aber nicht dasselbe. Wer Arbeit vom Main Thread nimmt, verbessert sowohl die Bildrate als auch die Interaction to Next Paint. Eine Animation auf dem Compositor-Thread blockiert den Main Thread nicht und lässt damit Raum für die Verarbeitung von Eingaben. Beide Ziele gemeinsam zu verfolgen, ergibt eine Oberfläche, die sich rundum wertig anfühlt.

Flüssige Animationen dauerhaft halten

Ein guter Zustand ist kein Endzustand. Jede neue Komponente, jedes Design-Update und jedes zusätzliche Skript kann eine teure Eigenschaft animieren oder ein erzwungenes Layout einschleusen. Deshalb gehört die Renderleistung in eine feste Routine: Vor jedem größeren Release die kritischen Interaktionen im Performance-Panel prüfen und die Zahl der animierten Nicht-Compositor-Eigenschaften niedrig halten. Ein Performance-Budget kann festlegen, dass Animationen ausschließlich transform und opacity nutzen - Abweichungen fallen dann in der Überprüfung auf, bevor sie live gehen. So bleibt die einmal erreichte Glätte über viele Versionen erhalten und passt zu einer messbaren Core-Web-Vitals-Strategie.

Die größten Fortschritte entstehen meist dort, wo Diagnose und Umsetzung Hand in Hand gehen: erst die ruckelnden Frames im Werkzeug sichtbar machen, dann die verursachenden Eigenschaften austauschen, schließlich das Ergebnis erneut messen. Genau diesen Dreiklang setzen wir in der Frontend-Optimierung für Shops und Unternehmenswebsites um - eingebettet in ein breiteres Spektrum an Performance-Leistungen. Für eine erste Einschätzung der Renderleistung Ihrer wichtigsten Templates vereinbaren Sie ein unverbindliches Erstgespräch.

Quellen und Studien

Dieser Artikel basiert auf Daten aus: web.dev (Rendering Performance, Stick to Compositor-Only Properties, Avoid Large Complex Layouts and Layout Thrashing), Chrome for Developers (Rendering-Pipeline, Forced Reflow, Lighthouse Non-Composited Animations) und den MDN Web Docs (will-change, requestAnimationFrame, Animation Performance and Frame Rate). Die technischen Schwellenwerte entsprechen dem Stand Mai 2026.