Maîtriser GSAP de zéro : tweens, timelines, ScrollTrigger, plugins et intégration React/Next.js pour créer des animations web professionnelles.
Trois fonctionnalités souvent sous-utilisées mais qui font la différence entre des animations basiques et des animations soignées.
Stagger anime plusieurs éléments en décalant chacun dans le temps. C'est la façon correcte d'animer des listes, des grilles, des menus.
gsap.from('.card', {
y: 40,
opacity: 0,
duration: 0.6,
stagger: 0.1, // 0.1s de décalage entre chaque élément
})Chaque .card démarre 0.1s après la précédente. 10 cartes = la dernière démarre à 0.9s.
gsap.from('.card', {
y: 40,
opacity: 0,
duration: 0.6,
stagger: {
amount: 0.8, // durée totale répartie sur tous les éléments
from: 'start', // ordre : 'start' | 'end' | 'center' | 'random' | index
ease: 'power1.in', // ease sur le stagger lui-même
grid: 'auto', // pour les grilles — calcule les distances 2D
},
})amount vs valeur directe :
stagger: 0.1 = 0.1s entre chaque élément (dépend du nombre d'éléments)stagger: { amount: 0.8 } = 0.8s total réparti sur tous les éléments (indépendant du nombre)amount est préférable quand le nombre d'éléments varie — l'animation prend toujours le même temps total.
// Depuis le centre — effet "explosion vers l'extérieur"
stagger: { amount: 0.6, from: 'center' }
// Depuis un index précis
stagger: { amount: 0.6, from: 3 } // l'élément 3 en premier
// Aléatoire — bonne surprise visuelle
stagger: { amount: 0.6, from: 'random' }Pour une grille, le stagger peut prendre en compte la position 2D de chaque élément :
// 6 colonnes, 4 lignes
gsap.from('.grid-item', {
scale: 0,
opacity: 0,
duration: 0.5,
stagger: {
amount: 1,
grid: [4, 6], // [rows, columns]
from: 'center', // depuis le centre de la grille
ease: 'power2.out',
},
})Avec grid: 'auto', GSAP calcule la grille lui-même à partir des positions DOM.
gsap.utils est une collection d'utilitaires mathématiques souvent nécessaires dans les animations.
Contraint une valeur entre un min et un max.
const clamp = gsap.utils.clamp(0, 100)
clamp(150) // 100
clamp(-20) // 0
clamp(50) // 50Utile pour normaliser des valeurs de scroll ou de pointeur.
Mappe une valeur d'une plage vers une autre.
const mapper = gsap.utils.mapRange(0, 1000, 0, 100)
mapper(500) // 50
mapper(250) // 25
mapper(1000) // 100Cas d'usage courant : convertir une position de scroll en pourcentage d'animation.
window.addEventListener('mousemove', (e) => {
const normalized = gsap.utils.mapRange(0, window.innerWidth, -20, 20)
const rotation = normalized(e.clientX)
gsap.to('.card', { rotation, duration: 0.4, ease: 'power1.out' })
})Interpole entre deux valeurs selon un progrès (0 à 1).
const lerp = gsap.utils.interpolate('#ff0000', '#0000ff')
lerp(0) // '#ff0000'
lerp(0.5) // couleur intermédiaire
lerp(1) // '#0000ff'Fonctionne avec les nombres, couleurs, objets, et tableaux.
Convertit n'importe quoi en tableau — pratique pour normaliser les inputs.
gsap.utils.toArray('.card') // NodeList → Array
gsap.utils.toArray(someElement) // Element → [Element]
gsap.utils.toArray([el1, el2]) // Array → Array (inchangé)Utile dans les fonctions qui acceptent un sélecteur ou un tableau d'éléments.
Mélange un tableau en place.
const items = gsap.utils.toArray('.card')
gsap.utils.shuffle(items)
gsap.from(items, { y: 30, opacity: 0, stagger: 0.08 })Contraint une valeur au multiple le plus proche.
const snap = gsap.utils.snap(90)
snap(100) // 90
snap(140) // 90
snap(145) // 180
// Snap à une valeur d'un tableau
const snapTo = gsap.utils.snap([0, 100, 200, 300])
snapTo(80) // 100
snapTo(250) // 300Utile pour des sliders ou des carousels qui s'accrochent à des positions précises.
gsap.matchMedia() permet de définir des animations différentes selon la taille d'écran, et de les nettoyer automatiquement quand la media query ne s'applique plus.
const mm = gsap.matchMedia()
mm.add('(min-width: 768px)', () => {
// Animations desktop
gsap.from('.hero-text', {
x: -100,
opacity: 0,
duration: 1,
})
gsap.from('.hero-image', {
x: 100,
opacity: 0,
duration: 1,
})
// La fonction retournée est appelée au cleanup
return () => {
// nettoyage si nécessaire
}
})
mm.add('(max-width: 767px)', () => {
// Animations mobile — différentes ou désactivées
gsap.from('.hero-text', {
y: 40,
opacity: 0,
duration: 0.7,
})
// Pas d'animation sur hero-image en mobile
})Quand la fenêtre passe d'une résolution à une autre, GSAP annule automatiquement les animations de la media query inactive et applique la nouvelle. Plus besoin de gérer ça à la main.
mm.add({
isDesktop: '(min-width: 1024px)',
isTablet: '(min-width: 768px) and (max-width: 1023px)',
isMobile: '(max-width: 767px)',
prefersReducedMotion: '(prefers-reduced-motion: reduce)',
}, (context) => {
const { isDesktop, isMobile, prefersReducedMotion } = context.conditions
if (prefersReducedMotion) {
// Pas d'animations, ou animations très réduites
return
}
if (isDesktop) {
gsap.from('.hero', { x: -200, duration: 1.2 })
} else if (isMobile) {
gsap.from('.hero', { y: 40, duration: 0.7 })
}
})Toujours vérifier cette préférence utilisateur. Les personnes sensibles au mouvement peuvent configurer leur OS pour réduire les animations.
mm.add('(prefers-reduced-motion: no-preference)', () => {
// Toutes vos animations ici
// Elles ne s'activent que si l'utilisateur n'a pas demandé la réduction de mouvement
const tl = gsap.timeline({ ... })
// ...
})C'est la façon la plus propre de respecter cette préférence — une seule condition englobe toutes vos animations.
gsap.context() permet de scoper des animations à un conteneur DOM et de les nettoyer facilement.
const ctx = gsap.context(() => {
// Toutes les animations ici sont associées au contexte
gsap.from('.box', { y: 30, opacity: 0 })
gsap.from('.title', { x: -20, opacity: 0 })
}, containerElement) // scoped à containerElement
// Nettoyer toutes les animations du contexte
ctx.revert()revert() annule toutes les animations ET remet les éléments à leur état d'origine (avant que GSAP les modifie). C'est plus propre que kill() qui arrête sans remettre en place.
En React, useGSAP wrape gsap.context() — c'est couvert au chapitre suivant.
Stagger, utils et matchMedia permettent d'écrire des animations robustes, précises, et respectueuses des préférences utilisateur. Le chapitre suivant couvre l'intégration dans React et Next.js — avec les pièges propres aux frameworks.