Understanding GSAP ScrollTrigger
ScrollTrigger is one of the most powerful animation tools available. Here's how I actually think about it when building scroll-driven UIs.
ScrollTrigger confused me the first time I used it. Not because the API is bad — it's excellent — but because the mental model is different from what you'd expect.
The core idea
A ScrollTrigger watches a trigger element and fires animation logic based on where that element sits relative to the scroller (usually the viewport). You describe a start and end scroll position, and everything in between is up to you.
gsap.to(element, {
y: -100,
scrollTrigger: {
trigger: element,
start: "top 80%", // when element's top hits 80% from top of viewport
end: "bottom 20%",
scrub: 1, // ties animation to scroll position
},
});Pin vs scrub
These two are often confused:
The gotcha with pinSpacing
When you pin an element, GSAP adds space after it to prevent content jumping. If you're stacking pinned sections (like a hero that fades into the next section), you usually want `pinSpacing: false` so sections overlap correctly.
Horizontal scroll pattern
For a cards carousel, calculate the total scroll width and translate the track:
const totalScroll = track.scrollWidth - window.innerWidth;
gsap.to(track, {
x: -totalScroll,
ease: "none",
scrollTrigger: {
trigger: section,
start: "top top",
end: () => `+=${totalScroll}`,
scrub: 1,
pin: true,
},
});Use an arrow function for `end` so it recalculates on resize.