Vous avez sûrement déjà vécu cette sensation sans forcément mettre un mot dessus. Vous faites défiler une page, et soudain, un bloc apparaît avec une animation fluide. Le contenu semble vivant, plus agréable à lire, presque interactif. Sans que vous vous en rendiez compte, votre attention est captée. C’est exactement le rôle des animations à l’apparition à l’écran, aussi appelées animations au scroll ou Scroll-triggered animations grâce à IntersectionObserver en Javascript couplé à du CSS.
- Comprendre comment déclencher une animation au bon moment, lorsque un élément devient visible à l’écran, sans bricolage ni calculs complexes, pour créer des pages plus fluides et professionnelles.
- Choisir la bonne approche entre CSS moderne et IntersectionObserver en JavaScript, en connaissant les limites de chaque solution avec les navigateurs.
- Maîtriser une méthode fiable et performante pour mettre en place des animations au scroll propres et adaptées aux projets réels.
Ces animations donnent du rythme à une page, guident le regard et améliorent l’expérience utilisateur, à condition d’être bien utilisées. D’être un bon développeur Frontend …
Dans ce guide, nous allons partir de zéro. Pas de prérequis compliqué. L’objectif est simple : comprendre comment déclencher une animation lorsqu’un élément devient visible, pourquoi certaines solutions sont limitées, et comment utiliser les bonnes technologies au bon endroit, avec des exemples concrets et expliqués pas à pas.
- Comprendre le principe des animations à l’apparition
- Quand utiliser ce type d’animation
- Le réflexe CSS : une fausse bonne idée ?
- Peut-on vraiment faire une animation à l’apparition uniquement en CSS ?
- Pourquoi le JavaScript devient indispensable
- Détecter la visibilité avec IntersectionObserver
- Exemple simple avec IntersectionObserver
- JS décide quand, CSS décide comment
- Aller plus loin avec IntersectionObserver
- Jouer l’animation une seule fois
- Comprendre requestAnimationFrame
Comprendre le principe des animations à l’apparition
Avant de parler de code, prenons un instant pour comprendre ce que l’on cherche réellement à faire. Une animation à l’apparition consiste à déclencher un effet visuel lorsqu’un élément entre dans la zone visible de l’écran, autrement dit lorsque l’utilisateur scrolle et que cet élément devient perceptible. Tant que l’élément est hors écran, il reste discret. Dès qu’il apparaît, l’animation démarre.
Cela peut être un texte qui glisse doucement, une image qui se dévoile progressivement, ou un bloc qui se fond dans la page. Le point clé n’est pas l’animation elle-même, mais le moment où elle se déclenche.
C’est là que beaucoup de débutants se trompent. Ils savent animer en CSS, mais ils ne savent pas dire au navigateur quand lancer cette animation. Or, dans une animation au scroll, le “quand” est aussi important que le “comment”.
À quoi servent les animations au scroll ?
Ces animations ne sont pas là pour faire joli inutilement. Lorsqu’elles sont bien pensées, elles remplissent plusieurs rôles.
D’abord, elles améliorent la lisibilité. Un contenu qui apparaît au bon moment est plus facile à comprendre qu’un mur de texte affiché d’un coup. Ensuite, elles guident l’attention. L’utilisateur voit naturellement ce qui vient d’entrer dans son champ de vision.
Elles jouent aussi un rôle émotionnel. Une page animée donne une impression de modernité et de qualité. Sans aller jusqu’à en abuser, quelques animations bien placées peuvent vraiment changer la perception d’un site.
Enfin, elles peuvent servir des objectifs concrets comme augmenter le temps passé sur une page ou améliorer le taux de conversion. Un formulaire qui apparaît au bon moment, par exemple, est souvent plus efficace qu’un formulaire visible dès le chargement.
Quand utiliser ce type d’animation
Toutes les pages n’ont pas besoin d’animations au scroll. Sur un site très fonctionnel ou administratif, elles peuvent même devenir une gêne.
En revanche, elles sont particulièrement pertinentes dans plusieurs cas. Les pages de présentation, les landing pages, les portfolios, les articles longs ou pédagogiques sont des terrains idéaux. Dès que le contenu est découpé en sections verticales, l’animation à l’apparition prend tout son sens.
Il faut cependant garder une règle simple en tête : l’animation doit servir le contenu, jamais le ralentir. Si elle attire trop l’attention ou ralentit la lecture, elle devient contre-productive.
Le réflexe CSS : une fausse bonne idée ?
Quand on débute, le premier réflexe est souvent de vouloir tout faire en CSS. Et c’est compréhensible. Le CSS sait gérer les animations, les transitions, les effets visuels. Alors pourquoi ne pas l’utiliser pour déclencher une animation à l’apparition ?
Le problème, c’est que le CSS ne sait pas réellement répondre à une question essentielle : est-ce que cet élément est visible à l’écran ?
Pendant longtemps, le CSS n’avait aucune notion de scroll réel. Il pouvait réagir à un survol, à un focus, à un chargement, mais pas à l’entrée d’un élément dans la zone visible du navigateur.
On pouvait tricher avec des animations automatiques ou des délais, mais cela restait approximatif. L’animation se lançait à un moment donné, sans savoir si l’utilisateur était réellement en train de regarder l’élément.
Sur un projet pro, j’avais animé tous mes blocs avec un simple
animation-delay. Sur mon écran, tout semblait parfait. Les animations arrivaient “au bon moment”. Puis j’ai testé la page sur un autre écran, avec une résolution différente. Résultat : certaines animations se lançaient alors que les blocs étaient encore hors écran. L’effet “waouh” s’est transformé en effet “bizarre” : le scroll ne se devine pas, il se détecte.
Les nouvelles possibilités du CSS moderne
Le CSS a tout de même évolué. Des fonctionnalités récentes permettent de lier une animation à la position de scroll, notamment avec animation-timeline: view().
Sur le papier, cela semble être la solution idéale. Une animation purement CSS, déclenchée en fonction de la visibilité d’un élément. Sans JavaScript, sans logique complexe.
Mais comme toute nouveauté, cette approche a ses limites. Compatibilité partielle, contrôle réduit, comportements parfois imprévisibles selon les navigateurs.
Une séparation claire des rôles
Avant d’aller plus loin, retenez une idée clé qui va structurer tout ce guide : Dans une animation à l’apparition bien conçue, JavaScript décide quand, et le CSS décide comment.
Le JavaScript observe, détecte, déclenche. Le CSS s’occupe du rendu visuel, de la fluidité, des transitions. Mélanger les deux rôles conduit souvent à un code difficile à maintenir. C’est exactement pour cela que des outils comme IntersectionObserver existent. Ils permettent de détecter proprement la visibilité d’un élément, sans bricolage et sans calculs complexes.
Nous allons entrer dans le concret. Nous verrons d’abord comment fonctionne une animation CSS pure basée sur le scroll, avec ses avantages et surtout ses limites. Ensuite, nous plongerons dans IntersectionObserver, en expliquant son fonctionnement ligne par ligne, avec des exemples simples et efficaces. Enfin, nous parlerons de requestAnimationFrame, un outil souvent mal compris, mais extrêmement puissant lorsqu’il est utilisé au bon endroit.
Peut-on vraiment faire une animation à l’apparition uniquement en CSS ?
Avec l’évolution du CSS moderne, une question revient souvent : est-il enfin possible de créer une animation à l’apparition sans JavaScript ? Pendant longtemps, la réponse était clairement non. Aujourd’hui, elle devient plutôt : oui, mais….
Le CSS a introduit une notion très intéressante : la possibilité de lier une animation au scroll. Cela repose sur ce que l’on appelle une timeline, autrement dit une ligne temporelle qui ne dépend plus du temps, mais du défilement de la page.
C’est exactement ce que propose animation-timeline: view().
Sur le principe, cela semble idéal. L’animation progresse lorsque l’élément entre dans la zone visible, puis continue à évoluer au fur et à mesure du scroll. Pas besoin de JavaScript, pas besoin d’écouteurs d’événements. Tout est géré par le navigateur. Mais avant de s’emballer, il est important de comprendre comment cela fonctionne réellement.
Comprendre animation-timeline: view()
Traditionnellement, une animation CSS fonctionne avec le temps. Vous définissez une durée, par exemple une seconde, et l’animation se déroule sur cette durée, peu importe ce que fait l’utilisateur.
Avec animation-timeline: view(), le temps est remplacé par la visibilité de l’élément. L’animation progresse lorsque l’élément entre dans le viewport et évolue en fonction de sa position à l’écran. Autrement dit :
Ce n’est plus une animation qui démarre à un instant précis, mais une animation qui dépend du scroll.
Exemple d’animation à l’apparition en CSS pur
Imaginons un bloc qui doit apparaître progressivement lorsqu’il entre dans l’écran. Voici le HTML.
<section class="box">
<h2>Un bloc animé</h2>
<p>Ce contenu apparaît au scroll.</p>
</section>Passons maintenant au CSS.
.box {
opacity: 0;
transform: translateY(50px);
animation: reveal 1s linear forwards;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}Et l’animation associée.
@keyframes reveal {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}Prenons le temps de décortiquer.
Le bloc est invisible au départ grâce à opacity: 0 et légèrement déplacé vers le bas avec translateY. L’animation reveal décrit simplement le passage d’un état caché à un état visible.
La ligne vraiment intéressante est animation-timeline: view(). Elle indique que l’animation est liée à la visibilité de l’élément à l’écran.
Avec animation-range, vous précisez à quel moment l’animation commence et se termine. Ici, l’animation démarre dès que l’élément commence à entrer dans le viewport et se termine lorsqu’il est visible à environ 40 %.
Sur le papier, c’est élégant. Le code est lisible, entièrement en CSS, et relativement simple à comprendre.
Pour aller plus loin : La propriété animation en CSS
Les limites concrètes du CSS pur
Malheureusement, cette approche a plusieurs limites importantes qu’il ne faut pas ignorer, surtout dans un projet réel.
La première limite concerne la compatibilité navigateur. À l’heure actuelle, animation-timeline et view() ne sont pas pris en charge partout. Certains navigateurs les supportent partiellement, d’autres pas du tout. Cela signifie que votre animation peut simplement ne jamais se déclencher pour une partie de vos visiteurs.
La deuxième limite est le manque de contrôle logique. Le CSS ne peut pas décider de ne jouer l’animation qu’une seule fois. Si l’utilisateur scrolle vers le haut puis vers le bas, l’animation peut se rejouer, parfois de manière étrange.
Troisième problème, le CSS ne sait pas gérer des conditions complexes. Impossible de dire “lance cette animation uniquement si l’élément est visible à 60 %”, ou “ne l’anime que sur desktop”. On peut bricoler, mais cela devient vite fragile.
Enfin, le CSS ne sait pas déclencher d’actions. Il peut animer, mais pas décider. Vous ne pouvez pas facilement déclencher une autre logique, charger du contenu ou modifier un état applicatif.
C’est exactement pour cela que le CSS pur reste une solution expérimentale, intéressante pour des démos ou des projets simples, mais rarement suffisante dans un contexte professionnel.
Pourquoi le JavaScript devient indispensable
À partir du moment où vous voulez contrôler précisément quand une animation démarre, combien de fois elle se joue, et dans quelles conditions, le JavaScript devient incontournable.
Mais attention, cela ne signifie pas que vous devez tout animer en JavaScript. Bien au contraire. La bonne approche consiste à utiliser JavaScript uniquement pour observer et décider, puis à laisser le CSS gérer les animations visuelles. Cette séparation rend le code plus lisible, plus performant et plus facile à maintenir.
C’est exactement ce que permet l’API IntersectionObserver.
Détecter la visibilité avec IntersectionObserver
IntersectionObserver est une API JavaScript conçue pour répondre à une question simple : un élément est-il visible à l’écran, et à quel point ?

Des formations informatique pour tous !
Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…
Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.
Découvrez mes formations Qui suis-je ?Avant son existence, on utilisait souvent des écouteurs sur l’événement scroll, combinés à des calculs compliqués avec getBoundingClientRect. Cela fonctionnait, mais c’était lourd, imprécis et peu performant.
IntersectionObserver change complètement la donne. Vous décrivez ce que vous voulez observer, et le navigateur s’occupe du reste.
Principe de fonctionnement simplifié
L’idée est la suivante. Vous dites au navigateur : “Observe cet élément, et préviens-moi lorsqu’il entre ou sort de la zone visible”.
Le navigateur surveille en interne et appelle votre fonction uniquement lorsqu’un changement important se produit. Vous n’avez plus besoin d’écouter le scroll en permanence. C’est à la fois plus propre et plus performant.
Exemple simple avec IntersectionObserver
Commençons par un HTML volontairement simple.
<div class="card hidden">
<h3>Carte animée</h3>
<p>Cette carte apparaît au scroll.</p>
</div>Le CSS prépare l’animation, mais ne la lance pas encore.
.hidden {
opacity: 0;
transform: translateY(40px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.visible {
opacity: 1;
transform: translateY(0);
}Ici, aucune animation automatique. Le CSS décrit seulement deux états : caché et visible.
Passons maintenant au JavaScript.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
});
document.querySelectorAll('.hidden').forEach(el => {
observer.observe(el);
});Prenons le temps d’expliquer chaque étape.
- Vous créez d’abord un observateur avec
IntersectionObserver. La fonction passée en paramètre est appelée lorsque la visibilité d’un élément change. - Le tableau
entriescontient les éléments observés. Pour chacun,entry.isIntersectingindique s’il est visible dans le viewport. - Lorsqu’un élément devient visible, vous lui ajoutez simplement une classe CSS. Et c’est le CSS qui s’occupe de l’animation.
Résultat : une animation à l’apparition propre, fluide, contrôlée, et compatible avec tous les navigateurs modernes.
JS décide quand, CSS décide comment
Cet exemple illustre parfaitement un principe fondamental.
- Le JavaScript se contente de détecter un événement : l’entrée dans le viewport. Il ne gère ni la durée, ni la courbe, ni l’effet visuel.
- Le CSS, lui, décrit précisément l’animation. Vous pouvez la modifier sans toucher au JavaScript, ce qui est un énorme avantage en termes de maintenance.
C’est cette philosophie qui est aujourd’hui recommandée pour toutes les animations au scroll robustes.
Aller plus loin avec IntersectionObserver
L’exemple précédent montrait la version la plus simple d’IntersectionObserver. Mais cette API est bien plus puissante qu’elle n’y paraît. Elle permet d’affiner précisément quand une animation à l’apparition doit se déclencher.
C’est ici que l’on commence vraiment à maîtriser les Scroll-triggered animations.
Comprendre la notion de seuil de visibilité
Par défaut, IntersectionObserver considère qu’un élément est visible dès qu’un seul pixel entre dans l’écran. Dans certains cas, ce comportement est suffisant. Dans d’autres, il est préférable d’attendre que l’élément soit réellement visible.
C’est là qu’intervient la notion de threshold, autrement dit le pourcentage de visibilité requis pour déclencher l’événement.
Prenons un exemple concret.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, {
threshold: 0.5
});Avec threshold: 0.5, l’animation ne se déclenche que lorsque 50 % de l’élément est visible à l’écran. Cela évite des animations qui démarrent trop tôt, notamment sur les grands écrans ou les sections volumineuses.
Ce simple paramètre change radicalement le ressenti utilisateur.
Gérer la marge de déclenchement avec rootMargin
Il est parfois utile de déclencher une animation avant que l’élément ne soit réellement visible, ou au contraire un peu après. C’est exactement le rôle de rootMargin.
Ce paramètre fonctionne comme une marge autour de la zone visible.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, {
rootMargin: "0px 0px -100px 0px"
});Ici, l’animation ne se déclenche que lorsque l’élément est bien engagé dans l’écran. À l’inverse, une marge positive permet d’anticiper l’animation.
Ce réglage est très utilisé pour créer des animations douces, qui semblent presque prédire le scroll de l’utilisateur.
Jouer l’animation une seule fois
Un besoin très courant consiste à lancer l’animation une seule fois, même si l’utilisateur scrolle vers le haut puis redescend.
Bonne nouvelle : IntersectionObserver le permet très facilement.
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
});Une fois l’élément animé, on arrête simplement de l’observer. Cela garantit une animation propre, sans répétition inutile.
Ce détail fait toute la différence entre une animation maîtrisée et un effet gadget.
Pourquoi IntersectionObserver est aujourd’hui la référence
IntersectionObserver est performant, car il délègue le travail au navigateur. Il est précis, car il repose sur la visibilité réelle. Il est lisible, car la logique est claire. Et surtout, il est maintenable.
C’est pour toutes ces raisons qu’il est aujourd’hui la solution recommandée pour gérer les animations à l’apparition, les animations au scroll et plus globalement les Scroll-triggered animations modernes.
Mais il existe encore un autre outil, souvent mal compris, qui mérite d’être abordé.
Comprendre requestAnimationFrame
À première vue, requestAnimationFrame peut sembler être une solution pour animer au scroll. En réalité, ce n’est pas un outil de déclenchement, mais un outil de synchronisation.
Son rôle est simple : demander au navigateur d’exécuter une fonction juste avant le prochain rafraîchissement de l’écran. Cela garantit une animation fluide et optimisée.
Autrement dit, requestAnimationFrame ne décide pas quand une animation démarre, mais comment elle est calculée image par image.
Comment fonctionne requestAnimationFrame
Lorsqu’un navigateur affiche une page, il rafraîchit l’écran environ 60 fois par seconde. requestAnimationFrame permet d’exécuter du code exactement au bon moment dans ce cycle.
Voici un exemple simple.
function animate() {
console.log("Nouvelle frame");
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Cette fonction est appelée à chaque frame. C’est ce mécanisme qui permet de créer des animations ultra-fluides, synchronisées avec le rendu réel.
Exemple concret lié au scroll
Imaginons que vous souhaitiez animer un élément en fonction de la position de scroll, avec un contrôle très fin.
const element = document.querySelector('.progress');
function animate() {
const scrollY = window.scrollY;
element.style.transform = `scaleX(${scrollY / 1000})`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Ici, l’élément évolue en temps réel en fonction du scroll. Cela peut être utile pour des barres de progression, des effets parallaxe ou des animations très personnalisées.
Cependant, cette approche a un coût.
Pourquoi requestAnimationFrame n’est pas idéal pour l’apparition simple
Utiliser requestAnimationFrame pour détecter l’apparition d’un élément est souvent excessif. Vous exécutez du code à chaque frame, même lorsque rien ne change.
Cela demande plus de calculs, plus de vigilance sur les performances, et plus de code à maintenir. C’est pour cela qu’il faut bien distinguer les rôles.
Pour détecter la visibilité d’un élément, IntersectionObserver est le bon outil.
Pour calculer des animations complexes et continues, requestAnimationFrame devient pertinent. Les confondre mène souvent à des sites plus lourds que nécessaire.
Comment combiner intelligemment les deux
Dans certains cas avancés, les deux peuvent cohabiter. IntersectionObserver peut servir de déclencheur, et requestAnimationFrame prendre le relais pour une animation complexe.
Mais pour 90 % des besoins liés à une animation à l’apparition, IntersectionObserver accompagné de CSS suffit largement.
C’est cette sobriété qui fait la qualité d’une bonne expérience utilisateur.
Animer un élément lorsqu’il devient visible à l’écran n’est pas qu’une question d’effet visuel. C’est un équilibre subtil entre intention, timing et performance. Une animation réussie ne se remarque pas comme une animation, elle se ressent comme une évidence.
Le CSS moderne ouvre des portes intéressantes, mais il reste limité dès que l’on cherche du contrôle et de la fiabilité. IntersectionObserver apporte une réponse claire, élégante et robuste à un problème longtemps mal résolu. requestAnimationFrame, de son côté, rappelle que la performance passe avant tout par une bonne compréhension du fonctionnement du navigateur.
Si vous deviez retenir une seule idée, ce serait celle-ci : laissez JavaScript observer et décider, laissez le CSS s’exprimer et animer. En respectant cette séparation, vos animations au scroll gagneront en fluidité, en clarté et en longévité.
Et surtout, n’oubliez jamais que l’animation n’est pas une finalité. Elle est un langage silencieux, au service du contenu et de celles et ceux qui le lisent.

Fondateur de l’agence Créa-troyes, affiliée France Num
Intervenant en Freelance.
Contactez-moi
