GSAP (GreenSock) — Web Animation Skill
When to use
-
High-quality UI/motion design: entrances, micro-interactions, page transitions
-
Timeline-based sequences (vs. scattered CSS transitions)
-
Scroll-driven storytelling (with ScrollTrigger)
-
Complex easing, staggering, orchestration across many elements
Key concepts & APIs
-
Tweens:
-
gsap.to(targets, vars)
-
gsap.from(targets, vars)
-
gsap.fromTo(targets, fromVars, toVars)
-
Timelines:
-
const tl = gsap.timeline({ defaults, repeat, yoyo, paused })
-
Chain: tl.to(...).from(...).addLabel('x').add(() => ...)
-
Position parameter: absolute 1.2 , relative "+=0.5" , overlap "-=0.3" , label "intro"
-
Eases: ease: "power2.out" , "expo.inOut" , "elastic.out(1, 0.3)"
-
Staggers: stagger: 0.05 or { each, from: "start|center|end|random", grid }
-
Performance-friendly properties:
-
Prefer transforms (x , y , scale , rotation ) and opacity (autoAlpha )
-
ScrollTrigger (plugin):
-
gsap.registerPlugin(ScrollTrigger)
-
Inline: gsap.to(".box", { scrollTrigger: ".box", x: 500 })
-
Advanced: scrollTrigger: { trigger, start, end, scrub, pin, snap, markers }
-
Standalone: ScrollTrigger.create({ trigger, start, end, onUpdate, onToggle })
Common pitfalls (and fixes)
-
Animating layout properties (top/left/width/height) → jank
-
Use transforms, add will-change: transform , avoid forced reflow.
-
ScrollTrigger “not firing” due to wrong trigger sizing/overflow containers
-
Ensure trigger exists, has height, and check scroll container (nested scrolling needs config).
-
Not cleaning up in SPA/React
-
Use gsap.context() and revert on unmount; kill triggers (ScrollTrigger.getAll().forEach(t => t.kill()) ) if needed.
-
FOUC / measuring before fonts/images load
-
Initialize after layout is stable; run ScrollTrigger.refresh() after images load.
Quick recipes
- Hero entrance (stagger)
gsap.from(".hero [data-anim]", { y: 24, autoAlpha: 0, duration: 0.8, ease: "power2.out", stagger: 0.06, });
- Sequenced timeline
const tl = gsap.timeline({ defaults: { ease: "power2.out", duration: 0.6 } }); tl.from(".nav", { y: -20, autoAlpha: 0 }) .from(".hero-title", { y: 30, autoAlpha: 0 }, "-=0.2") .from(".hero-cta", { scale: 0.95, autoAlpha: 0 }, "-=0.2");
- Scroll-scrub pinned section
gsap.registerPlugin(ScrollTrigger);
gsap.timeline({ scrollTrigger: { trigger: ".story", start: "top top", end: "+=800", scrub: 1, pin: true, }, }).to(".story .panel", { xPercent: -200 });
What to ask the user (if requirements unclear)
-
Is this a static site or SPA (React/Next/Vue)? Any page transitions?
-
Do we need scroll-driven sections (pin/scrub/snap)?
-
Performance constraints (mobile support, reduced motion)?