Wenn ein Ansichtsübergang zwischen zwei verschiedenen Dokumenten erfolgt, wird dies als dokumentübergreifender Ansichtsübergang bezeichnet. Das ist in der Regel bei mehrseitigen Anwendungen (Multi-Page Applications, MPA) der Fall. In Chrome 126 und höher werden plattformübergreifende Ansichtsübergänge unterstützt.
Unterstützte Browser
Ansichtsübergänge zwischen Dokumenten basieren auf denselben Bausteinen und Prinzipien wie Ansichtsübergänge innerhalb eines Dokuments. Das ist ganz bewusst so:
- Der Browser erstellt Snapshots von Elementen, die sowohl auf der alten als auch auf der neuen Seite ein eindeutiges
view-transition-name
haben. - Das DOM wird aktualisiert, während das Rendering unterdrückt wird.
- Die Übergänge werden schließlich durch CSS-Animationen unterstützt.
Im Vergleich zu Ansichtsübergängen innerhalb desselben Dokuments müssen Sie bei dokumentübergreifenden Ansichtsübergängen nicht document.startViewTransition
aufrufen, um einen Ansichtsübergang zu starten. Stattdessen ist der Auslöser für einen dokumentübergreifenden Ansichtsübergang eine Navigation innerhalb desselben Ursprungs von einer Seite zur anderen. Diese Aktion wird in der Regel durch einen Klick des Nutzers auf Ihrer Website ausgeführt.
Mit anderen Worten: Es gibt keine API, die aufgerufen werden kann, um einen Ansichtsübergang zwischen zwei Dokumenten zu starten. Es müssen jedoch zwei Bedingungen erfüllt sein:
- Beide Dokumente müssen am selben Ursprung vorhanden sein.
- Auf beiden Seiten muss die Funktion aktiviert sein, damit der Wechsel der Ansicht möglich ist.
Beide Bedingungen werden weiter unten in diesem Dokument erläutert.
Ansichtsübergänge zwischen Dokumenten sind auf Navigationen mit demselben Ursprung beschränkt
Ansichtsübergänge zwischen Dokumenten sind nur auf Navigationen innerhalb desselben Ursprungs beschränkt. Eine Navigation gilt als Navigation mit demselben Ursprung, wenn der Ursprung beider teilnehmender Seiten identisch ist.
Der Ursprung einer Seite ist eine Kombination aus dem verwendeten Schema, dem Hostnamen und dem Port, wie auf web.dev beschrieben.
Beispielsweise kann beim Wechseln von developer.chrome.com
zu developer.chrome.com/blog
eine dokumentübergreifende Ansichtsübergang erfolgen, da diese Seiten dieselbe Quelle haben.
Dieser Übergang ist nicht möglich, wenn Sie von developer.chrome.com
zu www.chrome.com
wechseln, da es sich um unterschiedliche Ursprünge und dieselbe Website handelt.
Ansichtsübergänge zwischen Dokumenten müssen aktiviert werden
Damit ein dokumentübergreifender Wechsel zwischen zwei Dokumenten möglich ist, müssen beide beteiligten Seiten dies zulassen. Dazu wird in CSS die At-rule @view-transition
verwendet.
Legen Sie im Attribut-Regeln-Element @view-transition
den navigation
-Beschreibungstext auf auto
fest, um Ansichtsübergänge für ursprungsübergreifende Navigationen innerhalb eines Dokuments zu aktivieren.
@view-transition {
navigation: auto;
}
Wenn du den navigation
-Beschreibungstext auf auto
festlegst, erlaubst du Ansichtsübergänge für die folgenden NavigationType:
traverse
push
oderreplace
, wenn die Aktivierung nicht vom Nutzer über die Browser-Benutzeroberfläche initiiert wurde.
Von auto
ausgeschlossen sind beispielsweise Navigationen über die URL-Adressleiste oder Klicks auf Lesezeichen sowie jede Art von vom Nutzer oder Script initiiertem Neuladen.
Wenn eine Navigation zu lange dauert (in Chrome mehr als vier Sekunden), wird der Ansichtsübergang mit einem TimeoutError
DOMException
übersprungen.
Demo für bereichsübergreifende Ansichtsübergänge
In der folgenden Demo werden Ansichtsübergänge verwendet, um eine Demo für den Stack Navigator zu erstellen. Hier werden keine Aufrufe von document.startViewTransition()
ausgeführt. Die Ansichtsübergänge werden durch das Wechseln von einer Seite zur anderen ausgelöst.
Ansichtsübergänge zwischen Dokumenten anpassen
Es gibt einige Funktionen der Webplattform, mit denen Sie Ansichtsübergänge zwischen Dokumenten anpassen können.
- Die Ereignisse
pageswap
undpagereveal
- Informationen zur Navigationsaktivierung
- Blockierung des Renderings
Diese Funktionen sind nicht Teil der View Transition API-Spezifikation, sondern wurden für die Verwendung in Verbindung mit dieser entwickelt.
Die Ereignisse pageswap
und pagereveal
Damit Sie Ansichtsübergänge zwischen Dokumenten anpassen können, enthält die HTML-Spezifikation zwei neue Ereignisse, die Sie verwenden können: pageswap
und pagereveal
.
Diese beiden Ereignisse werden bei jeder Navigation zwischen Dokumenten mit demselben Ursprung ausgelöst, unabhängig davon, ob eine Ansichtsübergang bevorsteht oder nicht. Wenn zwischen den beiden Seiten ein Seitenübergang bevorsteht, können Sie über die Property viewTransition
auf das ViewTransition
-Objekt zugreifen.
- Das Ereignis
pageswap
wird ausgelöst, bevor der letzte Frame einer Seite gerendert wird. So können Sie kurz vor dem Erstellen der alten Snapshots noch Last-Minute-Änderungen an der ausgehenden Seite vornehmen. - Das Ereignis
pagereveal
wird auf einer Seite ausgelöst, nachdem sie initialisiert oder reaktiviert wurde, aber noch vor der ersten Rendering-Möglichkeit. So können Sie die neue Seite anpassen, bevor die neuen Snapshots erstellt werden.
Mit diesen Ereignissen können Sie beispielsweise einige view-transition-name
-Werte schnell festlegen oder ändern oder Daten von einem Dokument an ein anderes übergeben, indem Sie Daten in sessionStorage
schreiben und lesen, um den Ansichtsübergang vor der Ausführung anzupassen.
let lastClickX, lastClickY;
document.addEventListener('click', (event) => {
if (event.target.tagName.toLowerCase() === 'a') return;
lastClickX = event.clientX;
lastClickY = event.clientY;
});
// Write position to storage on old page
window.addEventListener('pageswap', (event) => {
if (event.viewTransition && lastClick) {
sessionStorage.setItem('lastClickX', lastClickX);
sessionStorage.setItem('lastClickY', lastClickY);
}
});
// Read position from storage on new page
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
lastClickX = sessionStorage.getItem('lastClickX');
lastClickY = sessionStorage.getItem('lastClickY');
}
});
Sie können den Übergang in beiden Ereignissen überspringen.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
Das ViewTransition
-Objekt in pageswap
und pagereveal
sind zwei verschiedene Objekte. Außerdem werden die verschiedenen Versprechen unterschiedlich behandelt:
pageswap
: Sobald das Dokument ausgeblendet ist, wird das alteViewTransition
-Objekt übersprungen. In diesem Fall lehntviewTransition.ready
ab undviewTransition.finished
löst das Problem.pagereveal
: DasupdateCallBack
-Versprechen ist an dieser Stelle bereits erfüllt. Sie können die VersprechenviewTransition.ready
undviewTransition.finished
verwenden.
Informationen zur Navigationsaktivierung
Sowohl bei pageswap
- als auch bei pagereveal
-Ereignissen können Sie Aktionen basierend auf den URLs der alten und neuen Seiten ausführen.
Im MPA Stack Navigator hängt die Art der Animation beispielsweise vom Navigationspfad ab:
- Wenn du von der Übersichtsseite zu einer Detailseite wechselst, müssen die neuen Inhalte von rechts nach links eingeblendet werden.
- Wenn Sie von der Detailseite zur Übersichtsseite wechseln, müssen die alten Inhalte von links nach rechts herausgeschoben werden.
Dazu benötigen Sie Informationen zur Navigation, die im Fall von pageswap
kurz bevorsteht oder im Fall von pagereveal
gerade stattgefunden hat.
Dazu können Browser jetzt NavigationActivation
-Objekte bereitstellen, die Informationen zur Navigation innerhalb desselben Ursprungs enthalten. Dieses Objekt gibt den verwendeten Navigationstyp, den aktuellen und den letzten Zielverlaufseintrag aus navigation.entries()
aus der Navigation API an.
Auf einer aktivierten Seite können Sie über navigation.activation
auf dieses Objekt zugreifen. Im Ereignis pageswap
können Sie über e.activation
darauf zugreifen.
In dieser Demo für Profile werden NavigationActivation
-Informationen in den Ereignissen pageswap
und pagereveal
verwendet, um die view-transition-name
-Werte für die Elemente festzulegen, die an der Ansichtsübergang teilnehmen müssen.
So müssen Sie nicht jedes Element in der Liste vorab mit einem view-transition-name
ausstatten. Stattdessen geschieht dies Just-in-Time mit JavaScript und nur bei Elementen, die es benötigen.
Der Code sieht so aus:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove view-transition-names after snapshots have been taken
// (this to deal with BFCache)
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove names after snapshots have been taken
// so that we're ready for the next navigation
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
Der Code beseitigt auch die Spuren seiner Ausführung, indem die view-transition-name
-Werte nach dem Ausführen der Ansichtsübergang entfernt werden. So ist die Seite für nachfolgende Navigationen bereit und kann auch den Navigationsverlauf verarbeiten.
Verwenden Sie dazu diese Dienstprogrammfunktion, mit der view-transition-name
s vorübergehend festgelegt werden.
const setTemporaryViewTransitionNames = async (entries, vtPromise) => {
for (const [$el, name] of entries) {
$el.style.viewTransitionName = name;
}
await vtPromise;
for (const [$el, name] of entries) {
$el.style.viewTransitionName = '';
}
}
Der vorherige Code kann jetzt so vereinfacht werden:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
// Clean up after the page got replaced
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.finished);
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
// Clean up after the snapshots have been taken
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.ready);
}
}
});
Warten auf das Laden von Inhalten mit Renderblockierung
Unterstützte Browser
In einigen Fällen möchten Sie das erste Rendern einer Seite möglicherweise verzögern, bis ein bestimmtes Element im neuen DOM vorhanden ist. So wird ein Flackern vermieden und der Zustand, zu dem die Animation erfolgt, ist stabil.
Definieren Sie in der <head>
mithilfe des folgenden Meta-Tags eine oder mehrere Element-IDs, die vorhanden sein müssen, bevor die Seite zum ersten Mal gerendert wird.
<link rel="expect" blocking="render" href="#section1">
Dieses Meta-Tag bedeutet, dass das Element im DOM vorhanden sein sollte, nicht, dass der Inhalt geladen werden sollte. Bei Bildern reicht beispielsweise das Vorhandensein des <img>
-Tags mit dem angegebenen id
im DOM-Baum aus, damit die Bedingung als wahr ausgewertet wird. Das Bild selbst wird möglicherweise noch geladen.
Bevor Sie das Rendern blockieren, sollten Sie sich darüber im Klaren sein, dass das inkrementelle Rendern ein grundlegender Aspekt des Webs ist. Seien Sie also vorsichtig, wenn Sie das Rendern blockieren. Die Auswirkungen des Blockierens des Renderings müssen von Fall zu Fall bewertet werden. Verwenden Sie blocking=render
standardmäßig nur, wenn Sie die Auswirkungen auf Ihre Nutzer aktiv messen und bewerten können, indem Sie die Auswirkungen auf Ihre Core Web Vitals messen.
Übergangstypen in dokumentübergreifenden Übergängen ansehen
Für dokumentübergreifende Ansichtsübergänge werden auch Übergangstypen unterstützt, mit denen sich die Animationen und die erfassten Elemente anpassen lassen.
Wenn Sie beispielsweise in einer Paginierung zur nächsten oder zur vorherigen Seite wechseln, können Sie je nachdem, ob Sie zu einer höheren oder niedrigeren Seite der Sequenz wechseln, unterschiedliche Animationen verwenden.
Wenn Sie diese Typen vorab festlegen möchten, fügen Sie sie in die At-rule @view-transition
ein:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Wenn Sie die Typen „on the fly“ festlegen möchten, verwenden Sie die Ereignisse pageswap
und pagereveal
, um den Wert von e.viewTransition.types
zu ändern.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
Die Typen werden nicht automatisch vom ViewTransition
-Objekt auf der alten Seite in das ViewTransition
-Objekt der neuen Seite übernommen. Sie müssen mindestens für die neue Seite festlegen, welche Typen verwendet werden sollen, damit die Animationen wie erwartet ausgeführt werden.
Verwenden Sie die Pseudoklassenauswahl :active-view-transition-type()
, um auf diese Typen zu reagieren, genau wie bei Ansichtsübergängen innerhalb desselben Dokuments.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
Da Typen nur für einen aktiven Ansichtsübergang gelten, werden sie automatisch entfernt, wenn ein Ansichtsübergang abgeschlossen ist. Daher eignen sich Typen gut für Funktionen wie BFCache.
Demo
In der folgenden Paginierungsdemo wird der Seiteninhalt je nach aufgerufener Seitenzahl vor- oder zurückgeschoben.
Der zu verwendende Übergangstyp wird in den Ereignissen pagereveal
und pageswap
anhand der „Zu“- und „Von“-URLs bestimmt.
const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {
const currentURL = new URL(fromNavigationEntry.url);
const destinationURL = new URL(toNavigationEntry.url);
const currentPathname = currentURL.pathname;
const destinationPathname = destinationURL.pathname;
if (currentPathname === destinationPathname) {
return "reload";
} else {
const currentPageIndex = extractPageIndexFromPath(currentPathname);
const destinationPageIndex = extractPageIndexFromPath(destinationPathname);
if (currentPageIndex > destinationPageIndex) {
return 'backwards';
}
if (currentPageIndex < destinationPageIndex) {
return 'forwards';
}
return 'unknown';
}
};
Feedback
Wir freuen uns immer über Feedback von Entwicklern. Wenn Sie uns Vorschläge und Fragen senden möchten, erstellen Sie auf GitHub ein Problem für die CSS-Arbeitsgruppe. Stellen Sie dem Problem das Präfix [css-view-transitions]
voran.
Sollten Sie auf einen Fehler stoßen, erstellen Sie stattdessen einen Eintrag für das Problem in Chromium.