Vers une métrique de fluidité de l'animation

Découvrez comment mesurer les animations, comment envisager les frames d'animation et la fluidité globale de la page.

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

Vous avez probablement rencontré des pages qui "stutterient" ou "se figent" pendant le défilement ou les animations. Nous tenons à souligner que l'expérience n'est pas fluide. Pour résoudre ce type de problèmes, l'équipe Chrome s'efforce d'étendre la prise en charge de nos outils d'atelier pour la détection des animations et d'améliorer régulièrement les diagnostics du pipeline de rendu dans Chromium.

Nous souhaitons vous faire part des progrès récents, proposer des conseils concrets sur les outils et discuter d'idées pour les futures métriques de fluidité des animations. Comme toujours, n'hésitez pas à nous faire part de vos commentaires.

Cet article aborde trois thèmes principaux:

  • Un rapide aperçu des animations et des frames d'animation.
  • Nos réflexions actuelles sur la mesure de la fluidité globale des animations.
  • Voici quelques suggestions pratiques que vous pourrez exploiter dans les outils de l'atelier.

Que sont les animations ?

Les animations donnent vie au contenu ! En faisant bouger le contenu, en particulier en réponse aux interactions des utilisateurs, les animations peuvent rendre l'expérience plus naturelle, compréhensible et amusante.

Toutefois, des animations mal implémentées ou l'ajout d'un trop grand nombre d'animations peuvent dégrader l'expérience et la rendre décisive du tout. Nous avons probablement tous interagi avec une interface qui vient d'ajouter trop d'effets de transition "utiles", qui deviennent en fait hostiles à l'expérience en cas de mauvaises performances. Certains utilisateurs pourraient donc préférer les mouvements réduits, une préférence utilisateur que vous devez respecter.

Comment fonctionnent les animations ?

Pour récapituler rapidement, le pipeline de rendu est composé de quelques étapes séquentielles:

  1. Style:calculez les styles qui s'appliquent aux éléments.
  2. Layout (Mise en page) : générez la géométrie et la position de chaque élément.
  3. Paint:remplissez les pixels de chaque élément en couches.
  4. Composite:dessinez les calques à l'écran.

Bien qu'il existe de nombreuses façons de définir des animations, elles fonctionnent toutes fondamentalement via l'un des éléments suivants:

  • Ajuster les propriétés de la mise en page.
  • Ajustement des propriétés de paint.
  • Ajuster les propriétés composites.

Ces étapes étant séquentielles, il est important de définir les animations en fonction de propriétés qui se situent plus bas dans le pipeline. Plus la mise à jour est effectuée tôt dans le processus, plus les coûts sont élevés et plus elle est moins susceptible d'être fluide. Pour en savoir plus, consultez la section Performances d'affichage.

Bien qu'il puisse être pratique d'animer les propriétés de mise en page, cela peut engendrer des coûts, même si ces coûts ne sont pas immédiatement apparents. Dans la mesure du possible, les animations doivent être définies en termes de modifications des propriétés composites.

Pour bénéficier d'animations fluides et efficaces, il est essentiel de définir des animations CSS déclaratives ou d'utiliser des animations Web et d'animer les propriétés composites. Toutefois, cela à lui seul ne garantit pas la fluidité, car même des animations Web efficaces ont des limites de performances. C'est pourquoi il est toujours important de mesurer les performances.

Que sont les frames d'animation ?

Les modifications apportées à la représentation visuelle d'une page mettent du temps à s'afficher. Un changement visuel entraînera l'affichage d'une nouvelle image d'animation, qui sera finalement affichée sur l'écran de l'utilisateur.

Les affichages sont mis à jour à intervalles réguliers. Les mises à jour visuelles sont donc regroupées. De nombreux écrans sont mis à jour à intervalles fixes, par exemple 60 fois par seconde (60 Hz). Certains écrans plus modernes peuvent offrir des fréquences d'actualisation plus élevées (de 90 à 120 Hz sont de plus en plus courants). Souvent, ces écrans peuvent s'adapter activement entre les fréquences d'actualisation si nécessaire, ou même proposer des fréquences d'images entièrement variables.

L'objectif d'une application, telle qu'un jeu ou un navigateur, est de traiter toutes ces mises à jour visuelles par lot et de produire à chaque fois une image d'animation visuellement complète dans le délai imparti. Notez que cet objectif est entièrement différent des autres tâches importantes du navigateur, telles que le chargement rapide de contenu à partir du réseau ou l'exécution efficace des tâches JavaScript.

À un moment donné, il peut devenir trop difficile d'effectuer toutes les mises à jour visuelles dans le délai imparti par l'écran. Dans ce cas, le navigateur supprime un frame. Votre écran ne devient pas noir, il se répète automatiquement. La même mise à jour visuelle s'affiche pendant un peu plus longtemps : la même image d'animation que celle présentée lors de l'opportunité de frame précédente.

En fait, cela arrive souvent ! Il n'est pas nécessairement même perceptible, en particulier pour le contenu statique ou de type document, ce qui est courant sur la plate-forme Web en particulier. Les frames supprimés ne sont visibles que lorsqu'il existe des modifications visuelles importantes, telles que des animations, pour lesquelles nous avons besoin d'un flux constant de mises à jour d'animation pour afficher un mouvement fluide.

Qu'est-ce qui a une incidence sur les frames d'animation ?

Les développeurs Web peuvent avoir un impact considérable sur la capacité d'un navigateur à afficher et à présenter des mises à jour visuelles de manière rapide et efficace.

Voici quelques exemples :

  • Utilisation de contenu trop volumineux ou gourmand en ressources pour un décodage rapide sur l'appareil cible
  • Utilisation d'un trop grand nombre de couches nécessitant trop de mémoire GPU.
  • Définir des styles CSS ou des animations Web trop complexes
  • Utiliser des anti-modèles de conception qui désactivent les optimisations de rendu rapide
  • Trop de tâches JS sur le thread principal, ce qui entraîne de longues tâches qui bloquent les mises à jour visuelles.

Mais comment savoir quand une image d'animation a dépassé son délai et a causé une perte de frame ?

Une méthode possible consiste à utiliser l'interrogation requestAnimationFrame(), mais elle présente plusieurs inconvénients. requestAnimationFrame(), ou "rAF", indique au navigateur que vous souhaitez exécuter une animation et lui demande de le faire avant l'étape suivante du processus de rendu du pipeline de rendu. Si votre fonction de rappel n'est pas appelée au moment attendu, cela signifie qu'un rendu n'a pas été exécuté et qu'un ou plusieurs frames ont été ignorés. En interrogeant et en comptant la fréquence d'appel de la fonction rAF, vous pouvez calculer une sorte de métrique "frames par seconde" (FPS).

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

L'utilisation du sondage requestAnimationFrame() n'est pas une bonne idée pour plusieurs raisons:

  • Chaque script doit configurer sa propre boucle d'interrogation.
  • Elle peut bloquer le chemin critique.
  • Même si l'interrogation rAF est rapide, elle peut empêcher requestIdleCallback() de planifier de longs blocs inactifs lorsqu'ils sont utilisés en continu (blocs qui dépassent une seule image).
  • De même, l'absence de longs blocs inactifs empêche le navigateur de planifier d'autres tâches de longue durée (telles que la récupération de mémoire plus longue et d'autres tâches en arrière-plan ou spéculatives).
  • Si l'interrogation est activée et désactivée, vous passerez à côté de cas où le budget de frames a été dépassé.
  • L'interrogation signale des faux positifs lorsque le navigateur utilise une fréquence de mise à jour variable (en raison de l'état de la puissance ou de la visibilité, par exemple).
  • Et surtout, il ne capture pas tous les types de mises à jour d'animation.

Une trop grande quantité de travail sur le thread principal peut affecter la capacité à voir les frames de l'animation. Consultez l'exemple d'à-coups pour voir comment une animation générée par un rAF, lorsqu'il y a trop de travail sur le thread principal (comme la mise en page), entraînera la perte de frames, moins de rappels rAF et un FPS inférieur.

Lorsque le thread principal est bloqué, les mises à jour visuelles commencent à stuttering. C'est un à-coup !

De nombreux outils de mesure se sont principalement concentrés sur la capacité du thread principal à produire en temps opportun et à assurer le bon fonctionnement des images d'animation. Mais ce n'est pas tout ! Prenons l'exemple suivant :

La vidéo ci-dessus montre une page qui injecte régulièrement des tâches longues dans le thread principal. Ces longues tâches nuisent complètement à la capacité de la page à fournir certains types de mises à jour visuelles. Vous pouvez voir en haut à gauche une baisse correspondante de requestAnimationFrame() FPS rapporté à 0.

Malgré ces longues tâches, le défilement de la page reste fluide. En effet, dans les navigateurs récents, le défilement est souvent organisé en fils de discussion, et entièrement géré par le compositeur.

Il s'agit d'un exemple qui contient simultanément de nombreux frames supprimés sur le thread principal, tout en présentant de nombreux frames de défilement correctement transmis sur le thread compositeur. Une fois la longue tâche terminée, la mise à jour principale de la peinture du thread n'a aucun changement visuel à offrir. L'interrogation rAF suggère une perte d'image à 0, mais visuellement, un utilisateur ne pourrait pas remarquer de différence.

Pour les frames d'animation, l'histoire n'est pas aussi simple.

Images d'animation: mises à jour importantes

L'exemple ci-dessus montre que l'histoire ne se limite pas à requestAnimationFrame().

Dans quel cas les mises à jour et les frames d'animation sont-ils importants ? Voici quelques-uns des critères qui nous intéressent et sur lesquels nous aimerions connaître votre avis:

  • Mises à jour des threads principaux et du compositeur
  • Mises à jour de peinture manquantes
  • Détecter des animations
  • Qualité ou quantité

Mises à jour des threads principaux et du compositeur

Les mises à jour des frames d'animation ne sont pas booléennes. Il n'est pas possible que les images soient entièrement abandonnées ou présentées entièrement. De nombreuses raisons peuvent expliquer pourquoi une image d'animation peut être partiellement présentée. En d'autres termes, il peut simultanément comporter du contenu non actualisé tout en présentant de nouvelles mises à jour visuelles.

L'exemple le plus courant est lorsque le navigateur n'est pas en mesure de produire une nouvelle mise à jour du thread principal dans le délai imparti, mais qu'il dispose d'une nouvelle mise à jour du thread compositeur (comme l'exemple précédent de défilement avec fils de discussion).

Il est recommandé d'utiliser des animations déclaratives pour animer les propriétés composites, car cela permet de diriger une animation entièrement par le thread compositeur, même lorsque le thread principal est occupé. Ces types d'animations peuvent continuer à produire des mises à jour visuelles efficacement et en parallèle.

D'autre part, il peut arriver qu'une mise à jour du thread principal devienne enfin disponible pour la présentation, mais seulement après avoir manqué plusieurs délais de rendu. Ici, le navigateur comportera une nouvelle mise à jour, mais ce n'est peut-être pas la toute dernière.

De manière générale, nous considérons les frames qui contiennent de nouvelles modifications visuelles comme des frames partiels. Les frames partiels sont assez courants. Idéalement, les mises à jour partielles incluent au moins les mises à jour visuelles les plus importantes, comme les animations, mais cela ne peut se produire que si les animations sont pilotées par le thread compositeur.

Mises à jour de peinture manquantes

Un autre type de mise à jour partielle se produit lorsque des contenus multimédias tels que des images n'ont pas fini de décoder et de rastériser à temps pour la présentation des images.

Ou, même si une page est parfaitement statique, les navigateurs peuvent toujours prendre du retard en ce qui concerne l'affichage des mises à jour visuelles lors d'un défilement rapide. En effet, les rendus de pixels du contenu au-delà de la fenêtre d'affichage visible peuvent être supprimés pour économiser de la mémoire GPU. L'affichage des pixels prend du temps, et peut prendre plus de temps qu'une seule image pour afficher tout ce qui suit un grand défilement, par exemple un balayage d'un doigt. Communément appelé damier.

Chaque fois qu'un rendu d'image est possible, il est possible de suivre la proportion des dernières mises à jour visuelles affichées à l'écran. Mesurer la capacité à le faire sur de nombreux frames (ou temps) est connu sous le nom de débit de trame.

Si le GPU est vraiment encombré, le navigateur (ou la plate-forme) peut même commencer à limiter la fréquence à laquelle il tente de tenter des mises à jour visuelles et diminuer ainsi les fréquences d'images effectives. Bien que cela puisse techniquement réduire le nombre de mises à jour de frames supprimées, visuellement, le débit de frames sera toujours inférieur.

Pourtant, tous les types de faible débit de trames ne sont pas tous mauvais. Si la page est principalement inactive et qu'il n'y a pas d'animations actives, une fréquence d'images faible est tout aussi attrayante visuellement qu'une fréquence d'images élevée (et peut économiser la batterie).

Dans ce cas, quand le débit des trames est-il important ?

Détecter des animations

Un débit de frames élevé est important, en particulier pendant les périodes comportant des animations importantes. Les différents types d'animations dépendent des mises à jour visuelles d'un thread spécifique (principal, compositeur ou nœud de calcul). Par conséquent, la mise à jour visuelle dépend du thread qui fournit sa mise à jour dans le délai imparti. Nous partons du principe qu'un thread donné affecte la fluidité chaque fois qu'une animation active dépend de sa mise à jour.

Certains types d'animations sont plus faciles à définir et à détecter que d'autres. Les animations déclaratives, ou animations basées sur une entrée utilisateur, sont plus claires à définir que les animations basées sur JavaScript implémentées en tant que mises à jour périodiques des propriétés de style animables.

Même avec requestAnimationFrame(), vous ne pouvez pas toujours supposer que chaque appel rAF produit nécessairement une mise à jour visuelle ou une animation. Par exemple, l'utilisation de l'interrogation rAF uniquement pour suivre la fréquence d'images (comme indiqué ci-dessus) ne devrait pas affecter les mesures de fluidité, car il n'y a pas de mise à jour visuelle.

Qualité ou quantité

Enfin, la détection des animations et des mises à jour des frames d'animation ne représente qu'une partie de l'histoire, car elle ne capture que la quantité de mises à jour de l'animation, et non leur qualité.

Par exemple, vous pouvez voir une fréquence d'images stable de 60 FPS lorsque vous regardez une vidéo. Techniquement, cette opération est parfaitement fluide, mais la vidéo elle-même peut avoir un faible débit ou des problèmes de mise en mémoire tampon sur le réseau. Elle n'est pas capturée directement par les métriques de fluidité de l'animation, mais elle peut être troublante pour l'utilisateur.

Un jeu qui utilise <canvas> (peut-être même en utilisant des techniques telles que le canevas hors écran pour garantir une fréquence d'images stable) peut techniquement être parfaitement fluide en termes d'images d'animation, tout en ne parvenant pas à charger des éléments de jeu de haute qualité dans la scène ou à ne pas présenter d'artefacts de rendu.

Et bien sûr, un site peut simplement comporter de très mauvaises animations 😃.

GIF &quot;Old school&quot; en construction

Je veux dire, je suppose qu'ils étaient plutôt cool pour leur temps !

États d'une seule image d'animation

Étant donné que les frames peuvent être partiellement présentés ou que leur perte peut se produire d'une manière qui n'affecte pas la fluidité, nous avons commencé à considérer chaque image comme ayant un score d'exhaustivité ou de fluidité.

Voici le spectre des façons dont nous interprétons l'état d'une seule image d'animation, du meilleur au pire:

Aucune mise à jour souhaitée Durée d'inactivité, répétition de l'image précédente.
Présentation complète La mise à jour du thread principal a été validée dans le délai imparti, ou aucune mise à jour du thread principal n'était souhaitée.
Partiellement présentée Compositeur uniquement. La mise à jour du thread principal retardée n'a présenté aucune modification visuelle.
Partiellement présentée Compositeur uniquement : le thread principal a eu une mise à jour visuelle, mais cette mise à jour n'incluait pas d'animation qui affecte la fluidité.
Partiellement présentée Compositeur uniquement. Le thread principal présente une mise à jour visuelle qui affecte la fluidité, mais un frame précédemment obsolète est arrivé et a été utilisé à la place.
Partiellement présentée Compositeur uniquement. Sans la mise à jour principale souhaitée, et la mise à jour du compositeur comporte une animation qui affecte la fluidité.
Partiellement présentée Compositeur uniquement, mais la mise à jour du compositeur ne comporte pas d'animation qui affecte la fluidité.
Frame supprimé Aucune mise à jour. Aucune mise à jour du compositeur n'était souhaitée, et la version principale a été retardée.
Frame supprimé Une mise à jour du compositeur était souhaitée, mais elle a été retardée.
Frame non actualisé Une mise à jour était souhaitée. Elle a été produite par le moteur de rendu, mais le GPU ne l'a toujours pas présentée avant la date limite pour vsync.

Il est possible de transformer ces états en une sorte de score. Pour interpréter ce score, vous pouvez considérer qu'il s'agit d'une probabilité d'être observable par l'utilisateur. Une seule perte de frames peut ne pas être très observable, mais une séquence de nombreuses pertes de frames affectant la fluidité d'une ligne l'est vraiment.

Synthèse: une métrique "Pourcentage d'images supprimées"

Bien qu'il soit parfois nécessaire d'étudier en détail l'état de chaque image d'animation, il est également utile d'attribuer un score rapide "en un coup d'œil" à une expérience.

Étant donné que les frames peuvent être partiellement présentés et que même les mises à jour de frames complètement ignorées peuvent en fait affecter la fluidité, nous voulons nous concentrer moins sur le simple comptage des frames, et plus sur l'étendue à laquelle le navigateur n'est pas en mesure de fournir des mises à jour visuellement complètes au moment opportun.

Le modèle mental doit passer de:

  1. images par seconde :
  2. détecter les mises à jour d'animation manquantes et importantes,
  3. Pourcentage de baisse sur une période donnée.

L'important est la proportion de temps à attendre des mises à jour importantes. Nous pensons que cela correspond à la façon dont les utilisateurs bénéficient de la fluidité du contenu Web dans la pratique. Jusqu'à présent, nous avons utilisé les éléments suivants comme ensemble initial de métriques:

  • Pourcentage moyen de suppression:pour toutes les images d'animation non inactives sur l'ensemble de la timeline
  • Pirer cas de pourcentage de frames supprimés:mesuré sur des fenêtres glissantes d'une seconde.
  • 95e centile du pourcentage de frames supprimés:mesuré sur des fenêtres glissantes d'une seconde.

Actuellement, vous pouvez trouver ces scores dans certains outils pour les développeurs Chrome. Bien que ces métriques se concentrent uniquement sur le débit global des trames, nous évaluons également d'autres facteurs, tels que la latence des frames.

Essayez par vous-même dans les outils pour les développeurs !

HUD des performances

Chromium dispose d'un HUD de performances soigné, caché derrière un indicateur (chrome://flags/#show-performance-metrics-hud). Vous y trouverez des scores en direct pour des éléments tels que les métriques Core Web Vitals, ainsi que quelques définitions expérimentales de fluidité des animations basées sur le pourcentage d'images supprimées au fil du temps.

HUD des performances

Statistiques de rendu d'image

Activez les statistiques de rendu des frames dans les outils de développement via les paramètres de rendu pour afficher une vue en direct des nouveaux frames d'animation, qui sont codés par couleur pour différencier les mises à jour partielles des mises à jour entièrement supprimées. La valeur FPS indiquée ne concerne que les images entièrement présentées.

Statistiques de rendu des images

Lecteur de frames dans les enregistrements de profils de performances des outils de développement

Le panneau "Performances des outils de développement" dispose depuis longtemps d'une visionneuse de frames. Cependant, il n'était pas synchronisé avec le fonctionnement réel du pipeline de rendu moderne. De nombreuses améliorations ont été récemment apportées, même dans la dernière version de Chrome Canary, ce qui devrait considérablement faciliter le débogage des problèmes d'animation.

Vous constaterez que les images de la visionneuse sont mieux conformes aux limites de vsync et sont codées par couleur en fonction de leur état. Il n'est toujours pas possible de visualiser toutes les nuances décrites ci-dessus, mais nous prévoyons d'en ajouter davantage prochainement.

Visionneuse de frames dans les outils pour les développeurs Chrome

Traçage dans Chrome

Enfin, avec Chrome Tracing, l'outil de prédilection pour explorer les détails, vous pouvez enregistrer une trace"Rendu du contenu Web" via la nouvelle interface utilisateur de Perfetto (ou about:tracing) et explorer le pipeline graphique de Chrome. Cette tâche peut être intimidante, mais nous avons récemment ajouté quelques éléments à Chromium pour vous faciliter la tâche. Vous pouvez obtenir un aperçu de ce qui est disponible dans le document Life of a Frame (Vie d'un frame).

Grâce aux événements de trace, vous pouvez déterminer de manière définitive:

  • Animations en cours d'exécution (à l'aide d'événements nommés TrackerValidation)
  • Obtenir la timeline exacte des images de l'animation (à l'aide d'événements nommés PipelineReporter)
  • Pour les mises à jour d'animation qui fonctionnent mal, déterminez exactement ce qui empêche votre animation de s'exécuter plus rapidement (en utilisant les répartitions des événements dans les événements PipelineReporter).
  • Pour les animations basées sur des entrées, découvrez le temps nécessaire pour obtenir une mise à jour visuelle (à l'aide d'événements nommés EventLatency).

Rapporteur du pipeline de traçage Chrome

Étapes suivantes

L'initiative Signaux Web vise à fournir des métriques et des conseils pour créer des expériences utilisateur de qualité sur le Web. Les métriques basées sur les ateliers telles que le temps de blocage total sont essentielles pour détecter et diagnostiquer les problèmes d'interactivité potentiels. Nous prévoyons de concevoir une métrique similaire basée sur des fonctionnalités expérimentales pour assurer la fluidité des animations.

Nous vous tiendrons informé au fur et à mesure que nous développerons des idées pour concevoir une métrique complète basée sur des données de frame d'animation individuelles.

À l'avenir, nous aimerions également concevoir des API permettant de mesurer efficacement la fluidité des animations pour des utilisateurs réels du domaine ainsi que dans l'atelier. Des informations supplémentaires seront bientôt disponibles.

Commentaires

Nous sommes fiers des dernières améliorations et outils pour les développeurs fournis dans Chrome afin de mesurer la fluidité de l'animation. Essayez ces outils, analysez vos animations et dites-nous où cela vous mène.

Vous pouvez envoyer vos commentaires au groupe Google web-vitals-feedback en indiquant "[Smoothness Metrics]" dans la ligne d'objet. Nous avons hâte de savoir ce que vous en pensez !