Créer des transitions de page de niveau professionnel en pur JavaScript

Nous allons découvrir dans cet article comment créer de superbes transitions de pages sans recourir à la moindre librairie tierce, en pure JavaScript.

Créer des transitions de page de niveau professionnel en pur JavaScript
Photo by Jorge Vasconez / Unsplash

Avez-vous toujours rêvé d'ajouter des transitions du niveau de ce qui est proposé par des librairies comme Framer Motion pour React.js ? Pensez-vous encore que cela n'est pas à la portée de n'importe qui et qu'il faut avoir un master en Design Graphique Web pour pouvoir y arriver ?

Si ce sont les questions que vous vous êtes déjà posé (je me les suis aussi posé auparavant), rassurez-vous, ces temps sont révolus.

Nous allons découvrir dans cet article comment créer de superbes transitions de pages sans recourir à la moindre librairie tierce, en pure JavaScript.

Qu'est-ce qu'une transition ?

Une transition est l'ensemble des étapes qui permettent, depuis un point de départ, de nous emmener vers un point d'arrivée.

On pourrait parler de la transition d'état d'un solide vers un liquide par réchauffement par exemple dans le cas d'un glaçon : ce sont toutes les étapes qui nous ont mené d'un glaçon, vers un liquide, en passant par l'état où le glaçon est déformé par la chaleur.

En ce qui concerne une page, le parallèle peut également être fait puisque si nous imaginons notre navigateur Web comme un appareil photo, il pourrait s'agir de toutes les photos qui constituerait le passage d'une page d'accueil vers une page de contact par exemple.

En rassemblant toutes ces photos, prises par exemple toutes les 0,1 secondes, nous pourrions recréer une vidéo qui serait la transition entre les deux pages, si on imagine une transition simple via un fondu.

L'API Web ViewTransition

Bien sûr, pour pouvoir faire tout cela, il faudrait pouvoir créer un système de toute pièce depuis zéro, ce qui serait bien pénible et nous ralentirait dans notre progression vers la création d'une application Web aux allures d'application mobile.

Pour cela, il existe, comme pour la plupart des problèmes rencontrés dans le Web, une API Web qui est l'API Web View Transition.

Cette API Web nous permet, comme son nom l'indique, de créer des transitions entre différentes vues. Une vue correspond à une page. Cette page peut tout à fait être une page HTML classique comme nous avons l'habitude d'en créer sur des frameworks comme Laravel ou Symfony, mais elle peut aussi très bien être une page virtuelle, comme c'est le cas sur des librairies et frameworks comme React.js et Vue.js.

Mais bien en dehors de tout cela, une vue est simplement l'état du DOM à un instant T. La transition entre deux vues correspond donc à un instant T et un instant T+1. Nous pourrions par exemple créer un élément HTML via l'API Web DOM, le remplacer complètement par un autre élément, et appeler cela une page ou une vue. C'est d'ailleurs comme cela que fonctionnent les frameworks JavaScript.

Pour pouvoir la faire fonctionner, il suffit d'utiliser la méthode startViewTransition sur l'objet global document de notre navigateur en JavaScript.

document.startViewTransition(() => {
  // Le code de transition ici
});

C'est tout ! Facile non ?

Transition de Single-Page Application

Pour donner un exemple concret, de ce qui pourrait se rapprocher de ce qui se passerait du point de vue d'un framework JavaScript, imaginons que nous ayons une première page contenant un paragraphe, et sur une deuxième page, un titre de niveau 1. L'exemple n'est pas proche du réel, mais cela nous donnera une bonne base pour la compréhension.

const oldView = document.createElement("p");

oldView.textContent = "Bienvenue sur la première page";

document.body.appendChild(oldView);

Nous venons, dans le code précédent, de créer un paragraphe, avec du contenu, et nous l'avons ajouté au corps de notre page HTML qui sera affichée aux yeux des utilisateurs.

Maintenant, imaginons que nous souhaitons, plus tard, au moment d'un click de bouton de changement de page, passer sur une nouvelle page, mais qui va totalement supprimer le contenu précédent, et afficher plutôt comme indiqué précédemment, un titre de niveau 1.

document.body.removeChild(oldView);

const newView = document.createElement("h1");

newView.textContent = "Bienvenue sur la seconde page";

document.body.appendChild(newView);

En faisant cela, bien qu'il y ait quand même un peu de code, nous pourrions avoir un « changement de page » instantannée, dont la suppression et l'ajout d'élément serait imperceptible, car ces instructions sont plutôt rapide (et bien qu'on puisse en dire le contraire, le navigateur est une plateforme très optimisée pour la manipulation du DOM), mais nous n'aurions pas une belle transition d'écran comme nous pourrions en avoir nativement sur des plateformes comme Android (Kotlin) et iOS (Swift).

Mais rassurez-vous, en très peu de lignes (nous les avons vu précédemment), nous allons pouvoir commencer à activer de la transition sur n'importe quelle vue, ou page.

document.startViewTransition(() => {
  document.body.removeChild(oldView);

  const newView = document.createElement("h1");

  newView.textContent = "Bienvenue sur la seconde page";

  document.body.appendChild(newView);
});

Tout ce que nous avons eu à faire, était d'englober notre code dans l'API Web qui permet d'activer les transitions de page.

Des transitions de page personnalisé

Par défaut, il n'y a rien à faire pour commencer à avoir des transitions de page basé sur une fonction de fondu, déjà prévue par défaut par les développeurs de l'API, elle conviendra pour les cas d'usages les plus classiques.

Mais il est également possible de combiner cette API Web avec l'API Web Animation afin d'avoir des transitions de page qui vont beaucoup plus loin et nous laissent carte blanche pour pouvoir animer de la façon dont nous souhaitons nos pages.

Par défaut, il n'y a rien à faire pour commencer à avoir des transitions de page basées sur une fonction de fondu, déjà prévue par défaut par les développeurs de l'API, elle conviendra pour les cas d'usage les plus classiques.

const transition = document.startViewTransition(() => {
  document.body.removeChild(oldView);

  const newView = document.createElement("h1");

  newView.textContent = "Bienvenue sur la seconde page";

  document.body.appendChild(newView);
});

await transition.ready;

const oldStartFrame = {
  opacity: 0
};

const oldEndFrame = {
  opacity: 1
};

const oldKeyframes = [
  oldStartFrame,
  oldEndFrame
];

const oldOptions = {
  duration: 250,
  easing: "ease-in-out",
  fill: "both",
  pseudoElement: "::view-transition-old(root)",
};

document.documentElement.animate(oldKeyframes, oldOptions);

const newStartFrame = {
  opacity: 1
};

const newEndFrame = {
  opacity: 0
};

const newKeyframes = [
  newStartFrame,
  newEndFrame
];

const newOptions = {
  duration: 250,
  easing: "ease-in-out",
  fill: "both",
  pseudoElement: "::view-transition-new(root)",
};

document.documentElement.animate(newKeyframes, newOptions);

Ok, il y a un peu plus de code, mais rassurez-vous, nous allons les expliquer.

const transition = document.startViewTransition(() => {
  // ...
});

await transition.ready;

Premièrement, il est possible de récupérer un objet renvoyé par l'appel de la méthode startViewTransition. C'est très utile puisque cela nous permet de savoir quand la transition va commencer, et nous permettra ensuite de modifier l'animation à utiliser sur les pseudo-éléments qui seront créés par le navigateur pour animer la transition de page.

const oldStartFrame = {
  opacity: 0
};

const oldEndFrame = {
  opacity: 1
};

const oldKeyframes = [
  oldStartFrame,
  oldEndFrame
];

Ces constantes nous permettent justement de contrôler la manière dont la page sera animée. La constante oldStartFrame correspond aux animations qui seront utilisées par la toute première frame, donc la toute première photo de la page (avant sa transition).

En toute logique, la constante oldEndFrame correspond à la dernière « photo » de la page, donc son état d'animation final.

Le tout est inclus dans la constante oldKeyFrames qui rassemble toutes les animations, entre le début et la fin, c'est là où le navigateur va ensuite pouvoir interpoller toutes ces données pour créer l'animation en question, en passant par une visibilité totale, vers une opacité complète, à un certain FPS (adapté selon les ressources de l'appareil).

const oldOptions = {
  duration: 250,
  easing: "ease-in-out",
  fill: "both",
  pseudoElement: "::view-transition-old(root)",
};

Ici, ce sont des options qui permettent de configurer la transition, comme sa durée (duration), sa fonction de transition (easing) et surtout l'élément qui sera visé par la transition (pseudoElement).

Ici, nous visons un pseudo-élément, nous en avons parlé précédemment. Pour détailler, le navigateur doit animer deux états : une page et une autre (ou toute autre chose que vous souhaitez considérer comme une page).

Il va donc prendre deux photos : une de l'ancienne page (la page actuelle), et une de la nouvelle page (il le fait dans un thread séparé afin de ne pas l'ajouter directement sur la page de l'utilisateur), et une fois que cela est fait, il fait la différence pour savoir comment créer toutes les photos entre les deux qui permettront de créer un film, qui sera notre transition.

::view-transition-old(root) correspond à notre ancienne page, et ::view-transition-new(root) Correspondra à notre nouvelle page.

C'est pour cela également que nous avons eu besoin d'englober tout le code permettant de passer à une nouvelle page dans la méthode startViewTransitionCar si le navigateur ajoutait la nouvelle page tout de suite, il serait bien difficile de créer une transition par la suite.

document.documentElement.animate(oldKeyframes, oldOptions);

La méthode animate Nous permet simplement d'activer cette animation sur l'élément en question. Tout cela est évidemment répété pour la « nouvelle page » visée par le pseudo-élément ::view-transition-new(root), et nous avons là une belle animation entre deux pages que nous pouvons désormais contrôler à notre guise.

Pour aller plus loin

C'est tout ce que je souhaitais vous montrer dans cette article qui est déjà suffisamment détaillé, sans non plus aller trop loin dans les détails et vous permettre quand même d'avoir les clés pour pouvoir vous amuser et chercher des choses que nous n'avons pas encore fait.

C'est d'ailleurs un exercice que je laisse à la discrétion du lecteur d'essayer d'implémenter une transition en slide (comme sur Powerpoint) vers la gauche ou la droite (ou les deux en fonction de si nous retournons en arrière ou en avant dans l'historique de navigation) pour pouvoir animer les pages qui sont consultés sur un site ou une application Web.

À savoir que nous n'avons montré qu'un exemple en pur JavaScript dans un cas hypothétique d'une Single-Page Application, il est encore plus simple et tout à fait possible de le faire pour une Multi-Page Application ! Essayez de vous replonger dans un projet Laravel ou Symfony et d'implémenter cela. Il est même possible de le faire sans JavaScript.

En l'état, ce code est déjà utilisable, y compris sur les frameworks JavaScript que vous utilisez, néanmoins la difficulté sera de capturer tout ce qui peut engendrer une changement de page, cela peut très bien être des événements déclenché par une librairie de routage, par le navigateur, par une action utilisateur, etc...

J'ai d'ailleurs créé ma propre librairie me permettant de naviguer de page en page sur React, sans passer par React Router ou Tanstack Router, et qui implémente désormais une transition de slide par défaut.

https://www.npmjs.com/package/@aminnairi/react-router

Vous pouvez maintenant créer des expériences proches d'un appareil mobile à moindre coût de développement ! Alors, plutôt Slide ou Fade ?