12k
All articles

Déclencher des animations CSS au défilement, sans JavaScript

Créez des animations CSS déclenchées au défilement sans JavaScript. Découvrez la syntaxe, les actions, les seuils et le repli avec IntersectionObserver.

OpenReplay Team
OpenReplay Team
Déclencher des animations CSS au défilement, sans JavaScript

Les animations déclenchées par le défilement (scroll-triggered animations) laissent le défilement décider du moment où une animation CSS classique démarre. Dès qu’un élément atteint une position de défilement donnée, l’animation se déroule selon sa propre horloge. On les déclare avec les propriétés timeline-trigger et animation-trigger, sans avoir besoin d’IntersectionObserver.

Le code d’un « fondu à l’apparition au défilement » se ressemble presque partout : un observer, un seuil, l’ajout ou le retrait d’une classe, et un appel à unobserve() que quelqu’un oublie d’ajouter. Tout cela peut désormais se faire en CSS.

Cet article présente la syntaxe, un exemple complet d’apparition, les trois comportements de déclenchement, le choix des bornes de défilement et la manière de déployer la fonctionnalité sans risque alors qu’elle n’est disponible que sur Chromium.

Points clés à retenir

  • Une animation déclenchée par le défilement nécessite deux déclarations. timeline-trigger nomme un déclencheur et définit la plage de défilement qui l’active. animation-trigger indique à une animation quel déclencheur suivre et quoi faire lorsqu’il se déclenche.
  • La plage entry 100% exit 0% n’active le déclencheur que lorsque l’élément se trouve entièrement à l’intérieur du scrollport. La documentation de Chrome la considère comme équivalente au mot-clé contain.
  • play-forwards play-backwards joue l’animation à l’entrée et l’inverse à la sortie. play-once, issu du brouillon Animation Triggers, ne l’exécute qu’une seule fois.
  • Une animation déclenchée conserve sa propre durée et sa propre fonction d’accélération : faire défiler plus vite ne la rend pas plus rapide.
  • Les données de compatibilité de Chrome ne mentionnent que les navigateurs Chromium pour les animations déclenchées par le défilement. Masquez donc le contenu uniquement à l’intérieur de @supports et conservez une solution de repli basée sur IntersectionObserver.

Quelle est la différence entre les animations pilotées et les animations déclenchées par le défilement ?

Les animations pilotées par le défilement (scroll-driven animations) lient la progression d’une animation à la position de défilement. Les animations déclenchées par le défilement n’utilisent le défilement que pour décider du moment où une animation ordinaire démarre. Notre guide sur la manière de créer des animations au défilement uniquement en CSS traite du premier modèle : animation-timeline avec scroll() et view(). Dans ce cas, la timeline remplace l’horloge, si bien que revenir en arrière dans le défilement joue l’animation à l’envers. Le présent article porte sur timeline-trigger et animation-trigger, qui lancent une animation classique, avec sa propre durée, dès que l’élément atteint une plage de défilement choisie.

Pour une barre de progression de lecture ou un effet de parallaxe, utilisez les animations CSS de progression au défilement. Pour qu’une carte apparaisse en fondu en 400 ms une fois visible, utilisez un déclencheur. Dans son récapitulatif Web UI de la Google I/O 2026, Chrome présente ces deux fonctionnalités séparément et décrit les déclencheurs comme un moyen de remplacer IntersectionObserver par du CSS. Autrement dit, un déclencheur est la version CSS de « ajouter une classe lorsque cet élément devient visible ».

Exemple de fondu déclenché par le défilement

Un fondu déclenché par le défilement nécessite trois éléments : une règle @keyframes, une animation avec un mode de remplissage forwards, et un déclencheur qui relie l’animation à une view timeline. L’annonce des animations déclenchées par le défilement par Chrome définit la syntaxe. Ses points essentiels : le déclencheur et l’animation peuvent se trouver sur des éléments différents, et un déclencheur utilise comme source une timeline de progression de défilement (scroll) ou de visibilité (view).

<section class="reveal">
  <div class="reveal-content">
    <h2>Shipping notes</h2>
    <p>This block fades and slides in once it is fully on screen.</p>
  </div>
</section>
@keyframes reveal {
  to {
    opacity: 1;
    translate: 0 0;
  }
}

@supports (timeline-trigger-name: --t) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      timeline-trigger: --reveal view() entry 100% exit 0%;
      trigger-scope: --reveal;
    }

    .reveal-content {
      opacity: 0;
      translate: 0 2rem;
      animation: reveal 400ms ease-out forwards;
      animation-trigger: --reveal play-forwards;
    }
  }
}

Fonctionnement :

  • Le déclencheur est placé sur la section, le mouvement sur son enfant. L’élément qui glisse n’est pas celui dont on mesure la visibilité : le décalage translate ne peut donc pas déplacer la borne du déclencheur.
  • entry 100% exit 0% active le déclencheur dès que la section se trouve entièrement à l’intérieur du scrollport.
  • play-forwards lance l’animation à chaque activation du déclencheur. Le remplissage forwards maintient ensuite la dernière keyframe.
  • L’état initial masqué se trouve à l’intérieur de la requête de fonctionnalité. Les navigateurs qui ne prennent pas en charge la fonctionnalité n’appliquent jamais opacity: 0, et le contenu reste donc visible.
  • trigger-scope limite la portée du nom --reveal à chaque section. Par défaut, n’importe quel élément de la page peut voir un nom de déclencheur, de la même manière qu’avec anchor-name, comme l’indiquent les notes de version de Chrome 146.

Avec play-forwards seul, quitter la plage n’a aucun effet : l’élément conserve son état final lorsque vous faites défiler la page au-delà. Lorsqu’il redevient entièrement visible, le déclencheur s’active de nouveau et l’animation est rejouée. La section suivante montre comment modifier ce comportement.

Choisir l’action du déclencheur

animation-trigger accepte un nom de déclencheur suivi d’au plus deux actions. La première s’exécute lorsque le déclencheur s’active, la seconde lorsqu’il se désactive. L’action choisie détermine ce qui se passe à la nouvelle entrée et à la sortie.

Comportement souhaitéValeur de animation-triggerRemarque
Rejouer à chaque nouvelle entrée--reveal play-forwardsPeut produire un retour visible et brusque à l’état initial
Inverser à la sortie--reveal play-forwards play-backwardsAprès l’inversion, l’élément affiche ses styles de base
Exécuter une seule fois, puis rester en l’état--reveal play-onceMot-clé issu d’un brouillon, susceptible d’évoluer
animation-trigger: --reveal play-forwards play-backwards; /* reverses on exit */
animation-trigger: --reveal play-once; /* runs once; Animation Triggers draft keyword */

Le remplissage forwards maintient la keyframe to une fois play-forwards terminé. Il ne maintient rien une fois play-backwards terminé : l’animation cesse alors de s’appliquer et l’élément revient à ses styles de base. Dans cet exemple, les styles de base correspondent à l’état initial masqué (opacity: 0; translate: 0 2rem), puisque les keyframes ne définissent que to ; l’élément finit donc tout de même masqué. Si vos keyframes définissent un état from différent des styles de base, utilisez both afin que cet état soit maintenu après l’inversion. play-once provient de l’Editor’s Draft CSS Animation Triggers Level 1, et non de l’annonce de Chrome : vérifiez-le dans la version de Chrome que vous ciblez avant de vous y fier. C’est l’équivalent le plus proche d’un appel à unobserve() après la première intersection.

Plage d’activation et plage active

Dans un déclencheur de défilement, la plage d’activation détermine le moment où le déclencheur se déclenche. La plage active, facultative et écrite après une barre oblique, détermine combien de temps il reste actif ; Chrome exige qu’elle englobe la plage d’activation. Selon Chrome, entry 100% exit 0% équivaut à contain et entry 0% exit 100% à cover. Ces deux lignes sont donc identiques :

timeline-trigger: --reveal view() entry 100% exit 0% / entry 0% exit 100%;
timeline-trigger: --reveal view() contain / cover;

Avec ce réglage, le déclencheur s’active dès que l’élément est entièrement visible et reste actif jusqu’à ce qu’aucune partie de l’élément ne soit plus visible. C’est important pour play-backwards, car l’animation inverse attend alors que l’élément soit sorti de l’écran.

Choisir des bornes de défilement pertinentes

La bonne plage de déclenchement dépend de la taille de l’élément et de toute interface fixe recouvrant le viewport.

SituationPlagePourquoi
Petite carte ou titreentry 100% exit 0% (contain)Se déclenche lorsque l’élément entier est visible
Section hauteentry 25% exit 0%contain peut se déclencher tardivement pour les éléments hauts
Sous un en-tête sticky de 4remview(block 4rem 0)Empêche l’en-tête de masquer l’élément
/* Tall element: fire when a quarter has entered */
timeline-trigger: --reveal view() entry 25% exit 0%;

/* 4rem sticky header: start (top) inset first */
timeline-trigger: --reveal view(block 4rem 0) entry 100% exit 0%;

Dans view(), le retrait de début (start inset) précède le retrait de fin (end inset). Sur un axe de bloc vertical, le début correspond au haut : la hauteur d’un en-tête placé en haut doit donc figurer en premier.

Compatibilité des navigateurs et solutions de repli

Le récapitulatif Web UI de la Google I/O 2026 de Chrome indique que les animations déclenchées par le défilement sont prises en charge par Chrome et Edge 146, mais ni par Firefox ni par Safari. L’annonce de Chrome de décembre 2025 prévoyait l’arrivée de la fonctionnalité dans Chrome 145, mais elle a finalement été livrée dans Chrome 146 (version stable du 10 mars 2026). L’article sur la bêta de Chrome 146 mentionne trigger-scope dans la même version. Traitez l’ensemble de la fonctionnalité comme une amélioration progressive.

Le bloc @supports de l’exemple s’en charge. Les navigateurs non compatibles ignorent tout le bloc et affichent le contenu de manière statique. Pour obtenir le même mouvement dans les autres navigateurs, conservez votre bascule de classe existante basée sur IntersectionObserver en dehors de la requête de fonctionnalité, et ne l’exécutez que lorsque CSS.supports() renvoie false. Notre article sur le remplacement des bibliothèques d’animation par des API web natives explique comment construire cette solution de repli sans dépendances de type AOS.

La requête prefers-reduced-motion: no-preference garantit que les utilisateurs qui demandent moins d’animations voient immédiatement le contenu. Pour échelonner plusieurs éléments, faites pointer leur animation-trigger vers un même déclencheur partagé, avec des délais différents.

Conclusion

Les animations déclenchées par le défilement couvrent le motif du fondu à l’apparition au défilement : le CSS décide du moment où une animation ordinaire démarre, et le navigateur se charge de l’observation. Partez de l’exemple ci-dessus. Choisissez l’action (rejouer, inverser ou exécuter une seule fois) adaptée à votre design, ajustez la plage pour les éléments hauts ou les en-têtes sticky, et supprimez le code de l’observer pour les navigateurs compatibles tout en le conservant pour les autres.

FAQ

Quelle est la différence entre les animations déclenchées par le défilement et les container queries scroll-state ?

Les container queries scroll-state appliquent des styles tant qu'un conteneur se trouve dans un état de défilement géré par le navigateur. Les animations déclenchées par le défilement lancent une animation à keyframes lorsqu'un élément franchit une plage sur une view timeline ou une scroll timeline. Chrome 133 a introduit les états stuck, snapped et scrollable, et Chrome 144 a ajouté scrolled, qui suit la direction de défilement la plus récente. Utilisez une requête scroll-state pour un en-tête sticky qui change de style une fois fixé, et un déclencheur pour les effets d'apparition.

Les animations déclenchées par le défilement sont-elles plus rapides qu'une apparition basée sur IntersectionObserver ?

Selon les notes de version de Chrome, comme l'interaction est décrite en CSS, le navigateur peut confier le travail à un worker thread. Une apparition basée sur IntersectionObserver exécute son callback et sa bascule de classe sur le thread principal, si bien qu'un travail de script intensif peut la retarder. Chrome présente ce déchargement comme un avantage de conception, et non comme un résultat mesuré : profilez donc vos propres pages avant et après la migration plutôt que de présumer un gain.

Les déclencheurs de défilement peuvent-ils contrôler des animations créées avec la Web Animations API ?

Oui. Les notes de version de Chrome 146 indiquent que la fonctionnalité s'accompagne d'interfaces JavaScript, de sorte que les déclencheurs peuvent piloter des animations créées avec la Web Animations API, et pas uniquement des animations CSS. Les noms de ces interfaces sont définis dans l'Editor's Draft CSS Animation Triggers Level 1, qui peut encore évoluer : vérifiez le brouillon et la version de Chrome que vous ciblez avant de vous y fier.

Pourquoi mon élément reste-t-il masqué après l'ajout d'un déclencheur de défilement ?

L'état initial masqué s'applique dès que la requête de fonctionnalité est satisfaite : l'élément reste donc à une opacité de 0 jusqu'à l'activation de son déclencheur. Les causes courantes sont une différence d'orthographe entre le nom défini dans timeline-trigger et celui utilisé dans animation-trigger, un élément animé situé en dehors du sous-arbre défini par trigger-scope, ou une plage que l'élément n'atteint jamais. Vérifiez d'abord le nom, puis assurez-vous que l'élément animé se trouve bien à l'intérieur de l'élément de portée.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.