Comment utiliser l'API View Transitions ? Du hello world aux cas complexes.
lundi 06 février 2024
Vous avez peut-ĂȘtre entendu parler des View Transitions ? Câest la nouvelle API dans les navigateurs qui, avec une ligne de CSS et une ligne de JS promet dâanimer vos transitions de maniĂšre performante.
Des dĂ©mos assez chouettes sont dĂ©jĂ disponibles notamment autour de lâĂ©cosystĂšme Astro. Par exemple avec :
En ce qui nous concerne, on va se concentrer sur un cas : passer dâune liste contenant plusieurs articles imagĂ©s Ă une pleine page qui reprend la mĂȘme image dans son header.
Câest assez classique, mais cela reprĂ©sente plusieurs difficultĂ©s :
- Comment sâassurer que lâanimation de lâimage se fasse correctement dans les deux sensâŻ?
- Comment Ă©viter de dĂ©grader les performances en nâanimant que ce qui est nĂ©cessaireâŻ?
- Les dimensions entre la vue liste et la vue pleine page sont assez diffĂ©rentes : comment faire pour que lâanimation soit tout de mĂȘme fluideâŻ?
- Comment Ă©viter des flashs pendant le chargement de lâimage pleine pageâŻ?
đĄ Attention ce nâest pour lâinstant disponible que sur les navigateurs basĂ©s sur Chromium. Mais les autres navigateurs ont donnĂ© leur accord sur lâAPI et vont donc lâimplĂ©menter dans les annĂ©es Ă venir. De plus, cet article se concentrera uniquement sur les animations dĂ©clenchĂ©es en JavaScript. Nous ne nous attarderont pas sur le cas des Multi Pages Applications (MPAs) qui ne sont pas encore sorties du mode expĂ©rimental dans Chromium.
PrĂ©sentation de lâAPI View Transition
Avant de rentrer dans le cĆur du sujet, prenons un exemple sans animation :
Element
Essentiellement, il sâagit du code suivant :
input.addEventListener('change', () => {
element.classList.toggle('active');
});
Par le passĂ©, si jâavais voulu lâanimer, jâaurais dĂ» jouer sur des propriĂ©tĂ©s CSS, manipuler des positions, et peut-ĂȘtre utiliser des techniques comme les animations FLIP pour mâassurer que ce soit performant.
Mais grĂące Ă la nouvelle API des View Transitions, si je veux lâanimer, je nâai quâune seule chose Ă faire : entourer le code qui active la modification de style par document.startViewTransition
.
input.addEventListener('change', () => {
+ document.startViewTransition(() => {
element.classList.toggle('active');
+ });
})
đĄ Attention toutefois,
document.startViewTransition
nâest pas disponible partout. Veillez donc Ă vĂ©rifier son existence afin de ne pas casser tous les autres navigateurs :input.addEventListener('change', () => { + if ('startViewTransition' in document) { document.startViewTransition(() => { element.classList.toggle('active'); }); + } else { + element.classList.toggle('active'); + } })
Une fois ceci fait, vous obtiendrez le résultat suivant :
Element
Avec ce petit changement de code, on a maintenant un crossfade entre les deux Ă©tats, autrement dit lâĂ©tat prĂ©cĂ©dent disparaĂźt pendant que le nouvel Ă©tat apparaĂźt. En effet, document.startViewTransition
dit essentiellement au navigateur de :
- Prendre un screenshot de la page avant
- Prendre un screenshot de la page aprĂšs
- Animer la transition entre les deux
Mais animer lâopacitĂ© nâest pas toujours le mieux. Heureusement, il est possible dâindiquer au navigateur comment animer la transition.
Notamment, plutĂŽt que de faire un screenshot de la page entiĂšre, on peut lui dire dâanimer uniquement une petite partie. Dans notre cas, on ne veut animer que lâElement.
Pour cela, il nous faut ajouter une propriété CSS :
.element {
view-transition-name: element-id;
}
Vous pouvez lui donner nâimporte quelle valeur. Lâessentiel est que cet id soit unique sur votre page.
Element
Ainsi, dĂšs cet instant, le navigateur sait que lâĂ©lĂ©ment ne disparaĂźt pas mais est simplement dĂ©placĂ©. Alors il va faire sa magie noir et le dĂ©placer. đ
đĄ Accessibilité : Gardez en tĂȘte toutefois que des animations de la sorte peuvent donner la nausĂ©e Ă toute une partie de vos utilisateurices (par exemple celles et ceux atteint de troubles vestibulaires).
A ce titre, il est prĂ©fĂ©rable de dĂ©sactiver les animations de transition quand la prĂ©fĂ©rence âreduced motionâ est activĂ©e dans le navigateur.
Cela peut ĂȘtre fait de maniĂšre globale avec ces quelques lignes de CSS. AInsi, je vous conseille de lâajouter dans vos
reset
dĂšs aujourdâhui, plutĂŽt que de lâoublier demain.@media (prefers-reduced-motion: reduce) { * { view-transition-name: unset !important; } }
Faire fonctionner document.startViewTransition dans un framework JS
âĄïž Si vous vous en moquez des frameworks JS, RDV Ă la section suivante. đ
Lâexemple ci-dessus est un exemple qui fonctionne en Vanilla JS. Cela dit, gardez en tĂȘte quâil peut y avoir quelques subtilitĂ©s en fonction du framework que vous utilisez. En effet, la plupart des frameworks font une tĂąche dâoptimisation pour vous : quand vous mettez Ă jour votre state
, il ne met pas Ă jour directement votre DOM. Il attend un petit peu pour faire toutes les modifications dâun coup. La consĂ©quence, câest que cette mise Ă jour vient trop tard et donc lâanimation Ă©choue.
Lâastuce est donc de faire en sorte que la mise Ă jour soit synchrone ou, Ă dĂ©faut, dâattendre quâelle soit finie.
document.startViewTransition
en React
En React, pour faire une mise à jour synchrone, vous pouvez importer la méthode flushSync
et entourer votre changement dâĂ©tat par celle-ci.
+import { flushSync } from 'react-dom';
const [status, setStatus] = useState('closed');
function open() {
+ if ('startViewTransition' in document) {
+ document.startViewTransition(() => {
+ flushSync(() => {
+ setStatus('opened');
+ });
+ });
+ } else {
setStatus('opened');
+ }
}
document.startViewTransition
en Vue.js
Si vous modifiez la valeur dâune ref, le DOM sera mis Ă jour de maniĂšre synchrone. Vous pouvez donc utiliser document.startViewTransition sans plus de cĂ©rĂ©monie.
+import { tick } from 'svelte';
const status = ref('closed');
function open() {
+ if ('startViewTransition' in document) {
+ document.startViewTransition(() => {
+ status.value = 'opened';
+ });
+ } else {
status.value = 'opened';
+ }
}
document.startViewTransition
en Svelte
En Svelte, la mise Ă jour est nĂ©cessairement asynchrone. Donc plutĂŽt que de changer de mode, lâastuce est dâattendre la fin de la mise Ă jour asynchrone pour dĂ©clencher la transition. Pour cela, on va transformer le callback de startViewTransition pour quâil soit asynchrone et await tick()
pour ĂȘtre sĂ»r que la mise Ă jour est terminĂ©e.
let status = 'closed';
function open() {
+ if ('startViewTransition' in document) {
+ document.startViewTransition(async () => {
+ setStatus('opened');
+ await tick();
+ });
+ } else {
setStatus('opened');
+ }
}
Utiliser les View Transitions dans un cas complexe (vue liste vers vue page)
Si je ne vous ai pas encore achevĂ© avec mes explications, nous avons maintenant les bases de lâAPI des View Transitions. Mais, vous vous en doutez, on peut rapidement tomber dans des cas plus complexes. Pour mieux comprendre la magie qui se cache derriĂšre les View Transitions, nous allons donc essayer dâanimer la transition dâune liste vers un bandeau de header.
Cliquez sur un des chiffres ci-dessous pour simuler la vue page :
Lâexemple est plus compliquĂ© parce que :
- ce nâest pas juste un ajout de classe, la structure complĂšte du DOM change. En effet, on passe dâun
<button>
quand on est dans la vue liste, Ă un<h4>
quand on est sur la nouvelle page - selon oĂč on clique, on ne veut pas animer tous les Ă©lĂ©ments (si je clique sur 4, il ne faudrait pas que le 1 sâanime)
- la taille de lâĂ©lĂ©ment change afin de simuler le comportement dâune banniĂšre en haut de page
Voyons donc comment gérer ces différentes problématiques pour obtenir une animation aux petits oignons.
Comment faire la transition lorsquâil y a un changement de DOMâŻ?
Un peu plus haut, je vous disais que lâAPI fonctionnait en prenant un screenshot avant, un screenshot aprĂšs, puis fait une transition entre les deux. De fait, cela veut dire que câest compatible mĂȘme si le DOM change complĂštement entre avant et aprĂšs.
La seule chose Ă bien prendre en compte, câest de sâassurer que la propriĂ©tĂ© CSS view-transition-name
est présente avant ET aprÚs, et que son ID soit stable.
Ici, si je veux faire la transition de lâĂ©lĂ©ment 4 de la liste, vers le header 4, il va donc falloir que je fasse en sorte que :
.element--4 {
view-transition-name: element-id;
}
.header {
view-transition-name: element-id;
}
Mais ça ouvre le problÚme que je dois avoir un id différent pour chacun des éléments de la liste. Sinon, le navigateur ne saura pas quel screenshot prendre avant de faire la transition.
Dâailleurs, il vous prĂ©viendra dans votre console en affichant le message dâerreur : Unexpected duplicate view-transition-name: <name>
.
Le premier réflexe est donc de gérer cela en créant autant de view-transition-name
diffĂ©rents quâil y a dâĂ©lĂ©ments dans votre liste:
<!-- â ïž ne pas faire ça -->
<!-- Dans la vue liste -->
<button style="view-transition-name: element-1">1</button>
<button style="view-transition-name: element-2">2</button>
<button style="view-transition-name: element-3">3</button>
<button style="view-transition-name: element-4">4</button>
<!-- Dans la vue page -->
<header style="view-transition-name: element-4">4</header>
Mais cela a pour inconvĂ©nient que ça surcharge le travail du navigateur. Notamment, dans la phase avant, le navigateur va ĂȘtre obligĂ© de prendre un screenshot de tous les Ă©lĂ©ments qui ont une view-transition-name
. Si vous nâen avez que quelques uns sur votre site, ça peut le faire. Mais quand vous aurez adoptĂ© cette technique plus largement pour animer beaucoup de transitions, cela va commencer Ă surcharger votre navigateur et risque dâallonger le temps de vos interactions.
Notamment, jâai fait quelques tests et dĂšs 25 view-transition-name
, sur des pĂ©riphĂ©riques pas trĂšs puissants, on dĂ©passe les 100ms de latence entre le click et le dĂ©but de lâanimation. Donc Ă lâintĂ©raction on commence Ă ressentir un petit lag. DĂšs 200 Ă©lĂ©ments, on constate des lags pendant lâanimation, mĂȘme si lâanimation est un simple fade.

Il est donc important dâajouter des view-transition-name
uniquement quand vous en avez besoin, câest-Ă -dire juste avant le dĂ©but de lâanimation :
/**
* @var {number} item
*/
function selectItem(item) {
+ const button = document.querySelector(`#item-${item}`)
+ button.style.setProperty('view-transition-name', 'element');
document.startViewTransition(async () => {
goToPage(item);
});
}
Et de sâassurer que le header, une fois la page ouverte, ait bien la view-transition-name aussi.
.header {
view-transition-name: element;
}
Attention toutefois, cela gĂšre lâanimation dans un sens uniquement : quand vous partez de la page pour revenir Ă la liste, il faut aussi penser Ă ajouter la view-transition-name
sur le bouton.
/**
* @var {number} currentItem the id of the displayed header
*/
function unselectItem(currentItem) {
document.startViewTransition(async () => {
goToList();
+ const button = document.querySelector(`#item-${currentItem}`)
+ button.style.setProperty('view-transition-name', 'element');
});
}
Et grùce à ça vous obtenez cette animation :
Câest mieux, mais ce nâest pas encore tout Ă fait ça. Dans la section suivante, nous allons voir comment amĂ©liorer la transition pour Ă©viter ce sentiment de flash, particuliĂšrement visible sur desktop.
đĄ Cependant, avant ça, je veux faire un petit laĂŻus sur les frameworks JS, parce qu'il n'est pas forcĂ©ment Ă©vident de voir comment traduire le code JS ci-dessus dans le framework de votre choix.
Cliquez ici pour afficher expliquer comment gérer les View Transitions en React (adaptable aux autres frameworks :))
Si vous essayez de manipuler directement le DOM, React ne va pas ĂȘtre content parce quâil ne sera plus synchronisĂ© avec le contenu du DOM. Vous finirez juste par avoir une erreur et une page blanche. On va donc plutĂŽt se reposer sur le systĂšme de rendu classique. Pour cela, nous allons donc utiliser des states.
Les points quâil faut retenir sont :
- pour pouvoir correctement animer lâanimation de retour il vous faut stocker quelque part lâid de lâitem dâorigine. Ca peut ĂȘtre dans un state, dans la session ou dans lâURL. A vous dâimplĂ©menter les propriĂ©tĂ©s
props.select
,props.unselect
etprops.previousItem
. Le piste la plus directe serait de faire un composant parent qui possĂšde unuseState
.- pour pouvoir ajouter un
view-transition-name
juste avant de dĂ©clencher lâanimation, vous pouvez sĂ©parer votre handler dâĂ©vĂ©nement en 2 Ă©tapes : un premier flush/render qui ajoute laview-transition-name
, une deuxiÚme qui déclenche lastartViewTransition
.- dans cet exemple, je pars du principe que vous nâutilisez pas de router externe. Si vous en utilisez un, sachez que Remix supporte les View Transitions, TanStack Router semble pouvoir le faire grĂące au hook
useNavigate
, mais quâil nâest pour lâinstant pas possible de le faire en Next.js.Voici ce que ça donnerait avec du vrai code :
/** * @var {number} props.item * @var {(item: number) => void} props.unselect pour revenir à la vue liste */ function Page(props) { function close(item: number) { if ('startViewTransition' in document) { document.startViewTransition(() => { flushSync(() => { props.unselect(item); }); }); } else { props.unselect(item); } } return ( <div> <h4 className="header">{item}</h4> <button onClick={close}> Revenir à la liste </button> </div> ); } /** * @var {number[]} props.list * @var {number|null} props.previousItem récupéré depuis le props.unselect de la Page * @var {(item: number) => void} props.select pour aller à la vue page */ function List(props) { // Si on vient d'une Page, on aura le paramÚtre passé à props.unselect qui // sera disponible. // En le passant ensuite via props.previousItem, c'est ce qui nous permet // de s'assurer qu'on va bien animer le retour à la liste, et c'est pour // cette raison qu'on n'initialise pas toujours le state à `null` const [itemWithViewTransitionName, setItemWithTransitionName] = useState(previousItem); function open(item: number) { if ('startViewTransition' in document) { // Avant de déclencher l'animation on met à jour le state pour que le // bouton concerné récupÚre la bonne propriété CSS view-transition-name flushSync(() => { setItemWithTransitionName(item); }); // Puis seulement on déclenche l'animation document.startViewTransition(() => { flushSync(() => { props.select(item) }); }); } else { props.select(item) } } return ( <ul> {list.map((item) => ( <li key={item}> <button style={ itemWithViewTransitionName === item ? { viewTransitionName: 'element' } : undefined } onClick={() => open(item)} > {item} </button> </li> ))} </ul> ); }
Comment faire la View Transition quand lâĂ©lĂ©ment change de tailleâŻ?
Voyons maintenant plus en dĂ©tail pourquoi lâanimation ne paraĂźt pas fluide. Pour cela, nous allons utiliser lâonglet Animations dans les DevTools de Chrome (vu que lâAPI des View Transitions nâexiste pas ailleurs pour le moment).
Voir comment utiliser l'onglet Animations

- Cliquer sur âCustomize and control DevToolsâ
- Cliquer sur âMore toolsâ
- Choisir lâoutil âAnimationsâ
Cela vous donner accĂšs Ă ce panneau, sur lequel nous allons appuyer sur âPause allâ.

Dans les faits, cela bloque les prochaines animations qui pourraient advenir dans la page. Ainsi, vous pouvez aller cliquer sur notre exemple de transition et constater quâune nouvelle timeline est apparue dans la boĂźte dâanimations.
En cliquant dessus, on va voir toutes les animations qui sont en cours dâexecution mais avec un temps bloquĂ© Ă 0. Vous pouvez alors dĂ©placer le petit losange rouge afin de jouer petit Ă petit lâanimation.

Ainsi, si on inspecte lâanimation en milieu dâexĂ©cution, on constate que cela ressemble Ă ceci :

Il y a bien une transition avec les screen avant/aprĂšs, mais les tailles ne sont pas cohĂ©rentes. Le screenshot qui vient de la vue liste devient trĂšs grand. Il est beaucoup plus haut quâil ne le devrait.
Heureusement pour nous, on peut le corriger.
Notamment, pendant lâinspection de lâanimation, si on utilise lâoutil pour cibler un Ă©lĂ©ment du DOM, ça va nous amener tout en haut de lâonglet âElementsâ oĂč on verra des ::view-transition-group
, ::view-transition-old
, ::view-transition-new
, etc.

En fait, ce sont des pseudo Ă©lĂ©ments qui nâexistent que le temps de lâanimation. On peut imaginer ça comme la structure du DOM qui permet dâafficher les screenshots avant, aprĂšs, et orchestrer la transition entre les deux.
En les inspectant, on se rend compte quâon peut vraiment les imaginer comme des tags HTML avec des images auxquels on a donnĂ© des propriĂ©tĂ©s CSS, des animations avec des @keyframes, etc.
Ce que ça veut dire, câest que si le navigateur a mal positionnĂ© ces images, on peut ajouter des propriĂ©tĂ©s CSS pour corriger ça. Notamment, on va souvent avoir besoin de corriger les tailles, les object-fit
, les positions, etc.
Dans notre cas, il faudrait que pendant la transition, le screenshot de lâĂ©tat aprĂšs, reprĂ©sentĂ© par le pseudo Ă©lĂ©ment ::view-transition-new(<name>)
(oĂč <name>
est la valeur de la propriété CSS view-transition-name
), soit de la taille de la petite boĂźte violette plutĂŽt que la grande boĂźte verte.

Ce qui nous donnerait le code CSS suivant :
/* J'active ces changements sur le screenshot avant (view-transition-old)
ET sur le screenshot aprĂšs (view-transition-new) parce qu'il faut que
l'animation fonctionne de la mĂȘme façon Ă l'ouverture et Ă la fermeture. */
html::view-transition-old(element),
html::view-transition-new(element) {
/* `block-size` et `inline-size` peuvent ĂȘtre compris comme
height & width.
Dans cet exemple, on les unset parce qu'on ne veut pas qu'il y
ait de transformation de taille, mais uniquement un
déplacement/agrandissement de la zone visible. */
block-size: unset;
inline-size: unset;
/* Par défaut les pseudo éléments sont déjà positionnés en absolu,
mais ferrés en haut à gauche. Dans notre cas, on veut que ce soit
centré, donc on utilise la bonne vieille méthode du 50% - 50% */
top: 50%;
left: 50%;
translate: -50% -50%;
/* Par défaut les transitions ont un fade-in et un fade-out pour
s'assurer d'avoir une transition fluide entre des éléments qui changent
de couleur ou de contenu. Ici, on sait que seul la fenĂȘtre de visibilitĂ©
change. Donc on peut désactiver cette animation. */
animation-name: unset;
}
Cependant, si on ne fait que ça, il nous reste un problĂšme : effectivement, lâĂ©lĂ©ment nâest plus trop petit, mais on a perdu lâagrandissement. Le nouveau screenshot est toujours visible.
Pour cela, on va sâaider de la totalitĂ© de la structure des ::view-transition-xxx
. Notamment, on peut voir que ::view-transition-old
et ::view-transition-new
sont entourés par un ::view-transition-image-pair
qui, lui, est toujours Ă la bonne taille.

Ne me demandez pas pourquoi sur le screen ci-dessus, ça affiche
::view-transition-group
, câest bien le::view-transition-image-pair
que je survole dans le DOM đ
Donc pour sâassurer que les images Ă lâintĂ©rieur ne dĂ©passent jamais celui-ci, quel est le CSS quâon ajouteâŻ?
html::view-transition-image-pair(element) {
overflow: hidden;
}
Et voilĂ đ Notre liste sâanime parfaitement Ă lâouverture et Ă la fermeture dâune page. đ
Pour aller plus loin
Voici quelques cas que vous pourriez essayer de creuser/implémenter pour vérifier que vous avez bien assimiler toutes ces notions.
Comment est-ce que vous animeriez une liste qui est composée d'un texte ET d'une image pour chaque élément ? (Cliquez pour voir la réponse)
Vous avez plusieurs options, mais cognitivement, moins vous déplacez d'élément, plus l'animation sera facile à comprendre. Donc il y a des chances que ce soit mieux de n'animer que l'image. Notamment la guideline "Unified direction" de Material Design montre bien son impacte.
Mais si vous voulez animer plusieurs Ă©lĂ©ments, nâhĂ©sitez pas Ă ajouter plusieurs view-transition-name
. Par exemple, pour que le lien âRevenir Ă la listeâ ne soit pas trop violent, je lui ai ajoutĂ© une view-transition-name
pour quâil ait un joli fade-in/fade-out le temps de lâanimation.
Pourquoi est-ce qu'à l'animation de mon image, j'ai un flash blanc disgracieux ? (Cliquez pour voir la réponse)
Si vous cliquez sur le bouton "Agrandir" avec un réseau qui est suffisamment lent, l'animation n'aura pas le comportement que vous espérez : la premiÚre version de l'image (petite) fera un fade out, puis pendant quelques (milli)secondes, il ne s'affichera rien, et enfin, quand la nouvelle image (grande) aurai fini de se charger, elle apparaßtra aussi sec.

DĂšs que vous commencerez Ă manipuler des images dans les View Transitions, ce comportement arrivera. En effet, vous avez des contraintes trĂšs diffĂ©rentes entre vos diffĂ©rentes pages. Par exemple, sur une vue liste, il vous faudra peut ĂȘtre une image en 300x300 et sur la vue pleine page, il vous faudra du 1920x600 sur desktop.
Dans la plupart des dĂ©mos que j'ai vu passer, le partie pris est de charger une image suffisamment grande pour que ce soit la mĂȘme URL d'image sur la liste ET sur la page.
C'est désastreux pour l'expérience de navigation si vous n'avez pas une connexion fibrée parce que votre liste mettra trop de temps à se charger.
A la place, vous avez deux options :
- Vous pouvez prĂ©charger l'image quand vous anticipez que la prochaine action risque d'ĂȘtre l'ouverture de la page (par exemple : si on a un mouseover ou un focus sur desktop, ou si on a un IntersectionObserver suffisamment long sur mobile). Ainsi, le preload permet d'ajouter la grande image en cache et donc de l'avoir Ă disposition au moment oĂč on dĂ©clenche la transition. La probabilitĂ© pour que la grande image n'ait pas le temps d'ĂȘtre tĂ©lĂ©chargĂ©e est beaucoup plus faible, et la plupart des personnes n'auront pas de flash.
Ou bien, toujours commencer par afficher la page avec la petite image. Ainsi, pendant la transition, c'est exactement la mĂȘme image qui est affichĂ©e (la petite), et pendant que la transition se joue, vous commencez Ă tĂ©lĂ©charger la grande image afin de l'afficher qu'Ă partir du moment oĂč vous avez fini de la tĂ©lĂ©charger. Voici, ci-dessous, ce que ça donnerait :
function selectItem(item) { document.startViewTransition(async () => { // La fonction goToPage doit bien faire attention // à afficher la page avec comme source la petite image goToPage(item); const src = await loadImage(`/image/item/${item}/big`) // Une fois seulement que la grande image a été téléchargée // (et donc mise en cache), alors on vient l'utiliser // dans la page. replacePageImageWith(src); }); } /** * Essaye de télécharger une image et résout la promesse * dÚs que celle-ci a fini de se télécharger */ async function loadImage(src: string) { return new Promise((resolve, reject) => { const img = document.createElement('img'); img.addEventListener('load', () => { resolve(src); }); img.addEventListener('error', (error) => { reject(error); }); img.src = src; // L'image peut avoir déjà été téléchargée. // Dans ce cas, l'event `load` n'est pas // déclenché. Il faut donc vérifier cela // manuellement. if (img.complete) { resolve(src); } }); }
Sauf si vous avez des yeux bioniques, je suis prĂȘt Ă parier que dans une navigation normale, vous ne remarquerez pas la diffĂ©rence. Mais dans le cas dâun rĂ©seau lent, ça fera toute la diffĂ©rence.
Dans l'exemple de cet article le contenu ne change pas de scale. Il n'y a que la taille de la zone visible qui change. Comment feriez-vous si l'image que vous animez était légÚrement zoomée aprÚs la transition ? (Cliquez pour voir la réponse)
Lâastuce ne sera plus dâunset les block-size
et inline-size
, mais de privilĂ©gier lâutilisation de object-fit
et object-position
. En effet, chaque screenshot est une image, donc toutes les propriĂ©tĂ©s qui vous aident dâordinaire Ă positionner des images vous seront aussi utiles ici.
Notez aussi que parfois des propriétés dynamiques comme inline-size: fit-content
peuvent vous sauver la mise.
Une fois votre transition finie, vous voulez déclencher d'autres animations (ex: fade-in d'un texte) ou utiliser des méthodes de easing. Comment orchestrer ça ? (Cliquez pour voir la réponse)
Par défaut, je mentionnais le fait qu'une view-transition se joue avec un fade-in (opacité 0 à 1). C'est d'ailleurs pour ça qu'on avait fait un animation-name: unset;
dans l'exemple de cet article. Donc si vous prĂ©fĂ©rez dĂ©caler l'animation d'un Ă©lĂ©ment, ça pourrait ĂȘtre en utilisant un animation-delay
.
Si les séquences sont vraiment complexes et bien séparées, sachez aussi que vous pouvez récupérer une promesse quand votre premiÚre animation est terminée.
const viewTransition = document.startViewTransition(() => ...);
await viewTransition.updateCallbackDone;
Conclusion
Avec tout ça, vous devriez ĂȘtre parĂ© pour animer tout ce qui vous passe sous la main đ
Cela dit, cette API reste particuliĂšrement adaptĂ©e quand vous voulez passer un Ă©lĂ©ment dâun point A Ă un point B. Câest pour ça quâon parle de transition
. Elle permet notamment de gĂ©rer des cas complexes oĂč le DOM change complĂštement.
Mais si vous avez une solution simple en CSS que vous arrivez à gérer avec des @keyframes
classiques, des propriétés transition
, qui restent performantes, letâs goâŻ! Nâoubliez pas tout ce que vous avez dĂ©jĂ pu utiliser par le passĂ©.
Si par contre, vous vous demandez jusquâoĂč on peut pousser cet API et vous avez envie de faire des tests et des expĂ©riences, simples ou compliquĂ©es, jâaimerais beaucoup en entendre parler.
La suite pour cette API est son activation dans un contexte de MPA (la page se recharge complĂštement). Câest aujourdâhui encore sous feature flag dans Chrome et jâai personnellement Ă©tĂ© confrontĂ© Ă quelques Ă©lĂ©ments bloquants. Mais dĂšs que ça se dĂ©bloque, je vous en parle đ
En tout cas, si ce genre de contenus vous plait ou que vous aimez lire des trucs sur le web, la webperf ou le front de maniĂšre gĂ©nĂ©ral, nâhĂ©sitez pas Ă me suivre sur les rĂ©seaux sociaux (Mastodon, LinkedIn ou Twitter) et Ă me dire ce que je devrais amĂ©liorer pour la suite.
Prenez soin de vous, et Ă trĂšs vite đ«¶
Si vous voulez suivre mes publications, il paraĂźt que j'ai un feed RSS, Mastodon et un Twitter.
Si vous pensez à d'autres méthodes que vous voudriez que je mette en place (pigeon voyageur, avion en papier, etc.), n'hésitez pas à me les proposer :)