Transizioni della visualizzazione tra più documenti per le applicazioni con più pagine

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.

Supporto dei browser

  • 126
  • 126
  • x
  • x

Origine

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:

  1. Il browser acquisisce istantanee degli elementi con un valore view-transition-name univoco sia nella pagina precedente che in quella nuova.
  2. Il DOM viene aggiornato mentre il rendering viene soppresso.
  3. 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.

Un URL di esempio in cui sono evidenziati schema, nome host e porta. Combinati insieme, formano l'origine.
Un URL di esempio in cui sono evidenziati schema, nome host e porta. Combinati insieme formano l'origine.

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 o replace, 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.

Registrazione della demo di Stack Navigator. Richiede Chrome 126 o versioni successive.

Personalizzare le transizioni della visualizzazione più documenti

Per personalizzare le transizioni della visualizzazione tra documenti, puoi utilizzare alcune funzionalità della piattaforma web.

Queste funzioni non fanno parte della specifica dell'API View Transizione ma sono progettate per essere utilizzate insieme.

Gli eventi pageswap e pagereveal

Supporto dei browser

  • 124
  • 124
  • x
  • x

Origine

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 oggetto ViewTransition viene ignorato. In questo caso, viewTransition.ready lo rifiuta e viewTransition.finished si risolve.
  • pagereveal: la promessa updateCallBack è già stata risolta a questo punto. Puoi usare le promesse viewTransition.ready e viewTransition.finished.

Supporto dei browser

  • 123
  • 123
  • x
  • x

Origine

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.

Registrazione della demo dei profili. Richiede Chrome 126 o versioni successive.

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

Supporto dei browser

  • 124
  • 124
  • x
  • x

Origine

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.

Registrazione della demo della paginazione (MPA). Utilizza transizioni diverse a seconda della pagina che visiti.

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.