Wenn ein Ansichtsübergang zwischen zwei verschiedenen Dokumenten stattfindet, wird dies als dokumentübergreifender Ansichtsübergang bezeichnet. Dies ist normalerweise bei mehrseitigen Anwendungen (MPA) der Fall. Ansichtsübergänge zwischen Dokumenten werden in Chrome ab Version 126 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.
Der Unterschied zu Ansichtsübergängen für dasselbe Dokument besteht darin, dass bei dokumentübergreifenden Änderungen der Ansicht nicht document.startViewTransition
aufgerufen werden muss, 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 denselben Ursprung haben.
- 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
Dokumentübergreifende Übergänge in der Ansicht sind auf Navigationen mit demselben Ursprung 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 verwendetem Schema, Hostname und 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 dokumentübergreifende Navigationen mit demselben Ursprung 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 Benutzeroberfläche des Browsers 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 Stack Navigator-Demo zu erstellen. Hier gibt es keine Aufrufe von document.startViewTransition()
. 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');
}
});
Wenn Sie möchten, können Sie 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 diesem Punkt 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 verfügbar machen, die Informationen zur Navigation am selben Ursprung enthalten. Dieses Objekt zeigt den verwendeten Navigationstyp, die aktuellen Einträge und die endgültigen Zielverlaufseinträge aus navigation.entries()
aus der Navigation API an.
Auf einer aktivierten Seite können Sie über navigation.activation
auf dieses Objekt zugreifen. Im pageswap
-Ereignis 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 Dienstfunktion, die view-transition-name
s vorübergehend festlegt.
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 kann es sinnvoll sein, das erste Rendern einer Seite zu warten, bis ein bestimmtes Element im neuen DOM vorhanden ist. So wird ein Flackern vermieden und der Zustand, zu dem animiert wird, 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 sich für eine Komplettblockierung entscheiden, sollten Sie sich bewusst sein, dass das inkrementelle Rendering ein grundlegender Aspekt des Webs ist. Seien Sie also vorsichtig, wenn Sie das Rendering 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
Bei dokumentübergreifenden Übergängen der Ansicht werden auch Arten von Ansichten unterstützt, mit denen die Animationen und die erfassten Elemente angepasst werden können.
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 im Voraus festlegen möchten, fügen Sie die Typen in der At-Regel @view-transition
hinzu:
@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 Active View-Übergang gelten, werden Typen automatisch bereinigt, wenn ein Ansichtsübergang abgeschlossen ist. Daher funktionieren Typen gut mit 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.