Quando si verifica una transizione della visualizzazione tra due documenti diversi, si parla di transizione della visualizzazione di più documenti. Questa situazione si verifica in genere nelle applicazioni con più pagine (MPA). Le transizioni della visualizzazione tra più documenti sono supportate in Chrome a partire da Chrome 126.
Le transizioni tra le visualizzazioni tra documenti si basano sugli stessi componenti di base e sugli stessi principi delle transizioni dalla visualizzazione dello stesso documento, che sono molto intenzionali:
- Il browser acquisisce istantanee degli elementi con un valore
view-transition-name
univoco sia nella pagina precedente che in quella nuova. - Il DOM viene aggiornato mentre il rendering viene soppresso.
- Infine, le transizioni sono basate sulle animazioni CSS.
La differenza rispetto alle transizioni della visualizzazione dello stesso documento è che non è necessario chiamare document.startViewTransition
per avviare una transizione di visualizzazione di più documenti. L'attivatore di una transizione della visualizzazione più documenti è invece una navigazione della stessa origine da una pagina all'altra, un'azione che in genere viene eseguita dall'utente del tuo sito web facendo clic su un link.
In altre parole, non esiste un'API da chiamare per avviare una transizione di visualizzazione tra due documenti. Tuttavia, ci sono due condizioni che devono essere soddisfatte:
- Entrambi i documenti devono esistere sulla stessa origine.
- Per consentire la transizione della visualizzazione, è necessario attivare entrambe le pagine.
Entrambe queste condizioni sono spiegate più avanti in questo documento.
Le transizioni tra le visualizzazioni tra documenti sono limitate alle navigazioni della stessa origine
Le transizioni tra le visualizzazioni tra documenti sono limitate solo alle navighe nella stessa origine. Una navigazione è considerata la stessa origine se l'origine di entrambe le pagine partecipanti è la stessa.
L'origine di una pagina è una combinazione dello schema utilizzato, del nome host e della porta, come dettagliato su web.dev.
Ad esempio, puoi avere una transizione della visualizzazione più documenti durante l'esplorazione da developer.chrome.com
a developer.chrome.com/blog
, poiché hanno la stessa origine.
Non puoi avere questa transizione durante la navigazione da developer.chrome.com
a www.chrome.com
, poiché sono multiorigine e dello stesso sito.
Le transizioni della visualizzazione tra documenti sono attivabili
Per eseguire una transizione della visualizzazione più documenti tra due documenti, entrambe le pagine partecipanti devono attivare questa opzione. Questa operazione viene eseguita con la regola at @view-transition
in CSS.
Nella regola at-rule @view-transition
, imposta il descrittore navigation
su auto
per attivare le transizioni di visualizzazione per le navigazioni tra più documenti con la stessa origine.
@view-transition {
navigation: auto;
}
Se imposti il descrittore navigation
su auto
, consenti l'esecuzione delle transizioni di visualizzazione per i seguenti NavigationType:
traverse
push
oreplace
, se l'attivazione non è stata avviata dall'utente tramite i meccanismi dell'interfaccia utente del browser.
Le navigazioni escluse da auto
consentono, ad esempio, di navigare utilizzando la barra degli indirizzi dell'URL o fare clic su un preferito, nonché con qualsiasi tipo di ricaricamento avviato da un utente o da uno script.
Se una navigazione richiede troppo tempo, ovvero più di quattro secondi nel caso di Chrome, la transizione di visualizzazione viene saltata con un DOMException
TimeoutError
.
Demo sulle transizioni tra più documenti
Guarda la seguente demo che utilizza le transizioni delle visualizzazioni per creare una demo di Stack Navigator. Non ci sono chiamate a document.startViewTransition()
qui. Le transizioni delle visualizzazioni vengono attivate passando da una pagina all'altra.
Personalizzare le transizioni della visualizzazione più documenti
Per personalizzare le transizioni della visualizzazione tra documenti, puoi utilizzare alcune funzionalità della piattaforma web.
- Gli eventi
pageswap
epagereveal
- Informazioni sull'attivazione della navigazione
- Blocco del rendering
Queste funzioni non fanno parte della specifica dell'API View Transizione ma sono progettate per essere utilizzate insieme.
Gli eventi pageswap
e pagereveal
Per consentirti di personalizzare le transizioni delle visualizzazioni tra documenti, la specifica HTML include due nuovi eventi che puoi utilizzare: pageswap
e pagereveal
.
Questi due eventi vengono attivati per ogni navigazione tra documenti nella stessa origine, indipendentemente dal fatto che si verifichi o meno una transizione di visualizzazione. Se sta per verificarsi una transizione della vista tra le due pagine, puoi accedere all'oggetto ViewTransition
utilizzando la proprietà viewTransition
in questi eventi.
- L'evento
pageswap
viene attivato prima che venga eseguito il rendering dell'ultimo frame di una pagina. Puoi utilizzarla per apportare alcune modifiche dell'ultimo minuto alla pagina in uscita, appena prima dell'acquisizione delle istantanee precedenti. - L'evento
pagereveal
si attiva su una pagina dopo che è stato inizializzato o riattivato, ma prima della prima opportunità di rendering. che ti consente di personalizzare la nuova pagina prima che vengano acquisite le nuove istantanee.
Ad esempio, puoi utilizzare questi eventi per impostare o modificare rapidamente alcuni valori view-transition-name
o per passare dati da un documento all'altro scrivendo e leggendo i dati di sessionStorage
per personalizzare la transizione della visualizzazione prima che venga effettivamente eseguita.
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');
}
});
Se vuoi, puoi decidere di saltare la transizione in entrambi gli eventi.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
L'oggetto ViewTransition
in pageswap
e pagereveal
sono due oggetti diversi. Inoltre, gestiscono le varie promesse in modo diverso:
pageswap
: una volta nascosto il documento, il vecchio oggettoViewTransition
viene ignorato. In questo caso,viewTransition.ready
lo rifiuta eviewTransition.finished
si risolve.pagereveal
: la promessaupdateCallBack
è già stata risolta a questo punto. Puoi usare le promesseviewTransition.ready
eviewTransition.finished
.
Informazioni sull'attivazione della navigazione
Negli eventi pageswap
e pagereveal
puoi intervenire anche in base agli URL delle pagine vecchie e nuove.
Ad esempio, in MPA Stack Navigator, il tipo di animazione da utilizzare dipende dal percorso di navigazione:
- Quando si passa dalla pagina Panoramica a una pagina dei dettagli, i nuovi contenuti devono scorrere da destra verso sinistra.
- Quando passi dalla pagina dei dettagli alla pagina Panoramica, i contenuti precedenti devono scorrere da sinistra a destra.
A tale scopo, hai bisogno delle informazioni sulla navigazione che, nel caso di pageswap
, sta per avvenire o che nel caso di pagereveal
si è appena verificato.
Per questo motivo, i browser ora possono esporre gli oggetti NavigationActivation
che contengono informazioni sulla navigazione nella stessa origine. Questo oggetto espone le voci della cronologia della destinazione utilizzata, corrente e finale, come indicato in navigation.entries()
dall'API Navigation.
In una pagina attivata, puoi accedere a questo oggetto tramite navigation.activation
. Nell'evento pageswap
, puoi accedere a questa funzionalità tramite e.activation
.
Guarda questa demo dei profili che utilizza le informazioni NavigationActivation
negli eventi pageswap
e pagereveal
per impostare i valori view-transition-name
sugli elementi che devono partecipare alla transizione della visualizzazione.
In questo modo, non dovrai decorare ogni singolo articolo dell'elenco con un view-transition-name
in anticipo. Ciò avviene giusto in tempo reale utilizzando JavaScript, ma solo negli elementi che ne hanno bisogno.
Il codice è il seguente:
// 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';
}
}
});
Il codice esegue automaticamente la pulizia rimuovendo i valori view-transition-name
dopo l'esecuzione della transizione della visualizzazione. In questo modo la pagina è pronta per le navigazioni successive e può anche gestire l'attraversamento della cronologia.
A tale scopo, utilizza questa funzione di utilità che imposta temporaneamente gli view-transition-name
.
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 = '';
}
}
Il codice precedente può ora essere semplificato come segue:
// 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);
}
}
});
Attendi il caricamento dei contenuti con il blocco della visualizzazione
In alcuni casi potresti voler sospendere il primo rendering di una pagina fino a quando un determinato elemento non è presente nel nuovo DOM. In questo modo si evita il flashing e si garantisce che lo stato dell'animazione sia stabile.
In <head>
, definisci uno o più ID elemento che devono essere presenti prima che la pagina venga visualizzata per la prima volta, utilizzando il meta tag riportato di seguito.
<link rel="expect" blocking="render" href="#section1">
Questo meta tag indica che l'elemento deve essere presente nel DOM, non che i contenuti devono essere caricati. Ad esempio, con le immagini, la semplice presenza del tag <img>
con il valore id
specificato nell'albero DOM è sufficiente affinché la condizione restituisca il valore true. L'immagine stessa potrebbe ancora essere in fase di caricamento.
Prima di andare a fondo sul blocco del rendering, tieni presente che il rendering incrementale è un aspetto fondamentale del web, quindi presta attenzione quando decidi di bloccarlo. L'impatto del blocco del rendering deve essere valutato caso per caso. Per impostazione predefinita, evita di usare blocking=render
, a meno che tu non possa misurare e valutare attivamente l'impatto che ha sui tuoi utenti misurando l'impatto sui tuoi Segnali web essenziali.
Visualizzare i tipi di transizione nelle transizioni della visualizzazione tra documenti
Le transizioni della visualizzazione tra documenti supportano anche i tipi di transizione delle visualizzazioni per personalizzare le animazioni e gli elementi acquisiti.
Ad esempio, quando passi alla pagina successiva o precedente in un'impaginazione, potresti voler utilizzare animazioni diverse a seconda che tu stia andando a una pagina più alta o a una più in basso rispetto alla sequenza.
Per configurare questi tipi in anticipo, aggiungili nella regola at @view-transition
:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Per impostare i tipi al volo, utilizza gli eventi pageswap
e pagereveal
per manipolare il valore di e.viewTransition.types
.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
I tipi non vengono trasferiti automaticamente dall'oggetto ViewTransition
della pagina precedente all'oggetto ViewTransition
della nuova pagina. Devi determinare i tipi da utilizzare almeno nella nuova pagina affinché le animazioni vengano eseguite come previsto.
Per rispondere a questi tipi, utilizza il selettore di pseudo-classe :active-view-transition-type()
come faresti con le transizioni della visualizzazione dello stesso documento
/* 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;
}
}
Poiché i tipi si applicano solo a una transizione di Visualizzazione attiva, i tipi vengono automaticamente ripuliti al termine della transizione di una visualizzazione. Per questo motivo, i tipi funzionano bene con funzionalità come BFCache.
Demo
Nella seguente demo di paginazione, i contenuti della pagina scorrono avanti o indietro in base al numero di pagina a cui stai accedendo.
Il tipo di transizione da utilizzare viene determinato negli eventi pagereveal
e pageswap
osservando i campi da e verso gli URL.
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
Il feedback degli sviluppatori è sempre apprezzato. Per condividerlo, invia una segnalazione al CSS Working Group su GitHub con suggerimenti e domande. Aggiungi il prefisso [css-view-transitions]
al problema.
Se riscontri un bug, segnala un bug di Chromium.