was wirklich smooth ist und was nur blinkt
Vue Transitionen sind mächtig, aber voller versteckter Fallen. Ein falsch gesetztes CSS-Property löst Layout Reflow aus und macht die Animation ruckelig. Ein fehlender key-Wechsel lässt Elemente verschwinden, ohne dass Transition überhaupt ausgelöst wird. Dieser Artikel erklärt, wie Vue Transitionen tatsächlich funktionieren, welche CSS-Properties smooth laufen und welche Muster in der Praxis zuverlässig schöne Animationen liefern.
Inhaltsverzeichnis
- 1. Wie Vue Transition intern funktioniert
- 2. Die sechs CSS-Klassen und wann sie gesetzt werden
- 3. Was wirklich smooth ist: opacity, transform, clip-path
- 4. Transition-Modes: out-in und in-out richtig einsetzen
- 5. TransitionGroup: Listentransitionen ohne Layout-Sprünge
- 6. Route-Transitionen mit Vue Router
- 7. JavaScript-Hooks für komplexe Animationen
- 8. Performance-Fallen und wie man sie erkennt
- 9. Vergleich: smooth vs. ruckelig
- 10. Zusammenfassung
- 11. FAQ
1. Wie Vue Transition intern funktioniert
Die Vue Transition-Komponente ist kein reines CSS-Feature, sondern eine Laufzeit-Koordination zwischen Vue und dem Browser. Wenn ein Element unter <Transition> durch v-if oder v-show auftaucht, fügt Vue zu definierten Zeitpunkten CSS-Klassen hinzu und entfernt sie. Vue wartet dabei auf den Browser — konkret: Auf das nächste Animations-Frame, um sicherzustellen, dass der Browser die Ausgangs-Klasse tatsächlich gerendert hat, bevor die aktive Klasse hinzugefügt wird. Dieses Warten auf den Browser ist notwendig, weil CSS-Transitions nur ausgelöst werden, wenn der Browser einen Zustandsunterschied zwischen zwei Frames beobachtet.
Ein häufiges Missverständnis: Vue Transition startet die Animation nicht. Es fügt CSS-Klassen hinzu — der Browser führt dann die CSS-Transition aus, die in diesen Klassen definiert ist. Wenn die CSS-Transition-Property fehlt oder auf 0s gesetzt ist, passiert kein sichtbarer Übergang, obwohl Vue die Klassen korrekt setzt. Wenn das Element beim Einblenden keine Zeit hat, den Ausgangszustand gerendert zu bekommen — weil Vue und Browser zu schnell hintereinander arbeiten — startet die Transition von einem falschen Ausgangspunkt. Der nextTick-Mechanismus in Vue stellt sicher, dass dieser Timing-Fehler systematisch vermieden wird.
Für v-show ist das Verhalten anders als für v-if. Bei v-if wird das Element beim Entfernen tatsächlich aus dem DOM genommen, nachdem die Leave-Transition abgeschlossen ist. Bei v-show wird statt DOM-Entfernung nur display: none gesetzt. Vue Transition behandelt beide korrekt, aber die Leave-Animation bei v-show kollidiert manchmal mit bereits definierten display-Properties im CSS — ein häufiger Bug, der zu sofortigem Verschwinden statt Animation führt.
2. Die sechs CSS-Klassen und wann sie gesetzt werden
Die Vue Transition-Komponente setzt sechs CSS-Klassen: drei für das Einblenden (Enter) und drei für das Ausblenden (Leave). Das Muster ist [name]-enter-from, [name]-enter-active, [name]-enter-to und entsprechend für Leave. -from definiert den Ausgangszustand, -to den Zielzustand und -active enthält die eigentliche transition-Deklaration — also Dauer, Easing-Funktion und die zu animierenden Properties. Die häufigste Fehlerquelle: transition in -active vergessen. Dann wechselt das Element sofort von -from zu -to, ohne Animation.
Vue setzt die Klassen in dieser Reihenfolge für Enter: Zuerst -from und -active gleichzeitig, dann wartet Vue einen Frame, entfernt -from und fügt -to hinzu. Der Browser sieht jetzt den Unterschied zwischen -from-Werten und -to-Werten und startet die CSS-Transition. Nach Abschluss der Transition — erkannt über das transitionend-Event — entfernt Vue alle Enter-Klassen. Das ist wichtig zu verstehen: Vue wartet auf das transitionend-Event des Browsers. Wenn dieses Event nicht feuert — z.B. weil kein transition-Property definiert ist oder das Element sofort aus dem DOM entfernt wurde — hängt Vue in einem undefinierten Zustand.
/* Correct Vue Transition CSS — all 6 classes defined */
/* The -active class MUST contain the transition property */
/* Fade transition — opacity only (compositor-layer, 60fps) */
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
/* transition defined here — duration, property, easing */
transition: opacity 250ms ease-in-out;
}
/* -enter-to and -leave-from inherit the component's default style */
/* (opacity: 1 by default) — no need to declare them explicitly */
/* Slide-up transition — transform only (compositor, no layout) */
.slide-up-enter-from {
transform: translateY(16px);
opacity: 0;
}
.slide-up-leave-to {
transform: translateY(-16px);
opacity: 0;
}
.slide-up-enter-active,
.slide-up-leave-active {
transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1),
opacity 300ms ease;
}
3. Was wirklich smooth ist: opacity, transform, clip-path
Nicht alle CSS-Properties sind gleich für Vue Transitionen. Der Browser rendert CSS-Transitions auf zwei Wegen: Properties, die einen Layout Reflow auslösen (width, height, margin, padding, top, left), müssen in jedem Frame vom Browser neu berechnet werden. Das ist CPU-intensiv und führt auf schwächeren Geräten zu spürbarem Ruckeln. Properties, die nur den Compositor-Layer betreffen (opacity, transform), können direkt von der GPU animiert werden — kein Layout Reflow, 60fps auch auf mobilen Geräten.
Die goldene Regel für smooth Vue Transitionen: Nur opacity und transform animieren. Wenn ein Element größer werden soll, statt width zu animieren lieber mit transform: scale() arbeiten. Wenn ein Element von oben hereingleiten soll, statt top: -100px zu top: 0 zu animieren lieber mit transform: translateY(-100px) starten. clip-path ist eine weitere Ausnahme, die auf modernen Browsers ohne Layout-Reflow animiert werden kann und für reveal-Effekte interessant ist. filter (blur, brightness) kann ebenfalls smooth animiert werden, ist aber auf mobilen Geräten teurer als opacity und transform.
4. Transition-Modes: out-in und in-out richtig einsetzen
Wenn ein Element unter <Transition> durch ein anderes ersetzt wird — z.B. durch den Wechsel eines :key-Attributs oder eines v-if/v-else-Paares — laufen standardmäßig Leave- und Enter-Transition gleichzeitig. Das bedeutet: Das alte Element blendet aus, während das neue einblendet. In vielen Layouts führt das zu einem visuellen "Sprung", weil beide Elemente kurzzeitig Platz beanspruchen. Der Vue Transition-Mode out-in löst das: Erst das alte Element ausblenden, dann das neue einblenden. Das ist die sicherste Option für die meisten Anwendungsfälle.
Der Mode in-out ist das Gegenteil: Das neue Element blendet zuerst ein, dann blendet das alte aus. Das klingt seltsam, ist aber für Slide-Transitionen nützlich, bei denen das neue Element von der Seite hereingleitet und das alte gleichzeitig in dieselbe Richtung weitergleitet. Ohne Mode ist Vue Transition bei einem key-Wechsel auf demselben Element manchmal schwer vorhersehbar — gerade bei schnellen Wechseln, bei denen eine laufende Enter-Transition von einer neuen Leave-Transition unterbrochen wird. Mode out-in verhindert diesen Race-Condition-Effekt vollständig.
5. TransitionGroup: Listentransitionen ohne Layout-Sprünge
Vue TransitionGroup ist für Animationen von Listen-Elementen zuständig, die ihre Positionen wechseln, hinzugefügt oder entfernt werden. Das Feature, das Vue TransitionGroup besonders macht, ist die FLIP-Animation (First, Last, Invert, Play): Wenn ein Element seine Position in der Liste ändert, berechnet Vue die Differenz zwischen alter und neuer Position und animiert das Element mit transform: translateX()/translateY() von der alten zur neuen Position. Das geschieht ohne Layout-Animation — der Browser sieht immer nur die Endposition, die Animation ist eine reine Transform-Animation.
FLIP in Vue TransitionGroup wird über die -move-Klasse gesteuert: [name]-move { transition: transform 300ms ease; }. Ohne diese Klasse gibt es keine FLIP-Animation — Elemente springen sofort zur neuen Position. Das tag-Attribut von Vue TransitionGroup definiert das Wrapper-Element im DOM. Standard ist span, was für Block-Elemente falsch ist — für Listenelemente sollte tag="ul" oder tag="div" explizit gesetzt werden. Wichtig: Jedes Kind von Vue TransitionGroup muss einen eindeutigen :key haben — ohne Key funktioniert weder Transition noch FLIP korrekt.
/* TransitionGroup with FLIP move transition */
/* All three types: enter, leave, move */
.list-enter-from {
opacity: 0;
transform: translateX(-20px);
}
.list-leave-to {
opacity: 0;
transform: translateX(20px);
}
.list-enter-active,
.list-leave-active {
transition: opacity 250ms ease, transform 250ms ease;
}
/* FLIP: Vue calculates position delta and applies as transform */
/* Without this class: elements jump instantly to new position */
.list-move {
transition: transform 350ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* Critical: leaving elements must be taken out of flow
during the move transition, otherwise FLIP breaks */
.list-leave-active {
position: absolute; /* remove from flow while leaving */
}
/* Usage in template:
<TransitionGroup name="list" tag="ul">
<li v-for="item in sortedItems" :key="item.id">...</li>
</TransitionGroup>
*/
6. Route-Transitionen mit Vue Router
Route-Transitionen in Vue 3 mit dem Vue Router werden über den <RouterView>-Slot realisiert. Das Muster: <RouterView v-slot="{ Component }"> gibt die aktuelle Route-Komponente als Slot-Variable heraus, die dann in ein <Transition>-Wrapper gepackt wird. Das :key="route.fullPath" auf dem <component>-Tag stellt sicher, dass Vue Transition ausgelöst wird, auch wenn nur Query-Parameter sich ändern — ohne :key bleibt die gleiche Komponenten-Instanz bestehen und Transition wird nicht aktiviert.
Ein häufiger Fehler bei Route-Vue Transitionen: Der Transition-Wrapper direkt auf <RouterView> statt im Slot. Vue Router 4 hat die API geändert — der Wrapper muss innerhalb des Slots sitzen, nicht als Elternelement. Ein zweiter häufiger Fehler: Verschiedene Route-Transitionen je nach Navigationsrichtung (vorwärts/rückwärts) implementieren. Das erfordert, die Navigationsrichtung im Router-Hook zu erkennen, in einem Ref zu speichern und als dynamischen Transition-Namen zu übergeben. Die Koordination zwischen Router-Navigation und Transition-Timing ist der schwierigste Teil dieser Architektur.
7. JavaScript-Hooks für komplexe Animationen
Vue Transition unterstützt JavaScript-Hooks als Alternative zu reinen CSS-Transitions: @before-enter, @enter, @after-enter und die entsprechenden Leave-Hooks. In diesen Hooks können beliebige JavaScript-Animationsbibliotheken verwendet werden — GSAP, Anime.js oder die native Web Animations API. Der @enter-Hook erhält das DOM-Element als erstes Argument und eine done-Callback-Funktion als zweites. done() muss aufgerufen werden, wenn die Animation abgeschlossen ist, damit Vue das Lifecycle-Management korrekt fortsetzen kann. Ohne done()-Call bleibt die Komponente im Enter-Zustand hängen.
Das wichtige Attribut bei JavaScript-Hooks ist :css="false" auf dem <Transition>-Tag. Damit weist man Vue an, keine CSS-Klassen zu setzen und auch nicht auf transitionend-Events zu warten. Ohne :css="false" überschneiden sich CSS-Klassen und JavaScript-Animationen, was zu undefiniertem Verhalten führt. JavaScript-Hooks sind besonders sinnvoll für sequenzielle Animationen (mehrere Elemente nacheinander), physikbasierte Animationen (Federdynamik) und für Animationen, die Zustandsdaten aus dem Vue-Store benötigen — was in CSS nicht möglich ist.
8. Performance-Fallen und wie man sie erkennt
Die häufigste Performance-Falle bei Vue Transitionen ist das Animieren von Properties, die Layout Reflow auslösen. Wenn im DevTools-Performance-Profiler "Layout" und "Recalculate Style" während einer Animation erscheinen, werden Layout-triggernde Properties animiert. Die Lösung ist fast immer, stattdessen transform und opacity zu verwenden. Eine hilfreiche Ressource ist CSS Triggers (csstriggers.com), die dokumentiert, welche Properties Reflow, Repaint oder nur Composite auslösen. Nur Composite-Operationen laufen smooth auf dem GPU-Compositor.
Die zweite Performance-Falle ist zu viele gleichzeitig animierte Elemente in Vue TransitionGroup. FLIP ist teuer bei großen Listen, weil Vue für jedes Element die Position vor und nach der Änderung berechnet. Bei Listen über 100 Elementen sollte man entweder auf Virtualisierung setzen (vue-virtual-scroller) oder die FLIP-Animation deaktivieren und nur Enter/Leave animieren. Die dritte Falle: Transitions auf Elementen, die Kinder mit komplexem Painting haben. Ein Schatten auf einem animierten Element erzeugt bei jeder Frame-Aktualisierung ein neues Layer-Composite. will-change: transform promotet ein Element auf eine eigene Compositor-Layer, was Animationen beschleunigt, aber Speicher kostet — sparsam einsetzen.
9. Vergleich: smooth vs. ruckelig
Dieser Vergleich zeigt die häufigsten Ursachen von ruckelnden Vue Transitionen und die jeweilige Lösung für smooth Animationen.
| Problem | Ruckelig (blinkt) | Smooth | Grund |
|---|---|---|---|
| Größe animieren | width/height transition | transform: scale() | scale() nur Compositor, kein Reflow |
| Position animieren | top/left transition | transform: translate() | translate() kein Layout-Reflow |
| Element-Wechsel | Kein mode → beide gleichzeitig | mode="out-in" | Kein doppeltes Layout |
| Liste umordnen | Kein -move Klasse | TransitionGroup + FLIP | Transform-Animation statt Sprung |
| Route-Transition | Wrapper um RouterView (vue router 4) | RouterView v-slot + Transition | Korrekte API in Vue Router 4 |
Ein Sonderfall sind Transitionen bei Modals mit Vue Teleport. Das Modal-DOM wird zu body teleportiert, aber die Vue Transition-Klassen werden korrekt auf das teleportierte Element angewendet. Das bedeutet: Vue Transition und Vue Teleport sind vollständig kompatibel und können direkt kombiniert werden. Die Transition-Klassen müssen in einem globalen CSS-File oder im Component-Scoped-CSS mit :deep() definiert sein, wenn das Modal-Element außerhalb des Scoped-CSS-Kontexts liegt.
Mironsoft
Vue 3 UI-Qualität und Animation-Performance
Ruckelnde Vue Transitionen durch smooth Animationen ersetzen?
Wir analysieren bestehende Vue-Animationen auf Layout-Reflow-Probleme und refactorieren sie zu compositor-only Transitions — messbar smooth auf allen Geräten.
Animation-Audit
DevTools-Performance-Analyse aller aktiven Transitionen auf Layout-Reflow
FLIP-Integration
TransitionGroup mit FLIP-Animationen für smooth Listensortierung und -filterung
Route-Transitions
Direktionale Route-Transitionen mit Navigationsrichtungs-Erkennung im Vue Router
10. Zusammenfassung
Vue Transitionen sind dann smooth, wenn sie ausschließlich opacity und transform animieren, den richtigen Mode verwenden und bei Listentransitionen mit FLIP arbeiten. Die wichtigsten Punkte: Die -active-Klasse muss immer die transition-Deklaration enthalten, sonst passiert kein sichtbarer Übergang. Mode out-in ist die sichere Standardwahl für Element-Wechsel. Vue TransitionGroup braucht die -move-Klasse und position: absolute auf -leave-active für korrekte FLIP-Animationen. Route-Transitionen in Vue Router 4 werden im v-slot von <RouterView> definiert.
Was blinkt statt smooth zu sein: Layout-Properties wie width, height, margin und top/left, die in jedem Frame Layout Reflow auslösen. Transitions ohne expliziten Mode bei Element-Wechseln, die zu doppeltem Layout führen. Fehlende -move-Klasse in Vue TransitionGroup, die Elemente springen lässt. JavaScript-Hooks ohne :css="false", die mit CSS-Klassen kollidieren. Wer diese Fallgruben kennt und mit den richtigen CSS-Properties arbeitet, baut Vue Transitionen, die auf jedem Gerät flüssig laufen.
Vue Transitionen — Das Wichtigste auf einen Blick
Smooth Properties
Nur opacity und transform animieren. Scale statt width/height. Translate statt top/left. Kein Layout Reflow = 60fps auf mobilen Geräten.
CSS-Klassen-Struktur
-from: Ausgangszustand. -active: transition-Deklaration (Pflicht!). -to: Zielzustand (oft default). Ohne transition in -active: kein sichtbarer Übergang.
TransitionGroup & FLIP
-move Klasse für FLIP-Positionsanimation. position:absolute auf -leave-active. Eindeutige :key auf jedem Kind. Tag explizit auf div oder ul setzen.
JavaScript-Hooks
:css="false" wenn JS-Hooks verwendet werden. done()-Callback immer aufrufen. Für sequenzielle Animationen, Federdynamik und Store-abhängige Animationen.