Maîtriser GSAP de zéro : tweens, timelines, ScrollTrigger, plugins et intégration React/Next.js pour créer des animations web professionnelles.
Dès qu'on veut enchaîner plusieurs animations, les tweens individuels avec des delay calculés à la main deviennent un cauchemar. Changer la durée d'une animation force à recalculer tous les délais suivants.
// À éviter — delay manuel = fragile
gsap.from('.title', { y: 30, opacity: 0, duration: 0.8 })
gsap.from('.subtitle', { y: 20, opacity: 0, duration: 0.6, delay: 0.8 })
gsap.from('.button', { scale: 0.9, opacity: 0, duration: 0.5, delay: 1.4 })Changer la durée du .title casse tout. Les timelines résolvent ça.
const tl = gsap.timeline()
tl.from('.title', { y: 30, opacity: 0, duration: 0.8 })
.from('.subtitle', { y: 20, opacity: 0, duration: 0.6 })
.from('.button', { scale: 0.9, opacity: 0, duration: 0.5 })Par défaut, chaque animation s'enchaîne après la précédente. La timeline calcule le timing automatiquement. Changer la durée de .title décale tout le reste proportionnellement.
Le troisième argument d'une animation dans une timeline est la position — quand elle démarre dans la timeline.
const tl = gsap.timeline()
// Démarre à 1.5s dans la timeline
tl.from('.element', { opacity: 0, duration: 1 }, 1.5)
// Démarre 0.2s après la fin de l'animation précédente (décalage positif)
tl.from('.element', { opacity: 0, duration: 1 }, '+=0.2')
// Démarre 0.2s AVANT la fin de l'animation précédente (overlap)
tl.from('.element', { opacity: 0, duration: 1 }, '-=0.2')
// Démarre en même temps que l'animation précédente
tl.from('.element', { opacity: 0, duration: 1 }, '<')
// Démarre 0.3s après le DÉBUT de l'animation précédente
tl.from('.element', { opacity: 0, duration: 1 }, '<0.3')
// Démarre au début de la timeline
tl.from('.element', { opacity: 0, duration: 1 }, 0)Le < est particulièrement utile pour des animations en parallèle avec un léger décalage.
const tl = gsap.timeline()
tl
.from('.card', { y: 60, opacity: 0, duration: 0.7, ease: 'power3.out' })
.from('.card-image', { scale: 1.1, duration: 0.6, ease: 'power2.out' }, '<') // en même temps
.from('.card-title', { y: 15, opacity: 0, duration: 0.5 }, '-=0.2') // overlap
.from('.card-body', { y: 10, opacity: 0, duration: 0.4 }, '-=0.2')
.from('.card-button', { scale: 0.9, opacity: 0, duration: 0.3 }, '-=0.1')Les labels permettent de nommer des points dans la timeline.
const tl = gsap.timeline()
tl
.from('.hero', { opacity: 0, duration: 1 })
.addLabel('heroVisible')
.from('.nav', { y: -30, duration: 0.5 })
.addLabel('navIn')
.from('.content', { y: 40, opacity: 0, duration: 0.8 })
// Sauter à un label
tl.play('heroVisible')
tl.seek('navIn')Utile pour des animations longues (landing pages) où on veut pouvoir reprendre à un point précis.
const tl = gsap.timeline({
// Defaults pour toutes les animations de cette timeline
defaults: {
duration: 0.6,
ease: 'power2.out',
},
// Répétition
repeat: 2,
yoyo: true,
repeatDelay: 0.5,
// Callbacks
onStart: () => console.log('début'),
onComplete: () => console.log('fin'),
onRepeat: () => console.log('répétition'),
// Ne démarre pas immédiatement
paused: true,
})defaults sur une timeline évite de répéter duration et ease sur chaque animation :
const tl = gsap.timeline({
defaults: { duration: 0.7, ease: 'power3.out' },
})
// Ces animations héritent de duration: 0.7 et ease: 'power3.out'
tl
.from('.title', { y: 30, opacity: 0 })
.from('.text', { y: 20, opacity: 0 })
.from('.cta', { scale: 0.9, opacity: 0 })Une timeline expose les mêmes contrôles qu'un tween.
const tl = gsap.timeline({ paused: true })
tl.from('.menu-items', { x: -20, opacity: 0, stagger: 0.08 })
// Déclenchement
document.querySelector('.menu-toggle').addEventListener('click', () => {
if (tl.reversed()) {
tl.play()
} else {
tl.reverse()
}
}).reversed() retourne true si la timeline joue en sens inverse. Ce pattern — play/reverse sur une même timeline — est la façon propre de gérer des animations d'ouverture/fermeture (menus, modales, accordéons).
Une timeline peut contenir d'autres timelines. Ça permet d'isoler des animations réutilisables.
function createCardAnimation(card) {
const tl = gsap.timeline()
tl
.from(card, { y: 40, opacity: 0, duration: 0.6 })
.from(card.querySelector('.title'), { y: 10, opacity: 0, duration: 0.4 }, '-=0.2')
return tl
}
// Timeline principale qui orchestre les cartes
const mainTl = gsap.timeline()
document.querySelectorAll('.card').forEach((card) => {
mainTl.add(createCardAnimation(card), '-=0.3')
})Chaque carte a sa propre timeline de 600ms + 400ms, mais elles se chevauchent de 300ms dans la timeline principale — effet cascade fluide.
Toujours tuer les tweens et timelines qui ne sont plus nécessaires — sinon les animations continuent sur des éléments qui n'existent plus dans le DOM.
const tl = gsap.timeline()
// ... animations
// Arrêter et libérer
tl.kill()
// Tuer toutes les animations sur un élément
gsap.killTweensOf('.element')
// Tuer toutes les animations GSAP de la page
gsap.globalTimeline.clear()Ce cleanup est particulièrement important en React et dans les SPAs — c'est couvert en détail au chapitre 6.
Tweens et timelines donnent un contrôle complet sur des animations séquencées. La suite : ScrollTrigger — le plugin qui lie vos animations à la position de scroll de l'utilisateur.