Nicolas Leport - Blog personnel
Il ne peut plus rien nous arriver d'affreux maintenant
– Red is dead, La cité de la peur
  Temps de lecture estimé : 5 min
Exploration sur les animations à 2 états

Exploration sur les animations à 2 états

Comment faire des animations qui bouclent complètement avant de se terminer en jouant une deuxième animation de fin.

État des lieux

Partant d'une discussion sur l'ergonomie avec ma femme UI/UX designer, nous nous sommes interrogés sur la façon de faire des beaux loaders et en quoi ça pouvait augmenter la satisfaction utilisateur et l'émotion procurée par l'interface. J'ai décidé de prendre le sujet côté technique et le premier point sera justement sur les loaders.

Pendant longtemps j'ai été fasciné par l'écran de démarrage des smartphone OnePlus : deux ronds blancs qui tournent autour d'un rond rouge en se poussant à chaque itération. Puis lorsque le chargement est fini, lors de la dernière loop, les deux ronds blancs se rejoignent et descendent sur le rond rouge pour terminer l'animation de chargement.

C'est ce que nous allons essayer de reproduire en décortiquant la chose.

➡ Vous pouvez voir le résultat final ici ou ici avec VueJS

Comment ça fonctionne

Disons que nous avons un écran de chargement suite à la soumission d'un formulaire assez velu, nous avons besoin de montrer à l'utilisateur que ça charge pour le rassurer sur le fait que son formulaire est bien envoyé.

Notre écran de chargement se compose d'un loader animé qui tourne en boucle. Une boucle qui va s'afficher à l'écran pendant une durée indéterminée.

Pour notre example l'animation durera 1 seconde et va se répéter X fois. Une deuxième animation qui durera elle aussi 1 seconde sera jouée après les X répétitions de la première.

Décomposition de la timeline

Okay, c'est parti, dessinons une timeline :

Loading timeline

En regardant de plus près, on peut voir le petit trick se produire entre 3 et 5 secondes. Comment attendre que notre loop courante soit terminée - de façon agnostique à la durée de l'animation - pour déclencher notre animation de fin puis masquer l'écran de chargement ?

La solution serait d'informer notre loader que notre chargement est terminé, et qu'à la prochaine itération il prenne le chemin B plutôt que le chemin A.

Loading timeline detail

Heureusement pour nous, nous avons un moyen simple de faire tout ça avec les CSS Animations.

Création de l'animation CSS

Nous allons dans un premier temps créer l'animation de boucle. Nous n'entrerons pas spécifiquement dans le détail de la création ici car il y a énormément de façon de faire des animations de chargement en CSS. Ici je vais utiliser des @keyframes CSS ainsi que des custom properties pour rendre mon loader modulable.

Notre boucle créée nous allons pouvoir créer l'animation de fin. J'y ajoute un button simple histoire de pouvoir ajouter/enlever la classe qui change l'animation. Au passage, on ajoute quelques keyframes et un peu de HTML...

C'est pas mal, on commence à avoir quelque chose qui ressemble à ce qu'on veut, reste qu'il faudrait que la classe is-finishing s'ajoute au bon moment pour que ce soit transparent.

Soyons un peu plus dynamique

Reprenons nos timelines : nous devons vérifier à chaque fin d'itération de notre animation de boucle si nous devons relancer une autre itération ou bien finir l'animation.

Vous l'avez ? Nous allons utiliser l'événement animationiteration !

Comme chaque événement, la propagation dans le DOM nous permet de mettre le addEventListener sur notre loader directement et pas sur le rond qui est animé.

Dans notre cas de figure, aucune vérification s'impose mais si notre animation de boucle était plus complexe, nous devrions certainement vérifier que le target de notre événement correspond bien à la boucle en que l'on souhaite tester.

Pour savoir si nous devons relancer une itération, il est nécessaire qu'on sache si notre chargement est fini, ou pas. Nous allons donc nous référer à un Boolean qui nous indiquera si notre requête est finie ou non.

Une bonne pratique pourrait être d'utiliser une Date plutôt qu'un Boolean. En effet, nous pourrions avoir besoin à terme de regarder quand la requête s'est terminée. Pour notre code ça ne change pas grand chose sachant qu'une Date est une valeur truthy.

Et voici le résultat final :

Pour voir plus loin

Afin de montrer un petit peu à quoi pourrait ressembler le code dans une application plus moderne j'ai fait un exemple rapide de la même démo mais avec VueJS. Le JS gagne en clarté et le two-way data binding nous rend beaucoup de services pour la gestion des classes et des actifs !

Notons également l'utilisation des composants Teleport et Transition pour améliorer encore un peu plus l'expérience utilisateur.

Si vous souhaitez approfondir votre savoir en VueJS, n'hésitez pas à vous rapprocher de notre service training chez Zenika, mes collègues et moi se feront un plaisir de vous former sur les sujets de vos choix 🤘

Quelques pistes d'améliorations pour conclure :

  • Si notre requête échoue, ajouter une classe spécifique d'erreur et jouer une animation d'erreur (par exemple une croix rouge) plutôt que notre coche verte
  • Ajouter un bouton retry en cas d'erreur

C'est-y pas beau tout ça ? Merci à tous 🙏

Sommaire