Signatur
Beschreibung
Ein ViewTransition-Objekt stellt eine aktive Ansichtstransition dar. Innerhalb eines Dokuments (Same-Document) erhältst du es als Rückgabewert von document.startViewTransition(). Bei dokumentübergreifenden Transitionen (Cross-Document) steht es in den Events pageswap (event.viewTransition) und pagereveal zur Verfügung. Mit dem Objekt kannst du die Phasen der Transition verfolgen, eigene Animationen per Web Animations API einhängen oder die Transition abbrechen.
Das Interface hat folgende Bestandteile:
updateCallbackDone:Promise<void>. Wird erfüllt, sobald das Promise des Update-Callbacks erfüllt ist, also wenn das DOM aktualisiert wurde. Wird abgelehnt, wenn der Callback fehlschlägt.ready:Promise<void>. Wird erfüllt, sobald der Pseudo-Element-Baum (::view-transition…) erzeugt ist und die Animation gleich beginnt. Das ist der richtige Zeitpunkt für eigene Animationen mitelement.animate().finished:Promise<void>. Wird erfüllt, wenn die Animation beendet ist und die neue Ansicht voll sichtbar und bedienbar ist. Es wird nicht abgelehnt, wenn nur die Animation übersprungen wird. Abgelehnt wird es nur, wenn der Update-Callback fehlschlägt.types: einViewTransitionTypeSet(Set-ähnlich). Damit lassen sich Transitionstypen zur Laufzeit setzen, die in CSS per:active-view-transition-type()abgefragt werden.skipTransition(): überspringt den Animationsteil. Der Update-Callback wird trotzdem ausgeführt.
Die Promises passen zu drei typischen Aufgaben: Mit updateCallbackDone reagierst du auf den neuen DOM-Zustand, egal ob animiert wird oder nicht. Mit ready startest du eigene Animationen. Mit finished räumst du auf, etwa Klassen entfernen oder den Fokus setzen.
Rückgabewert
document.startViewTransition() bzw. aus PageSwapEvent.viewTransition / PageRevealEvent.viewTransition.Beispiele
Transition starten und Phasen verfolgen
const updateList = async () => {
const items = await fetch('/api/items').then((r) => r.json());
document.querySelector('#list').innerHTML = items
.map((i) => `<li>${i.name}</li>`)
.join('');
};
const refresh = async () => {
// Ausweichlösung für Browser ohne Unterstützung
if (!document.startViewTransition) {
await updateList();
return;
}
const transition = document.startViewTransition(updateList);
await transition.updateCallbackDone;
console.log('DOM aktualisiert');
await transition.ready;
console.log('Animation startet');
await transition.finished;
console.log('Transition abgeschlossen');
};
refresh();
Eigene Kreis-Animation im ready-Promise
const toggleTheme = async (event) => {
const x = event.clientX;
const y = event.clientY;
const radius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle('dark');
});
await transition.ready;
// Pseudo-Element der neuen Ansicht animieren
document.documentElement.animate(
{
clipPath: [
`circle(0px at ${x}px ${y}px)`,
`circle(${radius}px at ${x}px ${y}px)`
]
},
{
duration: 500,
easing: 'ease-in-out',
pseudoElement: '::view-transition-new(root)'
}
);
};
document.querySelector('#theme-btn').addEventListener('click', toggleTheme);
Transition bei reduzierter Bewegung überspringen
const prefersReduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
const transition = document.startViewTransition(() => {
document.querySelector('#panel').hidden = false;
});
if (prefersReduced) {
// DOM-Update läuft trotzdem, nur die Animation entfällt
transition.skipTransition();
}
transition.finished.then(() => {
document.querySelector('#panel').focus();
console.log('Panel sichtbar');
});
Transitionstypen für Vor/Zurück-Navigation
const navigate = (direction, render) => {
const transition = document.startViewTransition({
update: render,
types: [direction] // 'forwards' oder 'backwards'
});
return transition.finished;
};
// Typ auch nachträglich über das types-Set änderbar
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
event.viewTransition.types.add('reveal');
console.log([...event.viewTransition.types]);
}
});
/* CSS:
html:active-view-transition-type(backwards) {
view-transition-name: none;
}
*/
// Wichtig · Fallstricke
- Unterstützung prüfen: Frag vor der Nutzung
document.startViewTransitionab. Chromium unterstützt die API seit Version 111, Safari seit 18, Firefox erst in neueren Versionen (ab 144). Dokumentübergreifende Transitionen per@view-transition { navigation: auto; }gibt es nur in Chromium 126+ und Safari 18.2+. Transitionstypen werden nicht überall gleich unterstützt. - Unbehandelte Ablehnungen: Wird
skipTransition()aufgerufen oder schlägt die Transition fehl (etwa wegen doppelterview-transition-name-Werte), wirdreadyabgelehnt. Ohne.catch()erscheint dann ein „Uncaught (in promise)“-Fehler in der Konsole. - Nur eine Transition gleichzeitig: Ein neuer Aufruf von
startViewTransition()überspringt die laufende Transition. Derenreadywird mit einemAbortErrorabgelehnt. - Timeout: Braucht der Update-Callback zu lange (in Chromium etwa 4 Sekunden), bricht der Browser mit einem
TimeoutErrorab. Während der Transition wird die Seite nicht gerendert, daher sollten lange Netzwerkanfragen vor dem Aufruf erledigt werden. - Eindeutige Namen: Jeder
view-transition-namedarf pro Zustand nur einmal vorkommen, sonst wird die Transition übersprungen. - Barrierefreiheit: Berücksichtige
prefers-reduced-motionin CSS oder rufeskipTransition()auf. - Sicherheit: Wenn im Update-Callback per
innerHTMLeingefügt wird, müssen Daten escaped werden, sonst droht XSS. Das hat mit der API selbst nichts zu tun.