Warum manche Animationen ruckeln und andere butterweich laufen
Jede Animation durchläuft im Browser die Rendering-Pipeline aus Layout, Paint und Composite, und wer width, top oder margin animiert, zwingt den Browser bei jedem einzelnen Frame zu teuren Neuberechnungen. Dieser Artikel zeigt, warum transform und opacity fast kostenlos auf dem Compositor-Thread laufen, wie will-change korrekt eingesetzt wird und wie sich bestehende Animationen gezielt refactoren lassen.
Inhaltsverzeichnis
- 1. Warum Animation-Performance den Unterschied macht
- 2. Die Rendering-Pipeline: Layout, Paint und Composite
- 3. Welche CSS-Properties Layout, Paint oder Composite auslösen
- 4. Warum transform und opacity fast kostenlos sind
- 5. will-change: Nutzen und Kosten
- 6. Praktische Refactoring-Beispiele
- 7. requestAnimationFrame für JS-gesteuerte Animationen
- 8. Die FLIP-Technik für Layout-Animationen
- 9. Messen mit Chrome DevTools: Rendering-Tab und Performance-Panel
- 10. Zusammenfassung
- 11. FAQ
1. Warum Animation-Performance den Unterschied macht
Ein Bildschirm mit 60 Hz aktualisiert sich alle 16,7 Millisekunden. Damit eine Animation als flüssig wahrgenommen wird, muss der Browser in genau diesem Zeitfenster Style-Berechnung, Layout, Paint und Composite komplett abschließen, bevor der nächste Frame ansteht. Reicht die Zeit nicht, wird ein Frame ausgelassen, sichtbar als kurzes Ruckeln oder Stottern. Anders als bei Ladezeiten, wo Nutzer eine gewisse Wartezeit tolerieren, fallen gerissene Animationen sofort auf, weil das menschliche Auge auf Bewegungsunregelmäßigkeiten extrem sensibel reagiert.
In Magento- und Hyvä-Shops betrifft das jede Interaktion mit visueller Rückmeldung: das Aufklappen des Mobile-Menüs, das Einblenden des Warenkorb-Drawers, Hover-Effekte auf Produktkarten und Alpine.js-x-transition-Übergänge bei Modals oder Filtern. Ein ruckelndes Menü wirkt unmittelbar unfertig und günstig, selbst wenn die eigentliche Ladezeit der Seite hervorragend ist. Animation-Performance ist damit kein kosmetisches Detail, sondern ein direkt wahrnehmbarer Qualitätsindikator, unabhängig von Server-Antwortzeiten oder Bundle-Größen.
2. Die Rendering-Pipeline: Layout, Paint und Composite
Nach jeder Style-Änderung durchläuft der Browser potenziell vier Stufen. Zuerst berechnet die Style-Berechnung, welche CSS-Regeln auf welches Element zutreffen. Danach folgt Layout (auch Reflow genannt): Der Browser berechnet die exakte Größe und Position jedes Elements im Dokument. Da Elemente sich gegenseitig beeinflussen, kann eine einzige Größenänderung eine Neuberechnung des gesamten Dokumentbaums auslösen, nicht nur des betroffenen Elements.
Anschließend rastert Paint die einzelnen Elemente in Pixel auf separaten Layern, inklusive Farben, Schatten und Rundungen. Die letzte Stufe, Composite, kombiniert diese bereits fertig gerasterten Layer über die GPU zum finalen Bild, ohne dass Layout oder Paint erneut angefasst werden müssen. Der entscheidende Punkt: Composite ist um Größenordnungen günstiger als Layout und Paint, weil dabei nur bestehende Bitmaps verschoben, skaliert oder überblendet werden, statt Pixel neu zu berechnen. Jede Animation, die ausschließlich auf der Composite-Stufe stattfindet, umgeht also die beiden teuersten Schritte der Pipeline komplett.
3. Welche CSS-Properties Layout, Paint oder Composite auslösen
CSS-Properties lassen sich grob in drei Kostenklassen einteilen. Layout-auslösende Properties wie width, height, top, left, margin, padding oder font-size zwingen den Browser, die Geometrie neu zu berechnen, und ziehen automatisch auch Paint und Composite nach sich, da sich die betroffenen Pixel zwangsläufig verschieben. Bei einem animierten left-Wert bedeutet das: 60-mal pro Sekunde ein vollständiger Layout-Pass, potenziell über den gesamten sichtbaren Dokumentbaum hinweg.
Eine zweite Klasse, Paint-auslösende Properties wie color, background-color, box-shadow oder border-radius, überspringt zwar Layout, erzwingt aber weiterhin ein Neurastern der betroffenen Pixel bei jedem Frame. Das ist günstiger als Layout, bleibt aber teurer als reines Compositing, besonders bei großflächigen Elementen oder aufwendigen Schatten mit großem Blur-Radius. Ein besonders tückisches Muster ist Layout Thrashing: Wird in einer JavaScript-Schleife abwechselnd geschrieben (etwa element.style.width) und gelesen (etwa element.offsetWidth), erzwingt jeder Lesezugriff eine synchrone Neuberechnung des Layouts, noch bevor der nächste Frame überhaupt ansteht.
4. Warum transform und opacity fast kostenlos sind
transform und opacity gehören zur dritten Klasse: Sie lösen weder Layout noch Paint aus, sondern werden direkt auf der Composite-Stufe verarbeitet. Der Grund liegt in der Natur der Operationen: Eine Translation, Skalierung oder Rotation über transform verändert lediglich, wie ein bereits gerasterter Layer auf dem Bildschirm positioniert wird, nicht aber, welche Pixel dieser Layer enthält. Genau das kann die GPU über eine simple Matrix-Multiplikation erledigen, in jedem Frame neu, ohne dass CPU-seitig irgendetwas neu berechnet werden muss.
opacity funktioniert analog: Der Browser überblendet zwei bereits existierende Layer mit einem Alpha-Wert, statt die darunterliegenden Pixel neu einzufärben. Voraussetzung dafür ist, dass der Browser das animierte Element bereits vorab auf einen eigenen Compositor-Layer promotet hat, was moderne Browser bei erkannten transform- oder opacity-Transitions automatisch tun. In der Praxis bedeutet das: Eine mit transform: translateX() animierte Sidebar kann butterweich bei 60fps laufen, während dieselbe visuelle Bewegung über left auf einem mobilen Gerät spürbar ruckelt, obwohl der Endzustand optisch identisch ist.
5. will-change: Nutzen und Kosten
Die CSS-Property will-change teilt dem Browser vorab mit, dass ein Element sich demnächst ändern wird, sodass er den entsprechenden Compositor-Layer bereits im Voraus anlegen kann, statt ihn erst beim ersten Animationsframe zu erzeugen. Ohne diesen Hinweis kann der erste Frame einer Animation spürbar ruckeln, weil die Layer-Erzeugung selbst Zeit kostet. Mit will-change: transform auf einem Hover-Element entfällt dieser Kaltstart-Effekt.
Der Haken: Jeder promotete Layer belegt eigenen GPU-Speicher, und bei zu vielen gleichzeitig aktiven Layern kann die Speicherbandbreite zum limitierenden Faktor werden, was die Gesamtperformance verschlechtert statt verbessert. will-change pauschal auf viele Elemente einer Produktliste zu setzen, ist daher ein häufiger Anti-Pattern. Die bewährte Praxis: will-change kurz vor der Animation setzen, etwa per JavaScript beim mouseenter-Event, und nach Abschluss über transitionend wieder entfernen, statt es dauerhaft im Stylesheet zu verankern.
6. Praktische Refactoring-Beispiele
Die häufigsten Refactoring-Kandidaten in Magento- und Hyvä-Themes sind Slide-in-Menüs, die über left oder right animiert werden, sowie Hover-Effekte auf Produktkarten, die width oder height verändern. Beide lassen sich verlustfrei auf transform: translateX() beziehungsweise transform: scale() umstellen, ohne das visuelle Ergebnis zu verändern. Wichtig dabei: Das Element muss bereits vor der Animation an der korrekten Basisposition stehen, die Bewegung entsteht ausschließlich über den Transform-Offset.
In Hyvä-Themes nutzt Alpine.js über x-transition standardmäßig bereits opacity und transform für Enter- und Leave-Übergänge, was strukturell gut ist. Trotzdem lohnt sich eine Prüfung des eigenen Theme-CSS: Tailwind-Utility-Klassen wie transition-all in Kombination mit Hover-Klassen, die w- oder h- verändern, animieren implizit Layout-Properties. Der gezielte Ersatz durch scale--Utilities oder eine explizite transition-transform-Klasse behebt das ohne zusätzliches JavaScript.
/* Bad: animating "left" triggers a full layout pass on every frame */
.menu-slow {
position: fixed;
left: -280px;
transition: left 0.3s ease;
}
.menu-slow.is-open {
left: 0;
}
/* Good: animating transform runs entirely on the compositor thread */
.menu-fast {
position: fixed;
transform: translateX(-280px);
transition: transform 0.3s ease;
}
.menu-fast.is-open {
transform: translateX(0);
}
/* Bad: will-change applied permanently to every product card */
.product-card {
will-change: transform, opacity;
}
/* Good: promote the layer only right before the animation runs */
.product-card {
transition: transform 0.2s ease;
}
.product-card:hover {
will-change: transform;
transform: scale(1.03);
}
/* Remove the hint again via JS once the transition ends, e.g. on transitionend */
7. requestAnimationFrame für JS-gesteuerte Animationen
Für Animationen, die sich nicht als CSS-Transition oder Keyframe-Animation ausdrücken lassen, etwa physikbasierte Bewegungen oder ein individuell berechneter Scroll-to-Top-Effekt, ist requestAnimationFrame (rAF) das richtige Werkzeug. Anders als setInterval oder setTimeout synchronisiert sich rAF exakt mit dem nächsten Bildschirm-Refresh und pausiert automatisch, wenn der Tab im Hintergrund läuft, was sowohl Ruckeln als auch unnötigen Energieverbrauch vermeidet.
Entscheidend für flüssige rAF-Animationen ist die strikte Trennung von Lese- und Schreibzugriffen auf das DOM: Werden in derselben Schleife erst getBoundingClientRect() gelesen und dann Styles geschrieben, entsteht Layout Thrashing. Stattdessen sollten alle Messungen einmalig vor der Schleife erfolgen und pro Frame ausschließlich transform oder opacity geschrieben werden. Die Verwendung des timestamp-Parameters, den der Browser jedem rAF-Callback übergibt, erlaubt außerdem eine framerate-unabhängige Berechnung des Animationsfortschritts, statt sich auf eine feste Anzahl Frames zu verlassen.
// Smooth scroll-to-top driven by requestAnimationFrame, not setInterval
function animateScroll(targetY, duration) {
const startY = window.scrollY;
const distance = targetY - startY;
const startTime = performance.now();
function step(now) {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
// Ease-out cubic for a natural deceleration
const eased = 1 - Math.pow(1 - progress, 3);
window.scrollTo(0, startY + distance * eased);
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
8. Die FLIP-Technik für Layout-Animationen
Manche Effekte, etwa das Umsortieren einer Produktliste nach einem Filterwechsel, betreffen zwangsläufig Layout-Eigenschaften wie Position und Größe. Die FLIP-Technik (First, Last, Invert, Play) löst dieses Dilemma, indem sie die Layout-Änderung selbst sofort und ohne Animation ausführen lässt, den optischen Sprung aber über transform kaschiert. Der Ablauf: Erst wird die Ausgangsposition gemessen (First), dann die DOM-Änderung sofort angewendet und die neue Position gemessen (Last), anschließend das Element per transform optisch zurück an die alte Position verschoben (Invert), und schließlich zur finalen Position animiert (Play).
Für den Browser sieht das Ergebnis aus wie eine echte Layout-Animation, tatsächlich läuft aber ausschließlich eine Transform-Animation auf dem Compositor. Typische Einsatzgebiete in Magento-Shops sind das Neuanordnen von Produktkarten nach einer Filteränderung, das Kollabieren einer Warenkorbzeile nach dem Entfernen eines Artikels oder das Umschalten zwischen Listen- und Rasteransicht in der Kategorieseite, jeweils ohne dass der Browser während der eigentlichen Animation Layout neu berechnen muss.
// FLIP: First, Last, Invert, Play for a reordering product grid
function flip(element, mutateDom) {
const first = element.getBoundingClientRect();
mutateDom();
const last = element.getBoundingClientRect();
const deltaX = first.left - last.left;
const deltaY = first.top - last.top;
// Invert: jump back to the old visual position using only transform
element.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
element.style.transition = 'transform 0s';
requestAnimationFrame(() => {
// Play: animate to the identity transform, layout stays untouched
element.style.transition = 'transform 0.3s ease';
element.style.transform = '';
});
}
9. Messen mit Chrome DevTools: Rendering-Tab und Performance-Panel
Ob eine Animation tatsächlich nur auf dem Compositor läuft, lässt sich mit den Chrome DevTools präzise nachweisen, statt es anzunehmen. Im Rendering-Tab (über das Command-Menü mit Show Rendering erreichbar) zeigt Layout Shift Regions unerwartete Layout-Änderungen farbig markiert an, während Layer Borders sichtbar macht, welche Elemente bereits eigene Compositor-Layer besitzen. Der Frame Rendering Stats-Overlay zeigt live die aktuelle Framerate und ob GPU-Rasterung aktiv ist.
Im Performance-Panel liefert eine aufgezeichnete Interaktion die verlässlichste Diagnose: Die Timeline zeigt Layout- und Paint-Ereignisse als eigene farbige Balken, üblicherweise lila für Layout und grün für Paint. Läuft eine Animation sauber nur auf dem Compositor, erscheinen während der gesamten Animationsdauer keine dieser Balken, nur die deutlich schmaleren Composite-Ereignisse. Zusätzlich markiert Chrome erzwungene synchrone Layouts explizit als Forced reflow in der Timeline, meist mit einem roten Warndreieck, was Layout-Thrashing-Stellen im eigenen Code sofort auffindbar macht.
/* Loading spinner and toast fade-in using compositor-only properties */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.spinner {
animation: spin 0.8s linear infinite;
}
.toast {
animation: fade-in-up 0.25s ease-out;
}
CSS-Properties im direkten Vergleich
Die folgende Übersicht ordnet die gängigsten animierten CSS-Properties den ausgelösten Pipeline-Stufen zu und zeigt, welche Alternative in der Praxis vorzuziehen ist.
| CSS-Property | Ausgelöste Pipeline-Stufe | Kosten | Empfehlung |
|---|---|---|---|
| width / height | Layout + Paint + Composite | Hoch | transform: scale() verwenden |
| top / left / margin | Layout + Paint + Composite | Hoch | transform: translate() verwenden |
| color / background-color | Paint + Composite | Mittel | Nur bei kleinen Flächen animieren |
| box-shadow | Paint + Composite | Mittel | Vorgerenderten Schatten per opacity überblenden |
| transform | Nur Composite | Niedrig | Erste Wahl für Bewegung, Skalierung, Rotation |
| opacity | Nur Composite | Niedrig | Erste Wahl für Fade-Effekte |
In der Praxis lassen sich fast alle gängigen UI-Animationen, von Slide-ins über Modals bis zu Hover-Effekten, vollständig mit transform und opacity umsetzen. Bleiben einzelne Effekte übrig, die zwingend Layout-Properties betreffen, hilft die FLIP-Technik, den Layout-Sprung ohne animierten Reflow zu kaschieren.
Mironsoft
Animation-Performance und Compositor-Optimierung für Magento- und Hyvä-Shops
Ruckelnde Animationen auf 60fps bringen?
Wir analysieren die Animationen eures Shops mit dem Chrome Performance-Panel, identifizieren Layout-Thrashing und teure Properties und refactoren gezielt auf transform und opacity, inklusive Alpine.js-Transitions.
Animation-Audit
Performance-Panel-Analyse, Identifikation von Layout-Thrashing
Refactoring auf Compositor-Layer
Umstellung auf transform und opacity ohne visuelle Änderungen
Alpine.js-Transition-Optimierung
x-transition und rAF-Animationen für Hyvä-Interaktionen tunen
10. Zusammenfassung
Animation-Performance entscheidet sich in der Rendering-Pipeline, nicht im Auge des Betrachters. Wer width, top, left oder margin animiert, erzwingt bei jedem Frame einen vollständigen Layout-Pass, während transform und opacity ausschließlich auf der günstigen Composite-Stufe verarbeitet werden und dadurch zuverlässig 60fps erreichen. will-change beschleunigt den Animationsstart, sollte aber gezielt und temporär eingesetzt werden, um GPU-Speicher nicht unnötig zu belasten.
Für JavaScript-gesteuerte Animationen synchronisiert requestAnimationFrame die Bewegung exakt mit dem Bildschirm-Refresh, während strikte Trennung von DOM-Lese- und Schreibzugriffen Layout Thrashing verhindert. Betreffen Effekte zwangsläufig Layout-Eigenschaften, etwa beim Neusortieren von Listen, überbrückt die FLIP-Technik den Sprung mit einer reinen Transform-Animation. Chrome DevTools liefern mit Rendering-Tab und Performance-Panel den objektiven Nachweis, ob eine Animation tatsächlich auf dem Compositor läuft oder unbemerkt Layout und Paint auslöst.
Animation-Performance - Das Wichtigste auf einen Blick
Teure Properties meiden
width, top, left, margin lösen Layout aus. Für Bewegung und Größe immer transform nutzen.
Compositor-only bevorzugen
transform und opacity laufen ohne Layout und Paint, direkt auf der GPU, für stabile 60fps.
will-change gezielt einsetzen
Kurz vor der Animation setzen, danach wieder entfernen. Dauerhaft auf viele Elemente ist ein Anti-Pattern.
Messen statt raten
Chrome Performance-Panel und Rendering-Tab zeigen Layout-, Paint- und Composite-Ereignisse pro Frame.