Améliorations de l'API Web Animations dans Chromium 84

Orchestration d'animations avec des promesses, amélioration des performances grâce à des animations remplaçables, animations plus fluides avec des modes composites, etc.

Kevin Ellis
Kevin Ellis

Lorsqu'elles sont utilisées correctement, les animations améliorent la perception et la mémoire de votre marque, guident les actions des utilisateurs et aident les utilisateurs à naviguer dans votre application, en fournissant du contexte dans un espace numérique.

L'API Web Animations est un outil qui permet aux développeurs d'écrire des animations impératives avec JavaScript. Il a été écrit pour soutenir les implémentations d'animation et de transition CSS, et permettre le développement de futurs effets, ainsi que la composition et la synchronisation des effets existants.

Bien que Firefox et Safari aient déjà implémenté l'ensemble complet de fonctionnalités spécifiques, Chromium 84 apporte de nombreuses fonctionnalités auparavant incompatibles avec Chrome et Edge, ce qui permet l'interopérabilité entre les navigateurs.

L'API Web Animations a été utilisée pour la première fois dans Chromium dans la version 36 (juillet 2014). La spécification sera désormais terminée dans la version 84, qui sera lancée en juillet 2020.
Historique historique de l'API Web Animations dans Chromium.

Premiers pas

Créer une animation à l'aide de l'API Web Animations est un jeu d'enfant si vous avez utilisé des règles @keyframe. Vous devez d'abord créer un objet Keyframe. Voici comment cela pourrait se présenter en CSS:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

se présente comme suit en JavaScript:

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

Où vous définissez les paramètres d'animation en CSS:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

que vous définiriez en JavaScript:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

La quantité de code est à peu près identique, mais avec JavaScript, vous disposez de deux superpouvoirs que le CSS seul n'offre pas. Cela inclut la possibilité de séquencer des effets et un meilleur contrôle de leurs états de lecture.

Au-delà de element.animate()

Toutefois, avec la mise à jour, l'API Web Animations n'est plus limitée aux animations créées via element.animate(). Nous pouvons également manipuler des animations et des transitions CSS.

getAnimations() est une méthode qui renvoie toutes les animations sur un élément, qu'il ait été créé via element.animate() ou via des règles CSS (animation ou transition CSS). Voici un exemple:

Commencez par "get" les images clés de la transition afin de déterminer la source de la transition. Vous allez ensuite créer deux animations d'opacité, ce qui permet d'activer l'effet de fondu enchaîné. Une fois le fondu enchaîné terminé, vous supprimez la copie.

Orchestrer des animations avec des promesses

Dans Chromium 84, vous disposez désormais de deux méthodes qui peuvent être utilisées avec des promesses: animation.ready et animation.finished.

  • animation.ready vous permet d'attendre que les modifications en attente soient appliquées (c'est-à-dire, passer d'une méthode de contrôle de la lecture à une autre, comme lecture et pause).
  • animation.finished permet d'exécuter du code JavaScript personnalisé lorsqu'une animation est terminée.

Poursuivons avec notre exemple en créant une chaîne d'animation ordonnée avec animation.finished. Ici, nous obtenons une transformation verticale (scaleY), suivie d'une transformation horizontale (scaleX), puis d'un changement d'opacité sur un élément enfant:

Application de transformations et d'opacité à un élément modal d'ouverture. Voir la démonstration sur Codepen
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

Nous avons enchaîne ces animations à l'aide de animation.finished.then() avant d'exécuter l'ensemble d'animations suivant dans la chaîne. De cette façon, les animations apparaissent dans l'ordre, et vous appliquez même des effets à différents éléments cibles avec différentes options définies (comme la vitesse et l'aisance).

En CSS, cette opération serait fastidieuse à recréer, en particulier lors de l'application d'animations uniques, mais séquencées, à plusieurs éléments. Vous devez utiliser un @keyframe, trier les pourcentages de temps corrects pour placer les animations et utiliser animation-delay avant de déclencher les animations dans la séquence.

Exemple: Lire, mettre en pause et inverser

Ce qui peut s'ouvrir devrait se fermer ! Heureusement, depuis Chromium 39, l'API Web Animations nous permet de lire, mettre en pause et inverser nos animations.

Vous pouvez utiliser l'animation ci-dessus et la générer une animation fluide et inversée lorsque vous cliquez à nouveau sur le bouton à l'aide de .reverse(). De cette façon, vous pouvez créer une interaction plus fluide et plus contextuelle pour notre modale.

Exemple d'ouverture et de fermeture d'une fenêtre modale suite à un clic sur un bouton. Voir la démonstration sur Glitch

Vous pouvez créer deux animations en attente de lecture (openModal et une transformation d'opacité intégrée), puis mettre en pause l'une des animations et la retarder jusqu'à ce que l'autre soit terminée. Vous pouvez ensuite utiliser des promesses en attendant que chacune d'elles soit terminée avant de jouer. Enfin, vous pouvez vérifier si un indicateur est défini, puis inverser chaque animation.

Exemple: Interactions dynamiques avec des images clés partielles

Exemple de reciblage : un clic de souris permet d'ajuster l'animation à un nouvel emplacement. Voir la démonstration sur Glitch
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

Dans cet exemple, il n'y a qu'une seule image clé, et aucune position de départ n'a été spécifiée. Cet exemple illustre l'utilisation d'images clés partielles. Le gestionnaire de souris effectue plusieurs opérations: il définit un nouveau lieu d'arrivée et déclenche une nouvelle animation. La nouvelle position de départ est déduite de la position sous-jacente actuelle.

De nouvelles transitions peuvent être déclenchées alors que les transitions existantes sont encore en cours d'exécution. Cela signifie que la transition en cours est interrompue et qu'une nouvelle transition est créée.

Amélioration des performances grâce aux animations remplaçables

Lorsque vous créez des animations basées sur des événements, tels que 'mousemove', une animation est créée à chaque fois, ce qui peut rapidement consommer de la mémoire et dégrader les performances. Pour résoudre ce problème, des animations remplaçables ont été introduites dans Chromium 83 afin d'activer le nettoyage automatisé. Les animations terminées sont signalées comme remplaçables et automatiquement supprimées si elles sont remplacées par une autre animation terminée. Prenons l'exemple suivant :

Une traînée de comète s'anime lorsque la souris se déplace. Voir la démonstration sur Glitch
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

À chaque déplacement de la souris, le navigateur recalcule la position de chaque balle dans le tracé des comètes et crée une animation vers ce nouveau point. Le navigateur sait désormais qu'il faut supprimer les anciennes animations (pour permettre le remplacement) dans les cas suivants:

  1. L'animation est terminée.
  2. Une ou plusieurs animations de niveau supérieur dans l'ordre composite sont également terminées.
  3. Les nouvelles animations animent les mêmes propriétés.

Vous pouvez voir exactement combien d'animations sont remplacées en comptabilisant un compteur pour chaque animation supprimée, en utilisant anim.onremove pour déclencher le compteur.

Il existe quelques méthodes supplémentaires pour aller encore plus loin dans le contrôle de votre animation:

  • animation.replaceState() permet de savoir si une animation est active, persistante ou supprimée.
  • animation.commitStyles() met à jour le style d'un élément en fonction du style sous-jacent ainsi que de toutes les animations de l'élément dans l'ordre composite.
  • animation.persist() marque une animation comme non remplaçable.

Animations plus fluides grâce aux modes composites

Avec l'API Web Animations, vous pouvez désormais définir le mode composite de vos animations, ce qui signifie qu'elles peuvent être additives ou cumulatives, en plus du mode par défaut "Remplacer". Les modes composites permettent aux développeurs d'écrire des animations distinctes et de contrôler la façon dont les effets sont combinés. Trois modes composites sont désormais acceptés: 'replace' (mode par défaut), 'add' et 'accumulate'.

Lorsque vous créez des animations composites, un développeur peut écrire des effets courts et distincts, qu'il peut associer les uns aux autres. Dans l'exemple ci-dessous, nous appliquons une image clé de rotation et de mise à l'échelle à chaque zone, le seul ajustement étant le mode composite, qui a été ajouté en tant qu'option:

Démonstration présentant les modes composites par défaut, d'ajout et d'accumulation. Voir la démonstration sur Glitch

En mode composite 'replace' par défaut, l'animation finale remplace la propriété de transformation et se termine à rotate(360deg) scale(1.4). Pour 'add', un composite ajoute la rotation et multiplie l'échelle. L'état final est donc rotate(720deg) scale(1.96). 'accumulate' combine les transformations, ce qui donne rotate(720deg) scale(1.8). Pour en savoir plus sur les subtilités de ces modes composites, consultez les énumérations CompositeOperation et CompositeOperationOrAuto de la spécification Web Animations.

Examinons un exemple d'élément d'interface utilisateur:

Menu déroulant dynamique auquel deux animations composées sont appliquées. Voir la démonstration sur Glitch

Ici, deux animations top sont composées. La première est une macro-animation, qui déplace la liste déroulante selon la hauteur totale du menu en tant qu'effet coulissant à partir du haut de la page. La seconde, une micro-animation, applique un léger rebond lorsqu'elle atteint le bas de la page. L'utilisation du mode composite 'add' permet une transition plus fluide.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

L'avenir de l'API Web Animations

Il s'agit d'ajouts intéressants aux fonctionnalités d'animation des navigateurs actuels, et d'autres ajouts vont être apportés par la suite. Consultez ces spécifications futures pour en savoir plus sur ce qui va suivre: