Introduction
Les animations declenchees au scroll sont devenues incontournables pour creer des experiences web memorables. Elles permettent de guider l'attention de l'utilisateur, de raconter une histoire visuelle et d'ajouter une dimension interactive a vos pages.
Dans ce guide complet, nous allons explorer 5 techniques essentielles pour implementer des scroll-driven animations, de la plus classique (Intersection Observer) a la plus moderne (scroll-timeline CSS). Chaque technique est accompagnee d'une demo interactive et du code complet.
La barre de progression en haut de page et le suivi de lecture dans le sommaire sont des exemples concrets de scroll-driven animations. Scrollez pour les voir en action !
1. Fade-in au scroll avec Intersection Observer
L'Intersection Observer API est la methode la plus fiable et performante pour detecter quand un element entre dans le viewport. Contrairement aux anciennes techniques basees sur scroll events, elle ne bloque pas le thread principal.
Rapide
Animation fluide
Performant
Zero jank
Compatible
Tous navigateurs
// Configuration de l'observer
const observerOptions = {
root: null, // viewport par defaut
rootMargin: '0px', // marge autour du viewport
threshold: 0.1 // 10% visible pour declencher
};
// Callback quand un element entre/sort du viewport
const handleIntersect = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Element visible : ajouter la classe
entry.target.classList.add('visible');
// Optionnel : arreter d'observer apres animation
observer.unobserve(entry.target);
}
});
};
// Creer l'observer
const observer = new IntersectionObserver(handleIntersect, observerOptions);
// Observer tous les elements avec [data-animate]
document.querySelectorAll('[data-animate]').forEach(el => {
observer.observe(el);
});
/* Etat initial : invisible et decale vers le bas */
.fade-card {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Etat final : visible et a sa place */
.fade-card.visible {
opacity: 1;
transform: translateY(0);
}
/* Delai en cascade pour effet stagger */
.fade-card:nth-child(2) { transition-delay: 0.1s; }
.fade-card:nth-child(3) { transition-delay: 0.2s; }
.fade-card:nth-child(4) { transition-delay: 0.3s; }
Parametres cles de l'Intersection Observer
- root : L'element parent qui sert de viewport (null = viewport du navigateur)
- rootMargin : Marge autour du root (ex: "-100px" pour declencher 100px avant)
- threshold : Pourcentage de visibilite requis (0.1 = 10%, 1 = 100%)
2. Effet Parallax
L'effet parallax cree une illusion de profondeur en faisant bouger differentes couches a des vitesses differentes lors du scroll. C'est un classique du web design moderne.
// Elements avec differentes vitesses de parallax
const layers = [
{ element: document.querySelector('.parallax-stars'), speed: 0.2 },
{ element: document.querySelector('.parallax-moon'), speed: 0.3 },
{ element: document.querySelector('.mountain-1'), speed: 0.5 },
{ element: document.querySelector('.mountain-2'), speed: 0.7 },
];
// Fonction optimisee avec requestAnimationFrame
let ticking = false;
function updateParallax() {
const scrollY = window.scrollY;
layers.forEach(layer => {
const yOffset = scrollY * layer.speed;
layer.element.style.transform = `translateY(${yOffset}px)`;
});
ticking = false;
}
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(updateParallax);
ticking = true;
}
});
Utilisez toujours requestAnimationFrame pour les animations liees au scroll. Sans cette optimisation, vous risquez des saccades (jank) qui degradent l'experience utilisateur.
3. Progress bar de lecture
La barre de progression de lecture est un excellent indicateur visuel pour les articles longs. Elle montre a l'utilisateur ou il en est dans sa lecture.
const progressBar = document.querySelector('.reading-progress');
const article = document.querySelector('.article-content');
function updateProgress() {
// Position de l'article
const articleTop = article.getBoundingClientRect().top + window.scrollY;
const articleHeight = article.offsetHeight;
const windowHeight = window.innerHeight;
// Calcul du pourcentage
const scrolled = window.scrollY - articleTop + windowHeight;
const total = articleHeight + windowHeight;
const progress = Math.min(Math.max(scrolled / total, 0), 1);
// Mise a jour de la barre
progressBar.style.width = `${progress * 100}%`;
}
// Ecouter le scroll avec throttling
let rafId = null;
window.addEventListener('scroll', () => {
if (rafId) return;
rafId = requestAnimationFrame(() => {
updateProgress();
rafId = null;
});
});
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 0%;
height: 3px;
background: linear-gradient(
90deg,
#6366f1,
#8b5cf6,
#d946ef
);
z-index: 9999;
transition: width 0.1s ease-out;
}
4. Reveal animations (slide, scale)
Les reveal animations permettent de faire apparaitre les elements de differentes manieres : depuis la gauche, la droite, avec un effet de zoom, ou meme avec une rotation. Voici un exemple avec plusieurs variantes.
Scale
Zoom depuis le centre
Rotate
Rotation + scale
/* Slide depuis la gauche */
.reveal-slide-left {
opacity: 0;
transform: translateX(-50px);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Slide depuis la droite */
.reveal-slide-right {
opacity: 0;
transform: translateX(50px);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Scale depuis le centre */
.reveal-scale {
opacity: 0;
transform: scale(0.8);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Rotation + scale */
.reveal-rotate {
opacity: 0;
transform: rotate(-10deg) scale(0.9);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Etat visible commun */
.reveal-slide-left.visible,
.reveal-slide-right.visible,
.reveal-scale.visible,
.reveal-rotate.visible {
opacity: 1;
transform: translateX(0) scale(1) rotate(0);
}
Animation en cascade (stagger)
Pour creer un effet de cascade elegant, utilisez transition-delay avec une valeur incrementale :
// Ajouter un delai incrementiel a chaque element
const items = document.querySelectorAll('[data-animate="reveal"]');
items.forEach((item, index) => {
item.style.transitionDelay = `${index * 0.1}s`;
});
5. Scroll-timeline CSS natif
La specification CSS Scroll-driven Animations permet de lier des animations directement a la progression du scroll, sans JavaScript. C'est la methode la plus performante car elle s'execute entierement sur le GPU.
Animation liee au scroll
Scrollez la page pour voir la transformation
/* Animation keyframes classique */
@keyframes scrollReveal {
from {
opacity: 0;
transform: translateY(100px) scale(0.8);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.scroll-animated-element {
/* Lier l'animation au scroll de la page */
animation: scrollReveal linear;
animation-timeline: scroll();
/* Controler quand l'animation se joue */
animation-range: entry 0% cover 50%;
}
Options de animation-timeline
scroll(): Lie a la timeline de scroll du documentscroll(root): Lie au scroll du document racinescroll(nearest): Lie au conteneur scrollable le plus procheview(): Lie a la visibilite de l'element dans le viewport
Options de animation-range
entry: Quand l'element entre dans le viewportexit: Quand l'element sort du viewportcover: Quand l'element couvre le viewportcontain: Quand l'element est entierement visible
Scroll-timeline est supporte dans Chrome 115+ et Edge 115+. Pour une compatibilite plus large, utilisez l'Intersection Observer avec un fallback.
Bonnes pratiques et accessibilite
Performance
- Utilisez
transformetopacity: Ces proprietes sont composites et animees sur le GPU - Evitez d'animer
width,height,top,left: Elles declenchent un reflow couteux - Throttling avec
requestAnimationFrame: Essentiel pour les animations liees au scroll will-changeavec parcimonie : Utile mais consomme de la memoire GPU
Accessibilite
Respectez toujours les preferences utilisateur concernant les animations :
/* Desactiver les animations si l'utilisateur le demande */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
/* Afficher directement les elements animes */
.fade-card,
.reveal-item {
opacity: 1 !important;
transform: none !important;
}
}
// Verifier la preference utilisateur en JavaScript
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
);
if (prefersReducedMotion.matches) {
// Desactiver les animations JavaScript
console.log('Animations reduites');
}
// Ecouter les changements de preference
prefersReducedMotion.addEventListener('change', () => {
// Reactiver/desactiver dynamiquement
});
Tableau comparatif des techniques
| Technique | Performance | Compatibilite | Cas d'usage |
|---|---|---|---|
| Intersection Observer | Excellente | 97%+ | Reveal, lazy loading |
| Scroll event + rAF | Bonne | 100% | Parallax, progress bar |
| CSS scroll-timeline | Optimale | ~75% | Animations complexes |
Conclusion
Les scroll-driven animations sont un outil puissant pour creer des experiences web immersives. Avec les 5 techniques presentees dans ce guide, vous avez tout ce qu'il faut pour implementer des animations performantes et accessibles.
Retenez ces points essentiels :
- Intersection Observer pour les animations de reveal simples
- requestAnimationFrame pour le parallax et les progress bars
- CSS scroll-timeline pour les animations complexes (avec fallback)
- Toujours respecter
prefers-reduced-motion
Retrouvez des dizaines d'effets scroll prets a l'emploi dans notre bibliotheque d'effets, avec code copiable et personnalisable.