Le problème
Les sites Web proposent souvent plusieurs vues d'un objet, par exemple une liste de produits, puis une page d'informations pour chaque produit. La navigation entre les deux vues semble souvent déconnectée. Lorsqu'un utilisateur clique sur la miniature d'un produit pour afficher ses détails, la miniature disparaît et une nouvelle image plus grande apparaît instantanément ailleurs sur l'écran. Ce manque de continuité empêche les utilisateurs de suivre facilement les relations entre les éléments.
La solution
L'API View Transitions vous permet de spécifier des paires d'éléments qui existent dans différents états avant et après une transition. Lors du déclenchement d'une transition avec document.startViewTransition() dans une navigation sur une seule page (SPA), le navigateur identifie ces éléments partagés par leur view-transition-name unique partagé. Il calcule ensuite automatiquement la différence de position, de taille et de style, et les anime de manière fluide de l'ancien état au nouvel état. Cette transition se produit dans la couche supérieure, au-dessus des éléments ayant des valeurs z-index élevées.
Guide d'implémentation
Étape 1 : Encapsulez les changements d'état dans startViewTransition
Pour les applications monopages (SPA) ou les changements d'état simples, encapsulez la logique qui met à jour le DOM dans document.startViewTransition. Le navigateur capture un instantané de l'état actuel, exécute la mise à jour, puis capture le nouvel état.
function navigate(view) {
// MANDATORY: Wrap the update in startViewTransition
document.startViewTransition(() => updateDOM(view));
}
Étape 2 : Attribuer des noms de transition partagés
Utilisez la propriété CSS view-transition-name pour indiquer au navigateur les éléments à transformer. Le nom peut être n'importe lequel (sauf none). OBLIGATOIRE : il ne doit pas y avoir plus d'un élément avant et après avec un view-transition-name donné. S'il existe deux éléments ou plus avec un view-transition-name donné, le DOM sera immédiatement mis à jour vers le nouvel état, sans transition.
Vous pouvez utiliser plusieurs view-transition-name pour transformer plusieurs paires d'éléments. Par exemple, vous pouvez appliquer des transitions distinctes à l'image et au titre du produit.
Comme la vue sous forme de liste contient plusieurs éléments, vous ne pouvez pas leur attribuer le même view-transition-name. Dans une SPA, ce problème peut être résolu de deux manières.
Page d'informations dynamique : attribuez un
view-transition-nameunique à chaque élément de la page de liste, puis appliquez dynamiquement ce nom à l'élément correspondant sur la page d'informations lorsque l'élément de la liste est sélectionné, comme illustré ici./* In the list view, give each */ #product-1 { view-transition-name: p1 } #product-2 { view-transition-name: p2 } #product-3 { view-transition-name: p3 }function updateDOM(clickedTransitionName){ const hero = document.getElementById("hero"); hero.style.viewTransitionName = clickedTransitionName; }Élément de liste dynamique : attribuez un
view-transition-nameà l'élément sur la page d'informations, puis appliquez ce nom à l'élément sur la page de liste lorsqu'il est sélectionné. Supprimez l'élémentview-transition-namede l'élément de la page de liste lorsque vous revenez à la page de liste.
L'élément #hero de la page d'informations et l'élément .thumbnail sélectionné de la page de liste partagent un view-transition-name.
#hero{
view-transition-name: hero;
}
.thumbnail.selected {
view-transition-name: hero;
}
Lorsqu'un utilisateur clique sur une miniature, nous devons préparer la vue sous forme de liste en attribuant view-transition-name à l'aide du sélecteur de classe .selected et en apportant les modifications nécessaires au DOM avant de lancer la transition.
Vous pouvez ensuite appeler document.startViewTransition() et appliquer les modifications pour passer de la vue détaillée à la vue sous forme de liste.
Après être revenu à la vue sous forme de liste, vous devez nettoyer les classes de transition de vue pour éviter que la navigation suivante ne génère une erreur. Vous pouvez effectuer ce nettoyage une fois que la promesse finished de la transition est résolue.
// Function called when a thumbnail is clicked
function goFromListToDetail(e){
e.currentTarget.classList.add("selected");
const hero = document.getElementById("hero");
const bgColor = getComputedStyle(e.currentTarget).backgroundColor;
hero.style.background = bgColor;
// Trigger the transition, checking for support
if (!document.startViewTransition) {
document.body.classList.add("detail");
// MANDATORY Accessibility Routing: Route focus to the newly revealed heading to announce context and preserve logical tab flow
document.getElementById("detail-heading")?.focus();
return; // MANDATORY: End function execution if view transitions are not supported.
}
const transition = document.startViewTransition(() => {
document.body.classList.add("detail");
});
// MANDATORY Accessibility Routing: Route focus after the view transition resolves
transition.finished.finally(() => {
document.getElementById("detail-heading")?.focus();
});
}
// Function called when navigating from detail back to list view
function goFromDetailToList() {
if (!document.startViewTransition) {
document.body.classList.remove("detail");
document.getElementById("list-heading")?.focus();
return;
}
const transition = document.startViewTransition(() => {
document.body.classList.remove("detail");
});
// Clean up the list view and route focus
transition.finished.finally(() => {
// Route focus back to list view
document.getElementById("list-heading")?.focus();
// Remove selected classList to remove view-transition-names
document.querySelectorAll(".selected").forEach(
(element) => {
element.classList.remove("selected");
},
);
});
}
La méthode que vous choisissez dépend du cas d'utilisation. L'élément de liste dynamique nécessite moins de CSS répété, mais plus de nettoyage manuel du code JavaScript.
Étape 3 : Corrigez l'étirement du format
Par défaut, le navigateur effectue un fondu enchaîné entre les anciens et les nouveaux instantanés d'un groupe qui s'étire pour s'adapter aux deux. Si vous effectuez une transition de texte, définissez la largeur de l'élément de texte sur fit-content dans l'ancienne et la nouvelle vue, afin que le format de l'élément de transition soit stable.
#list-page .title {
width: fit-content;
}
#detail-page #title {
width: fit-content;
}
Si vous effectuez une transition entre des éléments qui changent de format, vous devrez peut-être définir la hauteur de l'ancien et du nouveau pseudo-élément sur 100 % du pseudo-élément ::view-transition-pair().
::view-transition-old(hero),
::view-transition-new(hero){
height: 100%;
}
Les pseudo-éléments sont des instantanés des éléments actifs. Vous pouvez donc également utiliser les déclarations object-fit et object-position pour mieux contrôler l'effet de transition.
Bonnes pratiques
- À NE PAS FAIRE : spécifier trop de transitions. N'utilisez des éléments partagés que pour le contenu principal que l'utilisateur suit activement (par exemple, les images héros, les titres).
- À FAIRE : Supprimez les valeurs
view-transition-nametemporaires une fois la transition terminée pour éviter les effets secondaires sur les transitions futures. - NE PAS effectuer de transition d'éléments avec des animations actives. Les transitions de vue fonctionnent sur des instantanés. Par conséquent, toutes les animations semblent être mises en pause pendant la transition de vue.
- À FAIRE : Respectez les préférences des utilisateurs concernant la réduction des mouvements à l'aide de la requête média
prefers-reduced-motion. - OBLIGATOIRE Routage d'accessibilité : les transitions de vue modifient dynamiquement les mises en page, mais ne gèrent pas le focus de manière programmatique. Si le curseur reste sur un élément masqué ou supprimé pendant la transition, il est abandonné, ce qui prive les utilisateurs de clavier et de technologies d'assistance de contexte. Déplacez le curseur de manière programmatique vers un titre de page ou un conteneur de vue mis à jour (à l'aide de
tabindex="-1") immédiatement après la mise à jour du DOM ou lorsque la promessefinishedde la transition de vue est résolue.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Stratégies de remplacement
L'API View Transitions est conçue pour l'amélioration progressive. Les navigateurs qui ne sont pas compatibles exécuteront simplement la mise à jour du DOM immédiatement, sans animation.
function navigate(){
if (!document.startViewTransition) {
// Fallback: Just update the DOM
updateDOM();
} else {
document.startViewTransition(() => updateDOM());
}
}