Transitions d'affichage entre les documents pour les applications multipages

Lorsqu'une transition d'affichage se produit entre deux documents différents, on parle de transition de vue entre documents. C'est généralement le cas dans les applications multipages. Les transitions d'affichage de plusieurs documents sont compatibles avec Chrome à partir de Chrome 126.

Navigateurs pris en charge

  • 126
  • 126
  • x
  • x

Source

Les transitions de vue d'un document à l'autre reposent sur les mêmes éléments de base et les mêmes principes que les transitions de vue d'un même document, ce qui est très intentionnel:

  1. Le navigateur capture des instantanés des éléments qui ont un view-transition-name unique sur l'ancienne et la nouvelle page.
  2. Le DOM est mis à jour alors que le rendu est supprimé.
  3. Enfin, les transitions sont générées par des animations CSS.

La différence par rapport aux transitions d'affichage pour un même document est qu'il n'est pas nécessaire d'appeler document.startViewTransition pour démarrer une transition d'affichage. Au lieu de cela, le déclencheur d'une transition de vue entre documents est une navigation de même origine d'une page à une autre, une action généralement effectuée lorsque l'utilisateur de votre site Web clique sur un lien.

En d'autres termes, il n'existe aucune API à appeler pour démarrer une transition d'affichage entre deux documents. Toutefois, deux conditions doivent être remplies:

  • Les deux documents doivent exister sur la même origine.
  • Les deux pages doivent être activées pour permettre la transition d'affichage.

Ces deux conditions sont expliquées plus loin dans ce document.


Les transitions d'affichage de plusieurs documents sont limitées aux navigations de même origine

Les transitions de vue de plusieurs documents sont limitées aux navigations de même origine uniquement. Une navigation est considérée comme ayant la même origine si l'origine des deux pages participantes est identique.

L'origine d'une page est une combinaison du schéma, du nom d'hôte et du port utilisés, comme indiqué sur le site web.dev.

Exemple d'URL avec le schéma, le nom d'hôte et le port en surbrillance Ensemble, ils constituent l'origine.
Exemple d'URL avec le schéma, le nom d'hôte et le port en surbrillance Ensemble, ils constituent l'origine.

Par exemple, vous pouvez définir une transition de vue multidocument lorsque vous passez de developer.chrome.com à developer.chrome.com/blog, car ils sont de même origine. Cette transition ne peut pas s'effectuer lorsque vous passez de developer.chrome.com à www.chrome.com, car il s'agit d'une transition multi-origine et sur un même site.


Les transitions d'affichage entre des documents sont activables.

Pour passer d'un document à l'autre, vous devez activer l'option correspondante sur les deux pages participantes. Pour ce faire, utilisez la règle at @view-transition dans CSS.

Dans la règle at @view-transition, définissez le descripteur navigation sur auto afin d'activer les transitions d'affichage pour les navigations de même origine entre plusieurs documents.

@view-transition {
  navigation: auto;
}

En définissant le descripteur navigation sur auto, vous autorisez les transitions de vue pour les éléments NavigationType suivants:

  • traverse
  • push ou replace, si l'activation n'a pas été initiée par l'utilisateur via les mécanismes de l'UI du navigateur.

Les navigations exclues de auto incluent, par exemple, la navigation à l'aide de la barre d'adresse URL ou un clic sur un favori, ainsi que toute forme d'actualisation lancée par un utilisateur ou un script.

Si une navigation dure trop longtemps (plus de quatre secondes dans le cas de Chrome), la transition d'affichage est ignorée avec une DOMException TimeoutError.

Démonstration des transitions d'affichage entre plusieurs documents

Regardez la démonstration suivante, qui utilise des transitions d'affichage pour créer une démonstration de Stack Navigator. Il n'y a pas d'appel à document.startViewTransition() ici. Les transitions d'affichage sont déclenchées lorsque l'utilisateur passe d'une page à une autre.

Enregistrement de la démo de Stack Navigator. Nécessite Chrome 126 ou une version ultérieure.

Personnaliser les transitions d'affichage entre les documents

Pour personnaliser les transitions d'affichage entre plusieurs documents, vous pouvez utiliser certaines fonctionnalités de la plate-forme Web.

Ces fonctionnalités ne font pas partie de la spécification de l'API View Transition, mais sont conçues pour être utilisées conjointement avec elle.

Événements pageswap et pagereveal

Navigateurs pris en charge

  • 124
  • 124
  • x
  • x

Source

Pour vous permettre de personnaliser les transitions d'affichage entre les documents, la spécification HTML inclut deux nouveaux événements que vous pouvez utiliser: pageswap et pagereveal.

Ces deux événements sont déclenchés pour chaque navigation sur plusieurs documents à partir de la même origine, qu'une transition de vue soit sur le point de se produire ou non. Si une transition de vue est sur le point de se produire entre les deux pages, vous pouvez accéder à l'objet ViewTransition à l'aide de la propriété viewTransition sur ces événements.

  • L'événement pageswap se déclenche avant l'affichage du dernier frame d'une page. Vous pouvez ainsi apporter des modifications de dernière minute à la page sortante, juste avant que les anciens instantanés ne soient pris.
  • L'événement pagereveal se déclenche sur une page après son initialisation ou sa réactivation, mais avant la première opportunité d'affichage. Elle vous permet de personnaliser la nouvelle page avant de prendre les nouveaux instantanés.

Par exemple, vous pouvez utiliser ces événements pour définir ou modifier rapidement certaines valeurs view-transition-name, ou pour transmettre des données d'un document à un autre en écrivant et en lisant les données de sessionStorage afin de personnaliser la transition de vue avant qu'elle s'exécute réellement.

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');
  }
});

Si vous le souhaitez, vous pouvez ignorer la transition pour les deux événements.

window.addEventListener("pagereveal", async (e) => {
  if (e.viewTransition) {
    if (goodReasonToSkipTheViewTransition()) {
      e.viewTransition.skipTransition();
    }
  }
}

L'objet ViewTransition dans pageswap et pagereveal sont deux objets différents. De plus, ils gèrent les différentes promesses différemment:

  • pageswap: une fois le document masqué, l'ancien objet ViewTransition est ignoré. Dans ce cas, viewTransition.ready le refuse, tandis que viewTransition.finished se résout.
  • pagereveal: la promesse updateCallBack est déjà résolue à ce stade. Vous pouvez utiliser les promesses viewTransition.ready et viewTransition.finished.

Navigateurs pris en charge

  • 123
  • 123
  • x
  • x

Source

Dans les événements pageswap et pagereveal, vous pouvez également effectuer des actions en fonction des URL des anciennes et des nouvelles pages.

Par exemple, dans le MPA Stack Navigator, le type d'animation à utiliser dépend du chemin de navigation:

  • Lorsque vous passez de la page "Vue d'ensemble" à une page d'informations, le nouveau contenu doit glisser de droite à gauche.
  • Lorsque vous passez de la page d'informations à la page de présentation, l'ancien contenu doit glisser de gauche à droite.

Pour ce faire, vous avez besoin d'informations sur la navigation qui, dans le cas de pageswap, est sur le point de se produire ou, dans le cas de pagereveal, qui vient de se produire.

Pour ce faire, les navigateurs peuvent désormais exposer des objets NavigationActivation qui contiennent des informations sur la navigation de même origine. Cet objet expose le type de navigation utilisé, les entrées d'historique de destination actuelles et finales comme indiqué dans navigation.entries() de l'API Navigation.

Sur une page activée, vous pouvez accéder à cet objet via navigation.activation. Dans l'événement pageswap, vous pouvez y accéder via e.activation.

Découvrez cette démonstration des profils, qui utilise les informations NavigationActivation dans les événements pageswap et pagereveal pour définir les valeurs view-transition-name sur les éléments qui doivent participer à la transition de vue.

Ainsi, vous n'avez pas besoin de décorer chaque élément de la liste avec une view-transition-name au départ. Cette opération intervient juste à temps en utilisant JavaScript, uniquement pour les éléments qui en ont besoin.

Enregistrement de la démonstration des profils Nécessite Chrome 126 ou une version ultérieure.

Le code est le suivant :

// 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';
    }
  }
});

Le code effectue également un nettoyage après lui-même en supprimant les valeurs view-transition-name une fois la transition de vue exécutée. La page est ainsi prête pour des navigations successives et peut également gérer le balayage de l'historique.

Pour vous faciliter la tâche, utilisez cette fonction utilitaire qui définit temporairement les 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 = '';
  }
}

Le code précédent peut maintenant être simplifié comme suit:

// 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);
    }
  }
});

Attendre que le contenu soit chargé avec blocage de l'affichage

Navigateurs pris en charge

  • 124
  • 124
  • x
  • x

Source

Dans certains cas, vous pouvez attendre le premier affichage d'une page jusqu'à ce qu'un élément donné se trouve dans le nouveau DOM. Cela permet d'éviter le flash et de garantir la stabilité de l'état sur lequel vous effectuez l'animation.

Dans le fichier <head>, utilisez la balise Meta suivante pour définir un ou plusieurs ID d'élément qui doivent être présents avant que la page ne s'affiche pour la première fois.

<link rel="expect" blocking="render" href="#section1">

Cette balise Meta indique que l'élément doit être présent dans le DOM, et non que le contenu doit être chargé. Par exemple, pour les images, la simple présence de la balise <img> avec l'élément id spécifié dans l'arborescence DOM suffit pour que la condition soit remplie. Il est possible que l'image soit toujours en cours de chargement.

Avant de vous lancer dans le blocage de l'affichage, sachez que l'affichage incrémentiel est un aspect fondamental du Web. Soyez donc prudent lorsque vous choisissez de bloquer l'affichage. L'impact du blocage de l'affichage doit être évalué au cas par cas. Par défaut, évitez d'utiliser blocking=render, sauf si vous pouvez mesurer et évaluer activement son impact sur vos utilisateurs en mesurant l'impact sur vos métriques Core Web Vitals.


Afficher les types de transition dans les transitions d'affichage entre les documents

Les transitions d'affichage de plusieurs documents sont également compatibles avec les types de transition d'affichage, qui permettent de personnaliser les animations et les éléments à capturer.

Par exemple, lorsque vous passez à la page suivante ou précédente dans une pagination, vous pouvez utiliser des animations différentes selon que vous accédez à une page supérieure ou inférieure de la séquence.

Pour définir ces types à l'avance, ajoutez-les dans la règle at @view-transition:

@view-transition {
  navigation: auto;
  types: slide, forwards;
}

Pour définir les types à la volée, utilisez les événements pageswap et pagereveal pour manipuler la valeur de 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);
  }
});

Les types ne sont pas automatiquement transférés de l'objet ViewTransition de l'ancienne page vers l'objet ViewTransition de la nouvelle page. Vous devez déterminer le ou les types à utiliser au moins sur la nouvelle page pour que les animations s'exécutent comme prévu.

Pour répondre à ces types, utilisez le sélecteur de pseudo-classe :active-view-transition-type() de la même manière qu'avec les transitions de vue d'un même document.

/* 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;
  }
}

Étant donné que les types ne s'appliquent qu'à une transition Active View, ils sont automatiquement nettoyés à la fin d'une transition de vue. Pour cette raison, les types fonctionnent bien avec des fonctionnalités telles que BFCache.

Démonstration

Dans la démonstration de la pagination suivante, le contenu de la page glisse vers l'avant ou vers l'arrière en fonction du numéro de page auquel vous accédez.

Enregistrement de la démonstration de la pagination (MPA). Il utilise différentes transitions selon la page que vous consultez.

Le type de transition à utiliser est déterminé dans les événements pagereveal et pageswap en examinant les URL de destination et de départ.

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';
  }
};

Commentaires

Vos commentaires sont toujours importants pour les développeurs. Pour ce faire, signalez un problème au groupe de travail CSS sur GitHub en posant des suggestions et en posant des questions. Résolvez le problème avec [css-view-transitions]. Si vous rencontrez un bug, signalez-le dans Chromium.