Principes de base de l’animation Web – Comment rendre vos pages fluides ?

Et si je vous disais que les pages web sont des animations interactives reproduites par votre navigateur web ?

Nous observons différents mouvements chaque fois que nous sommes sur une page web.

Et je ne parle pas seulement des animations JavaScript ou CSS. Le défilement, le zoom par pincement, la sélection de texte et même le survol d’un bouton sont techniquement des animations et fonctionnent de la même manière.

En fait, il s’agit d’images séquentielles affichées rapidement pour nous donner une perception de mouvement ou simplement refléter un changement.

Chaque fois que le code JavaScript modifie la page, une zone de l’image précédente est invalidée et le navigateur en dessine une nouvelle.

Ces changements peuvent être aussi simples que l’ajout ou la suppression d’une ou de modifier le style d’un bouton.

Nous appelons ces images des cadres.

Selon les directives du W3C en matière de synchronisation des images, le navigateur Web doit être capable d’afficher soixante images par seconde (60 fps).

Bien entendu, une image reste à l’écran si elle ne change pas.

Et si je vous montrais quelques exemples ?

Lorsque vous faites défiler une page, le navigateur affiche des zones hors écran du document au fur et à mesure que vous faites défiler la page vers le bas (ou vers le haut).

L’image ci-dessous montre les trames séquentielles produites et affichées pendant quelques secondes de défilement.

animation-scroll-2
Cadres générés pendant quelques secondes de défilement

Et comme vous pouvez le constater, chaque image a été affichée pendant 16,7 ms (60 fps).

J’ai utilisé Google Chrome DevTools pour créer l’enregistrement ci-dessus. Vous pouvez le reproduire si vous le souhaitez. Dans DevTools, allez dans le panneau Performance, et cliquez sur le bouton d’enregistrement. Ensuite, faites défiler la page pendant quelques secondes, et arrêtez l’enregistrement.

Vous verrez un aperçu comme celui ci-dessus.

Même lorsque vous sélectionnez un morceau de texte, de nouvelles trames s’affichent à mesure que vous sélectionnez d’autres lettres et lignes.

Dans l’enregistrement ci-dessous, je déplace la souris sur le cadre temporel pour rejouer la sélection de texte :

text-select

Pourquoi ai-je besoin de savoir cela ? vous vous demandez peut-être.

Lorsqu’une page ne répond pas rapidement aux interactions de l’utilisateur ou présente des mouvements saccadés, quelque chose doit clocher.

C’est généralement dû au fait que le fil d’exécution principal du navigateur est tellement occupé qu’il ne peut pas délivrer les images à temps (voir ci-dessous).

Dans ce guide, je vais vous expliquer comment les navigateurs transforment le code en pixels et comment nous pouvons travailler avec eux pour offrir une expérience utilisateur agréable.

Je me concentrerai sur Google Chrome pour cet article. Toutefois, les concepts de haut niveau sont les mêmes pour tous les navigateurs.

Il y a beaucoup de théories à couvrir ici, et j’espère que cela ne vous dérange pas.

Michael Jordan a dit : « Gardez les principes de base, et le niveau de tout ce que vous faites s’élèvera. »

Croyez-moi, connaître ces théories ne sera pas sans récompense !

Vous aurez une nouvelle perspective sur la façon dont les pages Web changent. Et on finira par passer à beaucoup d’actions.

Fréquence de rafraîchissement ou fréquence d’images ?

Un dispositif d’affichage moyen rafraîchit l’écran soixante fois par seconde (60 Hz).

Pour l’œil humain, toute fréquence supérieure à 12 Hz est perçue comme un mouvement. Cet article de Paul Bakaus l’explique très bien.

Animhorse
Cheval animé (12 dessins par seconde) par Janke, sous licence CC BY-SA 2.5

Il existe des écrans avec des fréquences de rafraîchissement plus élevées comme 120Hz ou 144Hz, mais 60Hz est la norme pour la plupart des dispositifs d’affichage.

Le taux de rafraîchissement est différent du taux d’images, cependant.

Le taux de rafraîchissement est le nombre de fois qu’un dispositif d’affichage rafraîchit une image en une seconde. La fréquence d’images est un nombre arbitraire d’images (dans un système cinématographique), capturées ou dessinées en une seconde.

Par exemple, la fréquence standard pour l’enregistrement de films est de 24 images par seconde, même si ce n’est pas la fréquence de rafraîchissement maximale d’un téléviseur moderne.

Dans ce cas, les dispositifs d’affichage utilisent un algorithme pour répéter des images spécifiques afin de rendre la fréquence d’images compatible avec leur fréquence de rafraîchissement. Cela signifie que vous pouvez regarder un film à 24 fps sur un téléviseur 144Hz à la fréquence originale de 24 fps.

Pourquoi la fréquence d’images est-elle importante pour les pages Web, me direz-vous ?

Un utilisateur qui joue à des jeux à 120 fps remarquerait un défilement lent des pages sur le même ordinateur.

Il n’appréciera pas non plus les animations Web à une vitesse inférieure à 60 images par seconde.

Êtes-vous déjà tombé sur ces sites Web qui regorgent de publicités et de GIFs ? En général, je quitte rapidement ces pages car je sais que trouver un autre site me ferait gagner du temps !

Il y a une date limite pour produire chaque image

Il faut du temps au navigateur pour dessiner une nouvelle image.

L’affichage de soixante images par seconde signifie que chaque image doit être prête à l’écran en 16,7 ms (1 sec ÷ 60).

Sinon, la trame est retardée ou abandonnée. Ce problème est souvent appelé  » jank » sur une page Web.

plane-1
Une animation avec des chutes et des retards d’images

Notre priorité absolue est donc claire : nous devons faire en sorte que nos pages soient exemptes de jank 👆.

Mais d’abord, nous devons savoir comment tout fonctionne.

Comment un cadre est produit

Le navigateur Web génère un nouveau cadre parce que quelque chose a changé sur la page. Et il doit refléter ce changement.

Une page Web change lorsque :

L’utilisateur interagit avec la page. Il fait défiler, pince le zoom, clique, sélectionne un élément de texte, etc.

Un morceau de code JavaScript modifie la page. Par exemple, il ajoute un


ou modifie un style CSS.

Chaque modification déclenche une séquence de tâches, qui aboutit à une image unique.

Cette séquence de tâches est connue sous le nom de pixel pipeline, rendering waterfall ou rendering pipeline.

Et voici à quoi elle ressemble d’un point de vue de haut niveau :

    • JavaScript Evaluate

– le navigateur : oh, quelque chose a changé ! Je dois générer une nouvelle image.

    • Style Calculate – le navigateur : maintenant je dois appliquer la classe some-class à cet

élément).

    • Layout (reflow) –

le navigateur : Je vois que certains éléments ont maintenant de nouveaux styles. Je dois calculer l’espace qu’ils prennent sur l’écran et où ils doivent être positionnés en fonction de ces styles. Je dois également calculer la géométrie de tous les autres éléments affectés par ce changement !

    • Peinture

– le navigateur : Maintenant, je dois regrouper les éléments (qui ont une sortie) en plusieurs couches et convertir chaque couche en une représentation bitmap dans la mémoire ou la RAM vidéo.

  • Compositing –

le navigateur : Maintenant, je dois combiner ces bitmaps dans l’ordre défini pour former l’image finale.

Les mêmes étapes sont également suivies lorsque la page web est rendue pour la première fois.

pipeline-1
Le pipeline de pixels

Chaque activité du pipeline déclenche l’activité suivante. Par exemple, la mise en page déclenche la peinture, et cela continue jusqu’à la dernière étape.

Nous devons être attentifs à chaque activité du pipeline, car chacune d’entre elles peut contribuer à une baisse des performances.

Voir aussi :  Comment créer un séparateur de section à l'aide de CSS

Apprenons à les connaître un peu mieux.

Évaluer JavaScript – lorsque le code JavaScript s’exécute

Vous modifiez généralement la page à partir de votre code JavaScript.

Beaucoup d’entre nous suppriment un élément comme ceci :

let myBox = document.querySelector('.my-box')

if (myBox) {
 myBox.remove()
}

Ou bien on peut la cacher de cette façon

let myBox = document.querySelector('.my-box')

if (myBox) {
  myBox.style.display = "none
}

Ou ajoutez un sélecteur CSS à sa liste de classes :

let myBox = document.querySelector('.my-box')

if (myBox) {
  myBox.classList.add('my-special-box')
}

Ces modifications invalident une partie du document et obligent le navigateur à produire un nouveau cadre.

Style – quels styles CSS vont avec quel élément

Ensuite, le navigateur web associe les nouveaux styles aux éléments respectifs sur la base des sélecteurs correspondants.

Par exemple, si vous ajoutez la classe my-special-box à la liste des classes d’un élément :

let myBox = document.querySelector('.my-box')

if (myBox) {
  myBox.classList.add('my-special-box')
}

Cette étape est celle où les styles respectifs sont calculés et appliqués à votre élément.

De plus, comme vous le savez probablement, les éléments et les styles HTML sont convertis en arbres DOM et CSSOM, respectivement.

Le navigateur utilise ces structures de données en interne. Mais il les expose également à JavaScript via les API du navigateur. C’est ainsi que nous avons manipulé le document dans les exemples précédents – nous avons utilisé l’API DOM.

Le navigateur Web combine DOM et CSSOM pour créer un arbre de tous les éléments visibles dans la balise avec leurs styles CSS calculés.

Cet arbre est appelé arbre de rendu, arbre de rendu ou arbre de cadre.

Les pseudo-éléments CSS, qui ont un contenu, seront également dans l’arbre de rendu.

L’objectif est maintenant de transformer l’arbre de rendu en une image.

Layout – pour recalculer la géométrie des éléments après une modification

La géométrie d’un élément HTML peut affecter ses frères et sœurs et ses enfants.

Lorsque votre code ajoute (ou supprime) un élément ou modifie son style, le navigateur recalcule la nouvelle dimension et la position de cet élément.

Il calcule également la dimension et la position de chaque élément frère ou enfant qu’il peut affecter.

Par exemple, si vous augmentez la marge supérieure d’un paragraphe avec JavaScript, tous les éléments suivants du document seront poussés vers le bas.

Ou si la largeur d’un conteneur diminue, la taille de ses enfants peut également diminuer.

Cela dit, une simple modification de la géométrie d’un élément peut obliger le navigateur à recalculer la géométrie de centaines d’autres éléments affectés (directement ou indirectement) par cette modification.

Le navigateur utilise l’arbre de rendu pour recalculer la géométrie de chaque élément visible dans la fenêtre d’affichage.

Ce processus est également connu sous le nom de reflux.

Peinture – Quand le code est converti en pixels

À ce stade, le navigateur Web dispose de toutes les structures de données dont il a besoin. Les styles sont calculés et la mise en page est prête.

En fonction du moteur de rendu (Blink, Gecko, etc.), d’autres abstractions et structures de données auxiliaires sont créées en interne. Mais comme les éléments internes des navigateurs ont tendance à changer assez fréquemment, nous garderons notre discussion à un niveau aussi élevé que possible.

L’étape suivante consiste à transformer le code en pixels. Ce processus s’appelle la peinture.

À cette étape, le moteur de rendu du navigateur crée une liste d’affichage des commandes de dessin pour chaque élément de l’arbre de rendu.

Ces commandes ressemblent aux commandes de dessin de base : dessiner un rectangle, dessiner un cercle ou dessiner un morceau de texte à ces coordonnées.

Google Chrome utilise Skia pour effectuer le travail de dessin. Skia est une bibliothèque graphique 2D qui fournit des API standard sur diverses plateformes.

Chrome enregistre ces commandes dans un objet Skia SkPicture. SkPicture dispose d’une méthode de lecture, qui envoie les commandes de dessin une par une vers le canevas spécifié.

Au final, la sortie des listes d’affichage sera un ensemble de bitmaps.

Pour nous assurer que nous sommes tous sur la même longueur d’onde, définissons rapidement ce qu’est un bitmap.

Vous savez peut-être qu’un pixel (élément d’image) est le plus petit élément d’une image numérique. Chaque image est une grille de pixels (a*b), et chaque pixel a une couleur spécifique. Ces pixels forment ensemble l’image.

Maintenant, qu’est-ce qu’un bitmap ?

Un bitmap (dans un contexte graphique) est une méthode permettant de stocker les informations relatives à la couleur de chaque pixel sous la forme d’un ensemble de bits.

Twitter-post---59
Le visage souriant (remixé), sous licence CC0 1.0

Dans l’image ci-dessus, trois pixels sont mis en évidence avec leurs informations de couleur (un mélange de rouge, vert et bleu).

Ces valeurs forment ensemble le bitmap de l’image.

D’autre part, un bitmap est la façon dont les ordinateurs stockent les images dans la mémoire ou dans un dispositif de stockage.

La transformation du contenu d’une page web en bitmap est connue sous le nom de peinture ou de rastérisation.

Rien n’est encore peint, cependant. Cette étape est davantage une préparation de la peinture (ou prépeinture) que la peinture proprement dite.

Les éléments sont peints sur plusieurs couches

Le travail de peinture proprement dit est effectué plus tard, à la discrétion du compositeur. Mais le moteur de rendu fournit suffisamment d’indications au compositeur sur la façon dont les éléments doivent être peints sur plusieurs couches.

Certains éléments sont regroupés en une seule couche et rastérisés ensemble (ils partagent le même bitmap). Cependant, certains éléments sont peints sur une couche dédiée.

Par exemple, dans l’animation ci-dessous, les éléments sont peints sur quatre couches :

Vous pouvez voir ces couches dans le panneau Layers.

Pour activer le panneau Calques, dans Chrome DevTools, maintenez la touche ⌘+⇧+P (ou Ctrl+⇧ Shift+P) pour activer la palette de commande. Ensuite, tapez « Afficher les couches » et exécutez-la.

Ces calques (également appelés calques composites) permettent de réaliser des composites.

Ces calques composites sont ensuite combinés dans l’ordre défini et forment l’image finale (nous y reviendrons plus loin).

Les couches composites sont similaires aux couches des éditeurs de graphiques matriciels tels que Photoshop. En gérant les formes comme des calques, le concepteur peut transformer une forme sans affecter les autres formes.

Si vous vouliez modifier quelque chose sur une image aplatie, vous devriez peut-être redessiner l’ensemble.

À l’instar de Photoshop, le fait de peindre des éléments sur des couches distinctes permet au navigateur Web de réduire considérablement les travaux de peinture.

Ainsi, si un élément d’un calque est invalidé (il a été modifié), seules les zones invalidées (carreaux) du calque concerné doivent être repeintes.

Le moteur de rendu tient compte de divers facteurs pour prendre les décisions relatives aux couches. Par exemple, si l’opacité CSS d’un élément change au moment de l’exécution, il sera tramé sur une couche dédiée.

Vous pouvez également promouvoir un élément pour qu’il soit peint sur une couche dédiée avec les propriétés CSS will-change ou translateZ(0).

Cependant, vous devez toujours promouvoir un calque pour une raison précise.

La présence de nombreux calques entraîne des coûts en termes de mémoire et de temps de traitement. Cela peut devenir problématique sur les appareils à capacité limitée.

Voir aussi :  CSS REM - Qu'est-ce que le REM en CSS ?

Compositing : lorsque l’image finale est générée

Le compositeur reçoit une liste d’affichage du moteur de rendu avec des structures de données auxiliaires.

Son travail (entre autres) consiste à organiser le dessin des éléments sous forme de couches multiples.

En fonction du contenu de la page (et de ses styles), la peinture peut être réalisée par un logiciel (tramage logiciel) ou directement sur le GPU (tramage matériel).

Voici comment cela fonctionne sur Google Chrome (pour les autres navigateurs, vous devriez consulter leurs docs de conception) :

Dans le cas de la rastérisation logicielle, les commandes graphiques sont exécutées par un ensemble de threads de travail de rastérisation, puis les bitmaps générés sont partagés avec le GPU en tant que textures.

Cependant, si la rastérisation matérielle entre en jeu, Skia génère les bitmaps directement sur le GPU en envoyant des commandes de bas niveau à l’API graphique du système d’exploitation.

Une fois que les couches sont prêtes, le compositeur peut appliquer des transformations au niveau du compositeur (comme la transformation et l’opacité) sur chaque couche.

Et enfin, il combine (compose) les couches en une seule. Si l’accélération matérielle est activée, la composition sera également effectuée sur le GPU, en envoyant des commandes de bas niveau à l’API graphique du système d’exploitation.

N’oubliez pas cette partie car elle joue un rôle important dans l’optimisation des performances de l’animation.

Screenshot-2022-02-16-at-18.01.03-1
Couches après avoir été composées

Chaque fois que je pense aux couches composites, cela me rappelle l’ancienne production d’animation sur cel, où chaque image était dessinée sur une feuille de celluloïd transparente.

cel-animation-frame
Mon cel d’animation GunBuster, Licence CC BY-NC 2.0

L’arrière-plan était un dessin statique, que l’animateur déplaçait vers la gauche d’un pouce (avec un rouleau) et sur lequel il plaçait l’image cel suivante.

Cette technique a permis de réduire considérablement le travail de dessin et a aidé les studios d’animation à répartir le travail de conception entre plusieurs équipes.

Vous pouvez regarder cette vidéo de la production d’animation de Blanche-Neige par Disney si vous êtes curieux de connaître cette ancienne méthode de production.

Le compositing dans les navigateurs a un objectif similaire : minimiser le travail de peinture lorsque quelque chose change.

Il s’agit de la dernière étape du pipeline – où une nouvelle image voit le jour.

Comment optimiser les activités du pipeline

Une question demeure cependant. Comment puis-je éviter les mouvements saccadés de la page et cesser d’ennuyer mes utilisateurs ?

Voici quelques mesures à prendre.

Connaître les modifications les plus coûteuses

Tous les changements n’impliquent pas toutes les activités du pipeline de pixels. Certains changements nécessitent moins de travail et peuvent sauter une étape ou deux.

Toute modification de la géométrie d’un élément (lorsque vous changez la hauteur, la largeur, la gauche, le haut, le bas, la droite, le padding, la marge, etc.) implique l’ensemble du pipeline.

Ce type de modification est le plus coûteux des changements que vous pouvez apporter à une page Web.

Parfois, il est nécessaire, mais parfois il est totalement évitable (je vais vous dire comment).

pipeline-full-1
Toutes les étapes du pixel pipeline

Optimiser la peinture

Si vous modifiez la propriété background-color d’une div, le navigateur n’aura pas à recalculer sa géométrie, car vous n’avez modifié que la couleur.

Cela signifie que le navigateur Web saute l’étape de la mise en page cette fois et passe à la peinture.

La peinture reste une tâche coûteuse. Cependant, vous pouvez l’optimiser en réduisant la complexité de la peinture – en choisissant des styles plus simples plutôt que des styles compliqués.

Par exemple, les ombres de texte ou les dégradés sont plus coûteux qu’une simple couleur de fond.

Demandez-vous toujours si vous pouvez choisir un ensemble de styles moins coûteux. Parfois, ils ne font aucune différence en termes d’esthétique.

pipeline-paint
Pipeline de pixels sans l’étape de mise en page

Utilisez des transformations composites uniquement

Certaines modifications ne nécessiteront pas de layout et de paint parce que le compositeur peut les appliquer tout seul.

pipeline-composite
Le pipeline de pixels sans Layout et Paint

Vous trouverez ci-dessous la liste des modifications que le navigateur peut effectuer à moindre coût au moment de la composition :

  • Re-positionnement avec transform : translate(mpx, npx)
  • Rotation avec transform:rotate(xdeg)
  • Mise à l’échelle avec transform : scale(x)
  • Opacité avec opacity(x)

Ces propriétés CSS semblent être tout ce dont vous avez besoin lorsque vous apportez un changement à une page (enfin, la plupart) !

Mieux encore, si l’accélération matérielle est maintenue, le compositeur peut utiliser la puissance de calcul du GPU pour appliquer ces transformations. Les GPU sont créés pour ce type de charge de travail.

Ainsi, en fonction de la modification que nous apportons au DOM, le processus sera l’un de ces trois scénarios.

  • JavaScript → Style → Mise en page → Peinture → Composite
  • JavaScript → Style → Peinture → Composite
  • JavaScript → Style → Composite

« La performance est l’art d’éviter le travail. »

Et bien sûr, le dernier scénario est la voie la moins chère à choisir.

Essayez de réduire la charge de travail du thread principal

Un navigateur Web est essentiellement un programme informatique, et en tant que tel, il aura un ou plusieurs processus en mémoire pendant son exécution.

La plupart des navigateurs ont une architecture multiprocessus, dans laquelle les activités sont réparties sur plusieurs threads de différents processus (comme le processus Renderer et le processus GPU, le processus Browser, etc.)

Google-chrome-processes
Le processus Renderer et GPU dans Google Chrome

Dans le cas de Chrome, le JavaScript, le style, la mise en page et la configuration de la peinture se déroulent dans le fil d’exécution principal du processus de rendu (chaque onglet a un processus de rendu dédié).

C’est presque tout !

Le contenu HTML que votre navigateur récupère initialement via une requête HTTP est analysé sur un thread dédié, mais le rendu et tout contenu que vous ajoutez sont analysés sur le thread principal.

Cela dit, l’objectif est d’alléger la charge du thread principal. Et en retour, cela nous aide à avoir un taux de rafraîchissement constant.

Le site Web des déclencheurs CSS peut vous aider à comprendre comment la modification d’une propriété CSS déclenche la mise en page, la peinture et le compositing.

Vous pouvez également utiliser l’aide-mémoire que j’ai créé :

Twitter-post---55
Les propriétés CSS et leur étape initiale dans le pipeline des pixels

Assurez-vous que vos callbacks JavaScript prennent le train en marche !

Bon, maintenant nous savons comment aider le navigateur à faire moins d’étapes (quand c’est possible !), mais il y a autre chose à prendre en compte.

Qu’il s’agisse d’une animation ou d’une modification ponctuelle, nous devons nous assurer que nos modifications sont synchronisées avec la fréquence d’images à laquelle le navigateur affiche le contenu.

Qu’est-ce que cela signifie ? Vous vous demandez peut-être.

Imaginez un train en mouvement avec de nombreux wagons.

Ce train avance rapidement, et vous avez 16,7 ms pour dessiner un dessin et le jeter dans chaque wagon (pendant qu’il avance).

Si vous ne parvenez pas à charger un wagon en 16,7 ms, le train s’arrête brièvement jusqu’à ce que vous lanciez le dessin.

via GIPHY

Ce train en marche peut être n’importe quel mouvement sur la page Web. Il peut s’agir d’une animation, d’une transition, d’un défilement de page, d’une sélection de texte ou de tout autre mouvement.

Voir aussi :  Comment créer un nouveau contexte d'empilement avec la propriété Isolation en CSS

Si le train doit s’arrêter pour vous, il délivrera les images avec un retard. Les utilisateurs le remarqueront, et ils n’aimeront pas ça !

Chaque fois que vous voulez modifier la page, vous devez, d’une manière ou d’une autre, faire glisser votre travail dans un créneau de 16,7 ms sans le ralentir.

Mais c’est parfois délicat à faire.

De nombreux développeurs utilisent encore setInterval() pour réaliser une boucle temporisée. Par exemple, pour répéter une action ou créer une animation.

Il y a cependant un problème avec setInterval(). Elle n’est pas assez précise pour exécuter votre code à la fréquence exacte que vous avez définie.

Si vous définissez l’intervalle pour répéter votre code toutes les 16,7 ms, votre code pourrait s’exécuter à n’importe quel moment au cours de chaque intervalle de 16,7 ms.

Ainsi, si nous disposons de 16,7 ms pour effectuer une modification, générer la trame et la charger sur son wagon dédié, nous devons nous assurer que notre code s’exécute au début de chaque intervalle de 16,7 ms.

Sinon, il faudra plus de 16,7 ms pour le compléter, et il ne sera pas prêt pour le slot actuel.

Et s’il existait un moyen d’exécuter le callback au début de chaque tranche de temps de 16,7 ms ?

RequestAnimationFrame() a été conçu à cet effet.

Elle s’assure que vos callbacks sont exécutés au début de la prochaine image.

requestanimationframe
requestAnimationFrame() v.s. setInterval()

De cette façon, votre code a plus de chances de se terminer dans le délai de 10 ms afin de laisser suffisamment de temps au navigateur Web pour effectuer ses tâches internes dans la durée totale de 16,7 ms.

Ainsi, au lieu de :

setInterval(
	() => {
    	// faire un changement
    },
    16.7
)

Vous pouvez le faire :

const animateSomething = function () {
	// faire un changement
    
    // Appel suivant
    requestAnimationFrame(animateSomething)
}

// Premier appel manuel pour démarrer l'animation
requestAnimationFrame(animerquelquechose)

Un autre avantage de l’utilisation de requestAnimationFrame est que le navigateur peut exécuter votre animation plus efficacement.

Par exemple, si l’utilisateur passe à un autre onglet, le navigateur mettra l’animation en pause. Cela réduit le temps de traitement et l’autonomie de la batterie.

Comment optimiser une animation – voir en action

Comme promis, il est temps de faire quelques expériences.

Pour cette expérience, j’ai créé une animation de deux manières différentes.

L’animation porte sur un avion qui survole l’horizon au coucher du soleil.

Dans la première approche, j’ai utilisé toutes les propriétés de déclenchement de la mise en page (gauche et haut) sans me soucier des compromis en matière de performances.

J’ai également utilisé setInterval avec une fréquence de 16,7 ms pour ma boucle temporisée.

Dans la deuxième approche, j’ai remanié le code et utilisé des styles uniquement pour le compositeur. J’ai également promu mon élément mobile (l’avion) avec la propriété will-change pour m’assurer qu’il aura sa propre couche.

J’ai également remplacé setInterval par requestAnimationFrame pour une meilleure synchronisation.

Pour simuler le mouvement de l’avion, j’ai utilisé la méthode Math.sine() avec quelques ajustements. Le chemin de déplacement est également dessiné avec un graphique sinusoïdal basé sur SVG.

Voici le lien CodePen vers la première approche :

Et la seconde approche avec la promotion des couches (will-change : transform), les styles du compositeur uniquement (transform : translate()), et requestAnimationFrame:

Comparons les deux approches

L’une des mesures que vous pouvez utiliser est le taux de rafraîchissement. Elle vous aide à contrôler la cohérence des images au cours d’un mouvement.

Regardez l’enregistrement ci-dessous :

animation-paint

Vous pouvez voir le compteur FPS dans l’image ci-dessus (en haut à gauche de la capture d’écran). Même si la capture d’écran indique 90 images par seconde, les barres jaunes/rouges indiquent que certaines images ont été manquées ou retardées.

Le journal des événements (en bas à droite) montre toutes les étapes de l’enregistrement : Recalculer le style > Mise en page > Peinture > Couches composites.

Pour activer le FPS-mètre, lorsque vous êtes dans Chrome DevTools, maintenez la touche ⌘+⇧+P (ou Ctrl+⇧ Shift+P) pour activer la palette de commandes. Ensuite, tapez Compteur FPS et choisissez Afficher le compteur d’images par seconde (FPS).

Et voici un guide rapide pour le lire :

FPS-meter
Compteur FPS

Maintenant, mesurons la deuxième approche :

animation-composite

Dans le deuxième enregistrement, le nombre moyen d’images par seconde est de 118,8, sans aucune image manquée ou perdue.

Le journal des événements confirme également qu’aucune mise en page et peinture n’a été nécessaire, et que le compositeur a tout fait (Recalculer le style → Couche composite).

Vous pouvez également utiliser l’outil Paint Flashing de Chrome pour voir quelles parties de la page sont repeintes. Cela est utile pour détecter les travaux de peinture non désirés lors des interactions avec l’utilisateur.

Dans l’exemple de l’avion, la zone en cours de repeinte (l’avion en mouvement) s’affiche sous forme de rectangles bordés de vert.

L’activation du clignotement de la peinture pour la deuxième approche ne montrera rien car il n’y a pas de peinture pendant l’animation.

Trop long, vous n’avez pas lu ?

Pour avoir des mouvements fluides sur votre page, il suffit de s’en assurer :

  • Les cadres sont livrés à temps
  • Les cadres sont toujours

livrés à temps

Et voici une liste de contrôle pour y parvenir :

    • Assurez-vous que vos modifications JavaScript se produisent au début de chaque image en utilisant requestAnimationFrame

.

    • Pour modifier la dimension d’un élément, utilisez transform:scale() sur la hauteur et la largeur

.

    • Pour déplacer les éléments, utilisez toujours transform:translate() sur les coordonnées (haut, droite, bas et gauche

).

  • Réduisez la complexité de la peinture en utilisant des styles CSS simples plutôt que des styles coûteux. Par exemple, si possible, utilisez des couleurs unies plutôt que des dégradés ou des ombres.
  • Normalisez l’utilisation des transitions sur les versions mobiles. Même si la capacité de calcul des téléphones mobiles est limitée, l’UX des versions mobiles contient souvent plus de transitions/effets en raison de leur petit écran.
  • Utilisez les outils de développement de votre navigateur pour diagnostiquer les performances des animations. Utilisez des outils tels que le flashage de la peinture et le compteur FPS pour affiner vos animations.
  • Utilisez le panneau Performance de DevTool pour voir comment votre code s’exécute sur les appareils bas de gamme.

Vous pouvez appliquer ces micro-optimisations lors de tout type de modification. Qu’il s’agisse d’une animation JavaScript ou CSS, ou d’un changement ponctuel avec JavaScript.

C’était la ligne d’ouverture de ce guide :

Et si je vous disais que les pages Web étaient des animations interactives reproduites par votre navigateur Web.

Et si je vous disais maintenant que ce n’était que la partie émergée de l’iceberg !

Ne vous inquiétez pas, vous pouvez déjà faire beaucoup pour que vos pages Web soient agréables à regarder.

Si vous voulez pousser plus loin vos connaissances en matière de performances, je tiens à jour une page dédiée pour rassembler des ressources sur les performances web provenant de divers créateurs. Consultez-la !

Si vous avez des questions ou des commentaires, ou s’il y a quelque chose que j’ai oublié (ou que j’ai mal compris), n’hésitez pas à m’écrire à @lavary_ sur Twitter.

Merci de votre lecture !

Attributions :