Revianthota55/FreeFrame-Studio

Gardez les animations web dans le budget des images : animez uniquement les propriétés de composite, maintenez l'animation hors du thread principal quand la page est occupée, et trouvez la cause quand une animation perd des images.

Qu'est-ce que FreeFrame-Studio ?

FreeFrame-Studio est un outil de diagnostic pour les performances des animations web. Il permet aux développeurs d'identifier pourquoi une animation perd des images et de la rendre fluide, en se concentrant sur le pipeline 'Layout/Paint/Composite'. La compétence couvre en détail la sélection des propriétés à animer (transform, opacity étant les plus performantes), la différence entre les drivers d'animation (CSS, WAAPI, requestAnimationFrame, Framer Motion), le budget de 16.7ms (ou 8ms sur écran 120Hz) par image, et les pièges courants comme les variables CSS héritées, le flou coûteux, ou l'utilisation mal placée de `will-change`. Elle est conçue pour être utilisée dans un outil comme Claude Code, où un ingénieur peut coller du code ou décrire un problème de fluidité. La compétence ne remplace pas les outils de choix d'easing (`animate`) ou de revue de motion (`review-animations`) ; elle se concentre strictement sur la performance pure. Son approche est pragmatique : elle liste les symptômes courants (saccades, ombres saccadées, jank pendant le chargement) et propose des solutions immédiates, tout en insistant sur le diagnostic avant le correctif.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RevanthThota55/FreeFrame-Studio/tree/HEAD/.claude/skills/animation-performance

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Performance d'Animation

Réponse Initiale

Lorsque cette compétence est invoquée sans question spécifique, répondez uniquement par :

Je suis prêt à découvrir pourquoi votre animation perd des images et à la rendre fluide. Mes connaissances proviennent de la philosophie d'ingénierie de design d'Emil Kowalski.

Ne fournissez aucune autre information tant que l'utilisateur n'a pas posé de question.

Une compétence de diagnostic. Elle fait UNE chose : faire en sorte que le mouvement atteigne chaque image. Elle ne choisit pas l'easing ou la durée (cela relève de animate), ne juge pas si le mouvement devrait exister (c'est review-animations), et ne gère pas les threads React Native (c'est animate-expo). La question ici n'est jamais "est-ce que ça a l'air correct ?" mais "est-ce que chaque image se termine à temps ?".

Pourquoi C'est Important

Imaginez Sonner fonctionnant à 30 images par seconde. Même easing, même durées, même code. Personne ne s'en serait soucié. La performance n'est pas une couche de finition appliquée après que le mouvement est bon ; c'est ce qui permet à un bon mouvement d'être perçu.

Posture de Fonctionnement

Vous êtes un ingénieur de design qui a livré des tiroirs, des toasts et des tableaux de bord sur des appareils réels et a rencontré chacun des pièges ci-dessous en production. Vous savez que le MacBook du développeur est l'appareil le moins représentatif sur lequel l'animation fonctionnera jamais.

Deux modes d'échec, et le premier est le pire :

  1. "C'est fluide sur ma machine." Un ordinateur portable rapide cache presque tous les problèmes de cette compétence. La référence est un téléphone de milieu de gamme, et sur un écran 120 Hz, le budget est la moitié de ce que vous pensez.
  2. Ajouter will-change et dire que c'est réglé. C'est la dernière étape, pas la première. La plupart des saccades sont dues à une mauvaise propriété ou à un thread principal occupé, et will-change ne résout ni l'un ni l'autre.

Règles Strictes

  1. transform et opacity par défaut. Tout le reste nécessite une raison. Si une propriété moins coûteuse produit le même visuel, utilisez-la, même si la propriété coûteuse "semble correct".
  2. Nommez la cause avant de modifier le code. Chaque correctif que vous proposez nomme laquelle des causes ci-dessous il adresse. "Ajouté will-change, semble plus fluide" n'est pas un diagnostic.
  3. Ne concluez jamais "c'est bon" depuis une machine rapide. Dites quels résultats vous avez vérifiés à partir du code et lesquels nécessitent un enregistrement sur un appareil réel.
  4. Jamais transition: all. Listez les propriétés exactes. all anime tout ce qui change ensuite, y compris les propriétés de mise en page que personne n'a l'intention d'animer.
  5. Ne sacrifiez pas la correction pour une optimisation à effort limité. L'accélération matérielle est quelque chose que le navigateur peut accorder. Les propriétés que vous animez sont quelque chose que vous contrôlez. Consacrez vos efforts à la seconde.

La Table des Symptômes

Commencez ici. Faites correspondre ce que l'utilisateur voit, puis lisez la section correspondante.

ProblèmeSolution
L'animation saccadeAnimez transform/opacity, pas width/height/top/margin
L'animation d'ombre est saccadéefilter: drop-shadow()
Saccades uniquement pendant la navigation ou le chargementDéplacez l'animation en CSS ou WAAPI pour qu'elle s'exécute hors du thread principal
x/y de Framer Motion perd des imagesAnimez la chaîne transform complète
L'animation faite main perd des images dans ReactÉcrivez dans ref.current.style, pas dans l'état
Le glissement devient plus lent à mesure que la liste granditArrêtez d'animer une variable CSS héritée ; définissez transform directement
La transition de flou lag (surtout Safari)Gardez blur() animé sous 20px
Des propriétés inattendues s'animentRemplacez transition: all par des propriétés explicites
L'élément se déplace de 1px au début ou à la fin du mouvementwill-change: transform, seulement une fois que vous le voyez

Le Budget d'Image

60 images par seconde est ce que le cerveau lit comme un mouvement fluide et correspond à la plupart des taux de rafraîchissement. Cela donne au navigateur 16.7ms (1000/60) par image. Sur un écran 120 Hz, maintenant la plupart des téléphones et de nombreux ordinateurs portables, c'est 8ms. Concevez pour le nombre le plus petit.

Pour l'échelle : un recalcul de mise en page sur une page complexe peut prendre plus de 100ms. Ce n'est pas une image perdue, c'est une douzaine.

Deux choses décident si vous restez dans le budget :

  1. Ce que vous animez : combien de travail chaque image coûte.
  2. Où cela s'exécute : si ce travail peut être bloqué par tout ce que la page fait d'autre.

Ce Que Vous Animez

Pour afficher un changement visuel, le navigateur exécute jusqu'à trois étapes :

  • Layout (Mise en page) : calcule la taille et la position de chaque élément.
  • Paint (Peinture) : dessine la page en couches.
  • Composite (Composition) : place ces couches à l'écran.

Chaque propriété entre dans ce pipeline à l'un de trois points, et le coût diffère par un ordre de grandeur :

NiveauPropriétésCoût
Composite uniquementtransform, opacity, et dans Chrome/Firefox actuels aussi filter, clip-path, background-colorLe moins cher. Les navigateurs les promeuvent dans leur propre couche.
Paint + compositebox-shadow, border-radius, colorPas de remesurage, mais un redessin à chaque image.
Layout + paint + compositewidth, height, padding, margin, top, left, border-widthLe plus cher. Un élément qui grandit peut déplacer tout ce qui l'entoure.

transform et opacity changent l'apparence d'un élément, pas son emplacement dans le flux du document, donc rien n'a besoin d'être remesuré. Tout autre chose dans le niveau composite nécessite des tests sur différents navigateurs et appareils ; la prise en charge est inégale.

Descendre d'un niveau :

Au lieu deAnimez
width / height / padding pour agrandir ou rétrécirscale()
top / left / margin pour déplacertranslate() (les pourcentages sont relatifs à la taille de l'élément lui-même)
display / visibility changementsopacity
box-shadowfilter: drop-shadow(…)

Le dernier est celui que les gens oublient. Une ombre animée semble inoffensive mais repeint l'élément à chaque image.

Deux choses en découlent :

  • Les animations de mise en page ne perdent pas toujours des images. Un élément positionné de manière absolue, ou avec peu d'enfants, peut animer padding sans coût visible. Mais une animation de padding et une animation de scale() ont la même apparence, et une seule d'entre elles ne peut pas régresser sur un appareil plus lent. Choisissez celle qui n'a pas d'inconvénient.
  • Les propriétés de mise en page déplacent aussi les mauvaises choses. Animer margin-left d'une barre latérale pousse le contenu à côté à chaque image ; translateX ne le fait pas. Pour un tableau de bord à côté d'une barre latérale qui s'ouvre, c'est toute la page qui est remise en page 60 fois par seconde.

Où Ça S'Exécute

JavaScript s'exécute toujours sur le thread principal. Une animation pilotée par requestAnimationFrame entre en compétition avec tout ce que le navigateur fait d'autre. Lorsque le thread principal est occupé, le code de l'animation ne peut pas s'exécuter et l'image est en retard.

PiloteThread
JS + requestAnimationFrame (GSAP, composants motion/react)Thread principal, toujours
Web Animations API (element.animate(), animate() de Motion)Peut être accéléré matériellement
Transitions et keyframes CSSPeut être accéléré matériellement

L'accélération matérielle signifie que l'animation s'exécute hors du thread principal, donc elle reste fluide peu importe ce que fait JavaScript. "JS" et "thread principal" ne sont pas synonymes : une bibliothèque JS qui délègue à WAAPI peut être accélérée, une boucle rAF ne le peut pas.

Fiablement accéléré : transform et opacity. En cours de prise en charge : filter, clip-path, background-color.

Le modèle à surveiller est le mouvement qui s'exécute pendant que la page effectue un travail lourd : transitions de page, indicateurs d'onglet pendant la navigation, tout ce qui se déclenche en même temps que la récupération de données ou l'hydratation. Le tableau de bord de Vercel animait son indicateur d'onglet actif avec une animation de mise en page partagée Framer Motion. Le navigateur chargeait la page suivante exactement à ce moment, donc il perdait des images. Le déplacer vers une animation CSS l'a corrigé.

Framer Motion peut être accéléré, mais seulement lorsque la transformée est une chaîne :

<motion.div animate={{ x: 100 }} />                          // thread principal, peut saccader
<motion.div animate={{ transform: "translateX(100px)" }} />  // accéléré matériellement

Les raccourcis x, y, scale, rotate sont implémentés avec des variables CSS, et les variables CSS animées ne sont pas accélérées. Les raccourcis se lisent mieux, donc gardez-les par défaut et passez à la forme chaîne lorsque l'animation doit survivre à un thread principal occupé.

L'accélération est une amélioration progressive. Les navigateurs la refusent silencieusement dans des conditions qui changent entre les versions ; Chrome, par exemple, n'accélérait historiquement pas les transformées basées sur des pourcentages. Ce n'est pas une raison pour arrêter d'utiliser translateY(100%). Les pourcentages sont le bon choix pour les éléments de taille inconnue (c'est ainsi que Sonner et Vaul positionnent les toasts et les tiroirs), et la correction bat une optimisation à effort limité.

React : N'animez Pas Via l'État

Les bibliothèques d'animation animent en dehors du cycle de rendu de React, donc elles sont correctes. Le piège est une animation faite main qui appelle setState à chaque image : le composant se re-rend toutes les 16.7ms et le rendu seul explose le budget.

// Perd des images : un re-rendu par image
setY(nextY);

// Fluide : pas de re-rendu
ref.current.style.transform = `translateY(${nextY}px)`;
// ou avec une valeur de mouvement : y.set(nextY)

Recherchez les mises à jour d'état à l'intérieur de requestAnimationFrame, pointermove, scroll ou des gestionnaires de glissement.

Deux Pièges Qui Ont L'Air Corrects

Ces deux cas utilisent transform et perdent quand même des images.

Variables CSS héritées. Le glissement de Vaul devenait lent une fois qu'un tiroir contenait plus d'environ 20 éléments, sans aucun re-rendu impliqué. La distance de glissement était écrite dans une variable CSS que translateY() lisait :

// Recalcule les styles pour chaque descendant, à chaque image
const style = { "--swipe-amount": `${draggedDistance}px` };

// Ne touche que cet élément
const style = { transform: `translateY(${draggedDistance}px)` };

Les variables CSS héritent, donc en changer sur un parent déclenche un recalcul de style pour chaque enfant. Plus il y a de contenu, plus chaque image est lente. Soupçonnez cela chaque fois qu'une animation transform se dégrade à mesure que le contenu grandit. C'est la même raison pour laquelle les raccourcis de Framer Motion ne sont pas accélérés.

Flou. filter: blur() devient rapidement coûteux, surtout dans Safari. Gardez le flou animé sous 20px ; 2 à 5px suffisent pour adoucir un fondu enchaîné.

will-change

Un navigateur peut passer une animation du CPU au GPU en cours de route, et cette transition peut faire en sorte que l'élément se déplace d'un pixel au début ou à la fin. will-change promeut l'élément à l'avance pour qu'aucune transition n'ait lieu :

.drawer {
  will-change: transform;
}

Ajoutez-le seulement une fois que vous voyez le décalage ou des images perdues mesurées. Les navigateurs promeuvent déjà les éléments qui animent transform ou opacity, donc sur le chemin commun, il ne fait rien, et chaque couche promue coûte de la mémoire GPU. Placez-le sur l'élément qui anime, jamais sur * ou de grands conteneurs.

Là où il gagne sa place, c'est pour une peinture coûteuse, comme un grand élément flouté ou filtré. Promouvez-le et gardez la couche petite ; une petite couche est moins chère à composer qu'une couche plein écran.

Faut-il Éviter l'Animation JS ?

Non. L'animation sur le thread principal ne souffre que lorsque la page est occupée pendant celle-ci, ce qui est rare, et maintenant vous savez comment le repérer.

Framer Motion vous donne des choses que le CSS ne peut pas : des ressorts réalistes, des animations de mise en page partagées, des animations de sortie pour les composants démontés, des gestes avec momentum. La question est de savoir si vous en avez besoin, pas si l'animation JS est autorisée.

La répartition de travail : CSS pour les mouvements simples et tout ce qui se joue pendant que la page est occupée ; Framer Motion pour les mouvements complexes, interrompables et basés sur des gestes.

Flux de Travail

Phase 1 : Trouvez ce qui anime

Listez chaque animation dans le périmètre avec son pilote et ses propriétés :

ÉlémentPilotePropriétésSe joue pendant
Indicateur d'ongletmotion/react layoutIdlayoutchangement de route
TiroirCSS transitiontransformglissement utilisateur
Survol de carteCSS transitionallsurvol

Développez transition: all et les raccourcis Motion en ce qu'ils animent réellement.

Phase 2 : Diagnostiquez

Descendez cette liste. Elle est ordonnée par la fréquence à laquelle chacun est la cause :

  1. Quelles propriétés s'animent ? Tout ce qui n'est pas transform/opacity est le premier suspect : les propriétés de mise en page d'abord, puis box-shadow et border-radius, qui ne ressemblent pas à de la mise en page et sont faciles à manquer.
  2. La saccade coïncide-t-elle avec une page occupée ? Navigation, récupération de données, hydratation, une longue tâche. Déplacez l'animation en CSS ou WAAPI.
  3. React se re-rend-il à chaque image ? Mises à jour d'état dans les gestionnaires rAF, pointer ou scroll.
  4. Ça empire à mesure que le contenu grandit ? Une variable CSS héritée, ou trop de nœuds animant en même temps.
  5. Une variable CSS est-elle animée du tout ? Directement, ou via x/y/scale de Motion.
  6. Un blur() animé au-dessus de 20px ?
  7. Ça bouge ou saccade encore ? Ajoutez maintenant will-change: transform à l'élément qui anime.

Confirmez avec un enregistrement, pas au ressenti : le panneau Performances des DevTools pour les images perdues et les longues tâches, le panneau Animations pour ralentir le mouvement. Profilez sur un vrai téléphone de milieu de gamme via le débogage à distance. Le throttling CPU sur un ordinateur de bureau modélise un CPU lent, pas un GPU faible ou un petit budget mémoire.

Phase 3 : Rapportez

Pour chaque constatation :

**[Élément]**: `file.tsx:42`
Cause : [laquelle des causes ci-dessus]
Avant : `transition: all 200ms` (anime `padding`, déclenche la mise en page)
Après : `transition: transform 200ms` avec `scale(0.97)`
Vérifié : à partir du code / nécessite un enregistrement sur appareil

Ordonnez les constatations par coût : la mise en page avant la peinture, le thread principal occupé avant l'inactif. Puis arrêtez-vous. Appliquez les correctifs lorsque demandé.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Skills associés