Des animations qui ne ralentissent pas : transform, opacity et le travail du compositeur

Pourquoi certaines propriétés coûtent bien moins cher que d’autres, comment le compositeur garde les animations fluides même quand le thread principal est occupé, will-change, animations liées au défilement et effets sur l’INP et le CLS.

Une balle jaune qui rebondit le long d’une trajectoire en pointillés, avec l’étiquette 60 fps et les propriétés transform et opacity

Les animations font désormais partie du langage de nombreux sites : menus coulissants, cartes qui se déploient, éléments qui apparaissent au défilement, boutons qui réagissent à la souris, compteurs, carrousels, effets de parallaxe. Bien faites, elles rendent l’interface plus claire et plus agréable. Mal faites, elles rendent la page lente, font saccader le défilement et pénalisent les Core Web Vitals, en particulier l’INP et, dans certains cas, le CLS.

La différence entre une animation fluide et une animation qui ralentit tout tient presque toujours à un détail technique : quelles propriétés sont animées, et par qui. Dans cet article, nous voyons ce que fait le navigateur à chaque image, pourquoi certaines propriétés coûtent bien moins cher que d’autres, comment utiliser correctement la couche de composition, comment gérer les animations liées au défilement et comment vérifier le résultat.

Seize millisecondes par image

La plupart des écrans se rafraîchissent soixante fois par seconde. Pour qu’une animation paraisse fluide, le navigateur doit produire une nouvelle image à chaque rafraîchissement, soit environ toutes les 16,7 millisecondes, et dans ce laps de temps il doit tout faire : exécuter d’éventuels scripts, recalculer les styles, mettre à jour la mise en page, repeindre et composer la page. Si une image prend plus de temps, elle est sautée, et l’animation saccade. Sur les écrans à fréquence plus élevée, comme 90 ou 120 rafraîchissements par seconde, le temps disponible est encore plus court.

Comme nous l’avons vu dans l’article sur le chemin critique de rendu, pour afficher une page le navigateur passe par plusieurs étapes : calcul des styles, layout, paint, composition. Une animation les répète à chaque image, mais pas toujours toutes : cela dépend de ce qui change.

Ce que le navigateur recalcule à chaque image selon la propriété animée : width, height, top et margin demandent styles, layout, paint et composition et coûtent cher ; background, color et box-shadow demandent styles, paint et composition ; transform et opacity demandent seulement styles et composition et coûtent peu, car elles déplacent une couche déjà prête
Animer les bonnes propriétés évite au navigateur de recalculer le layout et de repeindre à chaque image.

Propriétés coûteuses et propriétés bon marché

Les propriétés qui touchent à la géométrie des éléments, comme width, height, top, left, margin et padding, obligent le navigateur à recalculer la mise en page : la position et la taille de l’élément animé, mais souvent aussi celles de tous les éléments qui l’entourent. Il doit ensuite repeindre les parties qui ont changé et composer le résultat. C’est le chemin le plus coûteux, et sur les pages complexes il peut à lui seul dépasser le temps d’une image.

Les propriétés qui ne changent que l’apparence, comme les couleurs, les arrière-plans et les ombres, ne touchent pas à la mise en page mais demandent de repeindre les pixels de l’élément. Le coût est intermédiaire, et il croît avec la surface repeinte : une grande ombre animée sur un grand élément peut être étonnamment lourde.

Enfin, transform et opacity. Déplacer, redimensionner, faire pivoter ou estomper un élément avec ces propriétés ne change pas la mise en page et ne demande pas de le repeindre : si l’élément se trouve sur sa propre couche de composition, le navigateur prend l’image déjà peinte et la déplace, la met à l’échelle ou change sa transparence, souvent avec l’aide de la carte graphique. C’est pourquoi la règle la plus importante pour des animations fluides est de n’animer que transform et opacity, chaque fois que c’est possible.

/* À éviter : le panneau coulisse en changeant left, mise en page recalculée à chaque image */
.pannello { position: fixed; left: -320px; transition: left 0.3s; }
.pannello.aperto { left: 0; }

/* Mieux : le même effet avec transform, composition seulement */
.pannello { position: fixed; left: 0; transform: translateX( -100% ); transition: transform 0.3s; }
.pannello.aperto { transform: translateX( 0 ); }

Le compositeur et le thread principal

Il y a un deuxième avantage, moins connu mais encore plus important. Les animations CSS de transform et opacity peuvent être exécutées entièrement par le compositeur, un composant du navigateur qui travaille sur un thread distinct du thread principal. Cela signifie qu’elles restent fluides même quand le thread principal est occupé par du JavaScript : l’ouverture d’un menu, un indicateur de chargement qui tourne, une transition entre onglets ne saccadent pas, même si la page exécute pendant ce temps un script lourd.

Les animations de propriétés géométriques, en revanche, dépendent du thread principal : s’il est occupé, les images attendent. Et l’inverse est vrai aussi : une animation coûteuse occupe le thread principal à chaque image, ce qui laisse moins de temps pour répondre aux interactions de l’utilisateur et dégrade l’INP.

Comparaison illustrative du travail par image : une animation de top et height prend entre 14 et 34 millisecondes par image, au-delà de la limite de 16,7 millisecondes dans neuf cas sur douze, avec des images sautées ; une animation de transform prend 4-5 millisecondes par image, sans image sautée
Au-delà de la limite de 16,7 millisecondes, l’image n’arrive pas à temps et l’animation saccade.

will-change et les couches

Pour qu’un élément soit animé par le compositeur, il doit se trouver sur sa propre couche. Le navigateur crée les couches automatiquement quand une animation de transform ou d’opacity démarre, mais la création a un coût et peut produire un petit accroc à la première image. La propriété will-change permet de prévenir le navigateur à l’avance, pour qu’il prépare la couche avant d’en avoir besoin.

Il faut l’utiliser avec parcimonie. Chaque couche occupe de la mémoire, souvent de la mémoire graphique, et une page avec des centaines d’éléments sur des couches séparées peut devenir plus lente, pas plus rapide, surtout sur smartphone. will-change a du sens sur les éléments sur le point d’être animés, comme un menu qui va s’ouvrir, et doit être retiré à la fin de l’animation. L’appliquer de façon générique à tous les éléments, comme on le voit parfois dans les feuilles de style, est contre-productif.

Animations et CLS

Les animations sont aussi liées au CLS. Un élément qui change de position dans la mise en page, par exemple parce que sa hauteur ou sa marge est animée, déplace les éléments qui le suivent, et ces déplacements peuvent être comptés comme des décalages inattendus. Les animations faites avec transform, en revanche, ne changent pas la mise en page et ne produisent pas de décalages comptabilisés par le CLS, même quand l’élément bouge visiblement. C’est une raison de plus de les préférer : un bandeau qui descend du haut avec transform ne déplace pas le reste de la page, un bandeau qui grandit en hauteur, si.

Un cas fréquent est le déploiement d’un contenu, comme une FAQ qui s’ouvre ou une description qui s’allonge : la hauteur doit changer, et le contenu en dessous doit se déplacer. Si le déploiement se produit en réponse à un clic de l’utilisateur, les décalages qui suivent dans un court laps de temps ne sont pas considérés comme inattendus et ne comptent pas dans le CLS. Le problème se pose pour les animations qui démarrent toutes seules, au chargement ou après un certain temps.

Animations liées au défilement

Les effets qui se déclenchent pendant le défilement de la page, comme l’apparition d’éléments, les barres de progression de lecture, la parallaxe, comptent parmi les plus répandus et parmi les plus problématiques. On les a traditionnellement construits en écoutant l’événement scroll en JavaScript et en mettant à jour les styles à chaque événement : du travail sur le thread principal qui se répète des dizaines de fois par seconde pendant que l’utilisateur fait défiler, et qui rend le défilement lui-même moins fluide.

Il existe de meilleures alternatives. Pour faire apparaître des éléments quand ils entrent à l’écran, l’API Intersection Observer ne notifie JavaScript que lorsqu’un élément franchit un seuil, sans aucun calcul à chaque événement de défilement. Pour les effets continus, les navigateurs modernes prennent en charge les animations pilotées par le défilement (scroll-driven animations) directement en CSS, qui relient la progression d’une animation à la position de la page et, pour les propriétés adaptées, s’exécutent hors du thread principal.

/* Barre de progression de lecture, sans JavaScript */
.avanzamento {
	position: fixed;
	inset: 0 0 auto 0;
	height: 4px;
	background: #174ea6;
	transform-origin: 0 50%;
	animation: cresci linear;
	animation-timeline: scroll( root );
}

@keyframes cresci {
	from { transform: scaleX( 0 ); }
	to   { transform: scaleX( 1 ); }
}

Quand on utilise malgré tout un gestionnaire de défilement en JavaScript, mieux vaut le déclarer passif, pour que le navigateur n’ait pas à attendre son exécution avant de faire défiler, et limiter les mises à jour à une par image avec requestAnimationFrame.

Le layout forcé

Une erreur fréquente dans les animations écrites en JavaScript consiste à alterner lectures et écritures de propriétés géométriques : lire la hauteur d’un élément, en modifier un autre, lire la position d’un troisième, et ainsi de suite. Chaque lecture après une modification oblige le navigateur à recalculer immédiatement la mise en page pour renvoyer une valeur à jour. Dans une boucle sur de nombreux éléments, la mise en page est recalculée des dizaines de fois dans la même image. La solution consiste à regrouper d’abord toutes les lectures, puis toutes les écritures, ou à s’appuyer sur des bibliothèques d’animation qui le font automatiquement.

Mouvement réduit

De nombreux systèmes d’exploitation permettent à l’utilisateur de demander une réduction des mouvements, un réglage important pour les personnes souffrant de troubles vestibulaires. Le site le lit avec la media query prefers-reduced-motion et peut supprimer ou simplifier les animations non essentielles. C’est un choix d’accessibilité qui a aussi un effet sur la performance : moins d’animations, moins de travail pour l’appareil.

Transitions entre pages et entre états

Les View Transitions permettent d’animer le passage d’un état de la page à un autre, ou d’une page à une autre, avec quelques lignes de code : le navigateur capture une image de l’état précédent et de l’état suivant et les anime avec opacity et transform, c’est-à-dire avec les propriétés bon marché. C’est un moyen d’obtenir l’effet fluide des single-page applications sans leur complexité. Gardez toutefois à l’esprit que pendant la transition la page est en attente : des transitions longues allongent le temps perçu avant que le nouveau contenu soit utilisable. Des durées courtes, autour de 200-300 millisecondes, suffisent en général.

Bibliothèques, thèmes et page builders

Beaucoup d’animations ne sont pas écrites à la main mais viennent d’un thème, d’un page builder ou d’une bibliothèque. Il vaut la peine de vérifier ce qu’elles animent : certains effets d’entrée utilisent encore top ou margin, certains carrousels déplacent le contenu en changeant left, certains effets de parallaxe mettent à jour la position de l’arrière-plan à chaque événement de défilement. Les bibliothèques d’animation modernes utilisent transform par défaut, mais vous laissent libre d’animer n’importe quelle propriété. De plus, chaque bibliothèque a un poids, et en charger une entière pour faire apparaître trois blocs est un coût disproportionné, déjà évoqué à propos du coût des page builders : souvent, quelques lignes de CSS et un Intersection Observer suffisent.

Un exemple concret

Prenons un site institutionnel avec un menu latéral pour mobile, animé en changeant la propriété left, et une série de blocs qui apparaissent au défilement grâce à un gestionnaire d’événement scroll qui modifie margin-top et opacity. Sur les smartphones d’entrée de gamme, le défilement saccade visiblement et l’ouverture du menu est irrégulière, surtout pendant les premières secondes, quand le thread principal est encore occupé par les scripts. Réécrire le menu avec transform, remplacer le gestionnaire de défilement par un Intersection Observer qui ajoute une classe, et animer l’apparition avec transform et opacity élimine les images sautées, réduit le travail du thread principal pendant le défilement et retire du CLS les décalages causés par le changement de marge. Quelques heures de travail, avec des effets visibles à chaque visite.

Comment vérifier

Les DevTools de Chrome offrent plusieurs outils. Dans le panneau Rendering, on peut mettre en évidence les zones repeintes, qui clignotent à chaque paint, et les bordures des couches de composition : une animation qui fait clignoter de grandes zones de la page repeint trop. Le panneau Performance montre les images, en signalant celles qui sont sautées, et pour chacune le travail effectué : recalcul des styles, layout, paint, scripts. Enregistrer avec la limitation du processeur activée montre comment l’animation se comporte sur un appareil d’entrée de gamme.

Lighthouse signale les animations qui ne peuvent pas être exécutées par le compositeur, en nommant la propriété responsable. C’est un bon point de départ pour trouver les animations à réécrire, surtout dans les thèmes et les page builders qui proposent des effets prêts à l’emploi.

En bref

Une animation fluide doit produire une image toutes les 16,7 millisecondes. Les propriétés géométriques obligent le navigateur à recalculer le layout et à repeindre à chaque image, occupent le thread principal et peuvent générer des décalages comptabilisés par le CLS ; transform et opacity ne demandent que de la composition, peuvent être exécutées par le compositeur même quand le thread principal est occupé et ne déplacent pas la mise en page.

Les règles pratiques sont peu nombreuses : animer transform et opacity, n’utiliser will-change que là où c’est nécessaire et le temps nécessaire, remplacer les gestionnaires de défilement par Intersection Observer ou par les animations pilotées par le défilement, éviter d’alterner lectures et écritures de propriétés géométriques, respecter la préférence de mouvement réduit. Le résultat est une interface plus agréable et, en même temps, plus rapide à répondre.

L’ÉTAPE SUIVANTE

Commençons
par votre site.

Parlez-nous du projet, des problèmes que vous constatez et de vos objectifs. Ensemble, nous définirons une intervention sur mesure.

Demander une analyse

Vous mène au formulaire de la page Contact.

MANAGED SERVER S.R.L.info@corewebvitals.fr +39 02 5656 9681

Du lundi au vendredi · 9 h 30 – 19 h 30 (CET)

Vous préférez un formulaire en ligne ?
Contactez-nous sur managedserver.it ↗