| name | gsap-scrolltrigger |
| description | Scroll-based animations using GSAP ScrollTrigger plugin including pinning, scrubbing, snap points, and parallax effects. Use when creating scroll-driven animations, sticky sections, progress indicators, or parallax scrolling experiences. |
GSAP ScrollTrigger
Scroll-driven animations and interactions.
Quick Start
bun install gsap
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
gsap.to(".box", {
x: 500,
scrollTrigger: {
trigger: ".box",
start: "top center",
end: "bottom center",
scrub: true,
},
});
Core Concepts
Basic ScrollTrigger
gsap.to(".element", {
x: 200,
scrollTrigger: {
trigger: ".element",
start: "top center",
end: "bottom center",
toggleActions: "play pause resume reset",
},
});
Start/End Positions
start: "top center";
start: "top 80%";
start: "center center";
start: "bottom top";
start: "top top+=100";
Position Reference
| Value | Description |
|---|
top | Top edge |
center | Center |
bottom | Bottom edge |
80% | 80% from top |
+=100 | Plus 100 pixels |
-=50 | Minus 50 pixels |
Scrub Animations
Basic Scrub
gsap.to(".progress-bar", {
scaleX: 1,
scrollTrigger: {
trigger: ".content",
start: "top top",
end: "bottom bottom",
scrub: true,
},
});
Smooth Scrub
gsap.to(".element", {
x: 500,
scrollTrigger: {
trigger: ".section",
scrub: 1,
},
});
Scrub with Timeline
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".container",
start: "top top",
end: "+=3000",
scrub: 1,
pin: true,
},
});
tl.to(".step1", { opacity: 1 })
.to(".step2", { opacity: 1 })
.to(".step3", { opacity: 1 });
Pinning
Basic Pin
ScrollTrigger.create({
trigger: ".panel",
start: "top top",
end: "+=500",
pin: true,
});
Pin with Animation
gsap.to(".content", {
x: "-200%",
ease: "none",
scrollTrigger: {
trigger: ".horizontal-section",
start: "top top",
end: () => "+=" + document.querySelector(".horizontal-section").offsetWidth,
pin: true,
scrub: 1,
},
});
Pin Spacing
ScrollTrigger.create({
trigger: ".section",
pin: true,
pinSpacing: true,
});
Toggle Actions
Action Syntax
toggleActions: "play pause resume reset";
toggleActions: "play none none none";
toggleActions: "play reverse play reverse";
toggleActions: "restart none none none";
toggleActions: "play complete reverse reset";
Action Values
| Action | Effect |
|---|
play | Play forward |
pause | Pause |
resume | Resume from paused |
reverse | Play backward |
restart | Restart from beginning |
reset | Reset to start (no animation) |
complete | Jump to end |
none | Do nothing |
Snap Points
Basic Snap
ScrollTrigger.create({
trigger: ".sections",
start: "top top",
end: "bottom bottom",
snap: 1 / 4,
});
Snap to Labels
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".container",
scrub: 1,
snap: {
snapTo: "labels",
duration: 0.5,
ease: "power2.inOut",
},
},
});
tl.addLabel("intro")
.to(".a", { opacity: 1 })
.addLabel("middle")
.to(".b", { opacity: 1 })
.addLabel("end");
Snap Configuration
snap: {
snapTo: [0, 0.25, 0.5, 0.75, 1],
duration: { min: 0.2, max: 0.6 },
delay: 0,
ease: 'power1.inOut',
directional: true
}
Callbacks
ScrollTrigger Callbacks
ScrollTrigger.create({
trigger: ".section",
onEnter: () => console.log("Entered"),
onLeave: () => console.log("Left"),
onEnterBack: () => console.log("Entered from bottom"),
onLeaveBack: () => console.log("Left going up"),
onUpdate: (self) => console.log("Progress:", self.progress),
onToggle: (self) => console.log("Active:", self.isActive),
onRefresh: () => console.log("Refreshed"),
});
Progress-Based Updates
ScrollTrigger.create({
trigger: ".section",
start: "top bottom",
end: "bottom top",
onUpdate: (self) => {
updateElement(self.progress);
},
});
Parallax Effects
Basic Parallax
gsap.to(".background", {
yPercent: -50,
ease: "none",
scrollTrigger: {
trigger: ".section",
scrub: true,
},
});
gsap.to(".foreground", {
yPercent: 50,
ease: "none",
scrollTrigger: {
trigger: ".section",
scrub: true,
},
});
Multi-Layer Parallax
const layers = [
{ selector: ".layer-1", speed: -20 },
{ selector: ".layer-2", speed: -40 },
{ selector: ".layer-3", speed: -60 },
{ selector: ".layer-4", speed: -80 },
];
layers.forEach((layer) => {
gsap.to(layer.selector, {
yPercent: layer.speed,
ease: "none",
scrollTrigger: {
trigger: ".parallax-section",
start: "top bottom",
end: "bottom top",
scrub: true,
},
});
});
Horizontal Scrolling
Horizontal Section
const sections = gsap.utils.toArray(".panel");
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".horizontal-container",
pin: true,
scrub: 1,
snap: 1 / (sections.length - 1),
end: () =>
"+=" + document.querySelector(".horizontal-container").offsetWidth,
},
});
Markers (Development)
ScrollTrigger.create({
trigger: ".section",
start: "top center",
end: "bottom center",
markers: true,
});
Batch Animations
Stagger on Scroll
ScrollTrigger.batch(".card", {
onEnter: (elements) => {
gsap.from(elements, {
opacity: 0,
y: 50,
stagger: 0.1,
duration: 0.5,
});
},
start: "top 85%",
});
Batch Configuration
ScrollTrigger.batch(".item", {
interval: 0.1,
batchMax: 3,
onEnter: (batch) => gsap.to(batch, { opacity: 1, y: 0, stagger: 0.1 }),
onLeave: (batch) => gsap.to(batch, { opacity: 0, y: 20 }),
onEnterBack: (batch) => gsap.to(batch, { opacity: 1, y: 0 }),
onLeaveBack: (batch) => gsap.to(batch, { opacity: 0, y: -20 }),
});
Common Patterns
Reveal on Scroll
gsap.utils.toArray(".reveal").forEach((elem) => {
gsap.from(elem, {
opacity: 0,
y: 50,
duration: 0.8,
scrollTrigger: {
trigger: elem,
start: "top 80%",
toggleActions: "play none none none",
},
});
});
Progress Indicator
gsap.to(".progress-bar", {
scaleX: 1,
transformOrigin: "left center",
ease: "none",
scrollTrigger: {
trigger: "body",
start: "top top",
end: "bottom bottom",
scrub: 0.3,
},
});
Sticky Header Transform
ScrollTrigger.create({
start: "top -80",
onUpdate: (self) => {
if (self.direction === 1) {
gsap.to(".header", { y: -80, duration: 0.3 });
} else {
gsap.to(".header", { y: 0, duration: 0.3 });
}
},
});
Temporal Collapse Patterns
Countdown Scroll Reveal
const sections = ["days", "hours", "minutes", "seconds"];
sections.forEach((section, i) => {
gsap.from(`.countdown-${section}`, {
opacity: 0,
scale: 0.8,
y: 50,
duration: 0.8,
ease: "power3.out",
scrollTrigger: {
trigger: `.countdown-${section}`,
start: "top 80%",
toggleActions: "play none none none",
},
});
});
Scroll-Based Time Dilation Effect
gsap
.timeline({
scrollTrigger: {
trigger: ".time-section",
start: "top center",
end: "bottom center",
scrub: 1,
},
})
.to(".time-digit", {
textShadow: "0 0 50px #FFFFFF, 0 0 100px #FFFFFF",
scale: 1.1,
})
.to(
".particles",
{
opacity: 1,
filter: "blur(0px)",
},
"<",
);
Performance Tips
ScrollTrigger.matchMedia({
'(min-width: 768px)': function() {
},
'(max-width: 767px)': function() {
}
});
ScrollTrigger.refresh();
ScrollTrigger.killAll();
const st = ScrollTrigger.create({ ... });
st.kill();