Performance-Optimierung bei Web-Animationen bezeichnet alle Techniken, die sicherstellen, dass Animationen mit 60 oder mehr Frames pro Sekunde laufen – ohne Layout-Reflows, Paints oder Compositor-Konflikte, die das Bild zum Ruckeln bringen.
Was bedeutet Animation-Performance?
Flüssige Animation erfordert, dass der Browser jeden Frame innerhalb von ~16.7 Millisekunden (bei 60Hz) oder ~8.3 ms (bei 120Hz) rendern kann. Wird diese Zeitgrenze überschritten, erscheint das Bild als ruckelnd (Fachbegriff: Jank).
Der Browser-Rendering-Prozess folgt einer Pipeline: JavaScript → Style → Layout → Paint → Composite. Je später in dieser Pipeline eine Änderung greift, desto günstiger ist sie für die Performance. Animations, die nur die Composite-Phase betreffen, sind die performantesten.
Erklärung
Das Browser-Rendering-Modell
Layout (Reflow): Berechnung von Größe und Position aller Elemente. Wird ausgelöst durch Änderungen von: width, height, margin, padding, top, left, border, font-size, line-height...
Layout ist das teuerste Rendering-Schritt, da es kaskadierend andere Elemente beeinflussen kann.
Paint (Repaint): Füllen von Pixeln mit Farben, Schatten, Text. Wird ausgelöst durch: color, background-color, border-color, box-shadow, text-shadow...
Paint ist günstiger als Layout, aber immer noch teuer bei großen Flächen.
Composite: Zusammensetzen von Rendering-Layern auf der GPU. Wird ausgelöst durch: transform, opacity, filter (in bestimmten Fällen)
Composite ist die günstigste Stufe – die GPU kann Layern zusammensetzen ohne CPU-Beteiligung.
Die goldene Regel: transform und opacity
```css / ✅ PERFORMANT – nur Composite / .gut { transition: transform 0.3s ease, opacity 0.3s ease; } .gut:hover { transform: translateX(20px); opacity: 0.8; }
/ ❌ TEUER – löst Layout aus / .schlecht { transition: width 0.3s ease, margin-left 0.3s ease; } .schlecht:hover { width: 120%; margin-left: -20px; }
/ ❌ TEUER – löst Repaint aus / .mittel-teuer { transition: background-color 0.3s ease; } / background-color ist animierbar, aber nicht composite-only / ```
Umformulierungsregeln:
- Element verschieben:
transform: translate()statttop/left/margin - Einblenden:
opacitystattvisibility-Switch - Vergrößern:
transform: scale()stattwidth/height
Compositing-Layer erzwingen
Für kritische Animationselemente kann man vorab einen GPU-Layer anlegen:
``css .animations-element { will-change: transform; /* Empfohlen – gibt Browser Hinweis */ /* ODER: */ transform: translateZ(0); /* "Null-Transform" – alter Trick */ } ``
will-change reserviert GPU-Speicher vorab, was Jank beim Animations-Start verhindert. Aber: Zu viele will-change-Elemente verbrauchen GPU-RAM. Nur für Elemente nutzen, die tatsächlich animiert werden.
```javascript // Best Practice: will-change nur aktiv setzen wenn nötig element.addEventListener('mouseenter', () => { element.style.willChange = 'transform'; });
element.addEventListener('mouseleave', () => { element.style.willChange = 'auto'; }); ```
Forced Layout / Layout Thrashing vermeiden
Alternierend Lesen und Schreiben von Layout-Eigenschaften zwingt den Browser zu wiederholten Reflows:
```javascript // ❌ Layout Thrashing elements.forEach(el => { const breite = el.offsetWidth; // Lesen → forced reflow el.style.width = (breite + 10) + 'px'; // Schreiben → invalides Layout });
// ✅ Besser: Erst alle Werte lesen, dann alle schreiben const breiten = elements.map(el => el.offsetWidth); // Alle Lesen elements.forEach((el, i) => { el.style.width = (breiten[i] + 10) + 'px'; // Alle Schreiben });
// ✅ Optimal: GSAP übernimmt das Batching automatisch gsap.to(elements, { width: '+=10' }); ```
requestAnimationFrame richtig nutzen
``javascript // ❌ Schlecht: In setTimeout/setInterval animieren setInterval(() => { element.style.transform = translateX(${x++}px)`; }, 16);
// ✅ Gut: requestAnimationFrame let x = 0; function animate() { element.style.transform = translateX(${x++}px); requestAnimationFrame(animate); } requestAnimationFrame(animate);
// ✅ Besser: Mit Delta-Time (frame-rate-unabhängig) let letzterZeit = 0; let x = 0; const geschwindigkeit = 100; // px/Sekunde
function animate(zeitstempel) { const delta = (zeitstempel - letzterZeit) / 1000; letzterZeit = zeitstempel;
x += geschwindigkeit * delta; element.style.transform = translateX(${x}px); requestAnimationFrame(animate); } requestAnimationFrame(animate); ```
Chrome DevTools: Performance-Analyse
Die Chrome DevTools sind das wichtigste Werkzeug für Animation-Performance:
- F12 → Performance-Tab → Aufnahme starten, Animation ausführen, stoppen
- Frames-Spur anschauen: Rote Frames = Jank
- Main Thread analysieren: Lange gelbe Blöcke = JavaScript, lila = Recalculate Style, grün = Paint
- Layers-Panel (Drei-Punkte-Menü → More Tools → Layers): Compositing-Layer visualisieren
- Rendering-Tab (Drei-Punkte-Menü → More Tools → Rendering):
- Paint flashing aktiviert: Grüne Overlays zeigen Repaint-Bereiche - FPS meter zeigt Frame-Rate in Echtzeit
GSAP vs. CSS: Performance-Vergleich
```javascript // GSAP optimiert intern: // - Batcht DOM-Reads/Writes // - Nutzt rAF // - Setzt will-change automatisch // - Normalisiert Browser-Unterschiede
gsap.to('.element', { x: 100, opacity: 0, duration: 0.5 });
// Entspricht optimiertem CSS: .element { transform: translateX(100px); opacity: 0; transition: transform 0.5s ease, opacity 0.5s ease; } ```
Beide Ansätze sind ähnlich performant, wenn korrekt implementiert. GSAP verringert das Risiko von Performance-Fehlern durch seine interne Optimierung.
Praxis-Checkliste
``markdown ✅ Nur transform und opacity animieren (CSS-Properties) ✅ will-change sparsam einsetzen (nur bei tatsächlich animierten Elementen) ✅ requestAnimationFrame für JS-Animationen nutzen ✅ Layout Thrashing vermeiden (erst lesen, dann schreiben) ✅ prefers-reduced-motion respektieren ✅ Chrome DevTools Performance-Tab für Analyse nutzen ✅ Paint Flashing aktivieren um Repaint-Bereiche zu identifizieren ✅ Auf mobilen Geräten testen (langsamere CPUs/GPUs) ✅ Bei 3D-Transforms: transform-style: preserve-3d nur wo nötig ✅ Komplexe Animationen mit IntersectionObserver nur aktivieren wenn sichtbar ``
Häufige Performance-Probleme und Lösungen
| Problem | Ursache | Lösung |
|---|---|---|
| Jank beim Start | Layer noch nicht erstellt | will-change: transform vorab setzen |
| Jank bei Scroll | scroll-Event-Handler | Passive Listener: {passive: true} |
| Ruckeln auf Mobile | CSS-Übergänge auf top/left | Auf transform umstellen |
| Fehlende Frames | Lange JS-Tasks | Web Worker oder chunking |
| Speicherleck | Viele will-change-Layer | will-change: auto nach Animation |
Vergleich & Abgrenzung
| Eigenschaft | Rendering-Pfad | Kosten |
|---|---|---|
transform | Composite only | Sehr günstig |
opacity | Composite only | Sehr günstig |
filter: blur() | Composite + ggf. Paint | Mittel |
background-color | Paint | Mittel |
width / height | Layout + Paint + Composite | Teuer |
box-shadow | Paint | Mittel–Teuer |
top / left | Layout + Paint + Composite | Teuer |
Häufige Fragen (FAQ)
Was ist CSS Houdini und hilft es bei Performance? CSS Houdini (CSS Typed OM, Paint API) ermöglicht benutzerdefinierte CSS-Eigenschaften und Paint Worklets, die native Browser-Performance haben. Teilweise unterstützt in Chrome, noch experimentell.
Schaden `filter`-Animationen der Performance? filter erzeugt einen eigenen Compositing-Layer, aber das Rendern des Filters selbst ist CPU-intensiv. backdrop-filter ist besonders teuer – mit Maß einsetzen.
Ist CSS oder JavaScript performanter für Animationen? Beide sind gleich performant wenn korrekt implementiert (beide nutzen dieselbe Engine). Der Unterschied liegt in der Implementierungsqualität, nicht im Ansatz selbst.
Verwandte Einträge
- CSS Transforms (2D & 3D) – Die performanteste Animations-Eigenschaft
- CSS Animationen (@keyframes) – @keyframes mit Performance-Aspekten
- GSAP (GreenSock) – Grundlagen – GSAP mit eingebauter Performance-Optimierung
- Web Animations API (WAAPI) – Native JS-API mit dieselber Performance wie CSS
Weiterführend
- Paul Lewis & Phil Walton: Rendering Performance (2023) – web.dev/rendering
- Paul Irish & Paul Lewis: High Performance Animations (2013/aktualisiert) – web.dev
- Chloe Cummings: Jank-Free Animations – csstriggers.com (jede CSS-Eigenschaft und ihr Rendering-Pfad)
- MDN: will-change (2024) – developer.mozilla.org
- Surma: The Anatomy of a Frame (2021) – surma.dev
- Chrome DevTools: Analyze runtime performance – developer.chrome.com
