ScrollTrigger is GSAP's scroll-based animation plugin that triggers animations based on scroll position. It provides precise control over when animations start and end based on element visibility and scroll progress.
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);Create scroll-based animation triggers with comprehensive configuration options.
/**
* Create a ScrollTrigger instance
* @param vars - ScrollTrigger configuration
* @returns ScrollTrigger instance
*/
ScrollTrigger.create(vars: ScrollTrigger.Vars): ScrollTrigger;
interface ScrollTrigger.Vars {
// Core properties
trigger?: Element | string; // Element that triggers the animation
start?: string | number | Function; // When animation starts
end?: string | number | Function; // When animation ends
// Animation
animation?: gsap.core.Animation; // Animation to control
toggleActions?: string; // Actions for enter/leave/enterBack/leaveBack
// Callbacks
onEnter?: Function; // Called when entering trigger area
onLeave?: Function; // Called when leaving trigger area
onEnterBack?: Function; // Called when entering from below
onLeaveBack?: Function; // Called when leaving upward
onUpdate?: Function; // Called on scroll update
onToggle?: Function; // Called on any toggle
onRefresh?: Function; // Called when refreshed
// Scrubbing
scrub?: boolean | number; // Link animation progress to scroll
// Visual helpers
markers?: boolean | object; // Show visual markers for debugging
// Advanced
pin?: boolean | Element | string; // Pin element during scroll
pinSpacing?: boolean; // Add spacing for pinned elements
snap?: number | object; // Snap to specific scroll positions
// Responsive
refreshPriority?: number; // Refresh priority order
invalidateOnRefresh?: boolean; // Invalidate animation on refresh
}Usage Examples:
// Basic ScrollTrigger
ScrollTrigger.create({
trigger: ".box",
start: "top 80%", // When top of .box hits 80% of viewport
end: "bottom 20%", // When bottom of .box hits 20% of viewport
animation: gsap.to(".box", { x: 100, duration: 1 }),
toggleActions: "play none none reverse"
});
// With scrubbing (animation linked to scroll progress)
ScrollTrigger.create({
trigger: ".section",
start: "top bottom",
end: "bottom top",
animation: gsap.to(".element", { rotation: 360, duration: 1 }),
scrub: true // Animation progress matches scroll progress
});Control animation behavior when entering/leaving the trigger area.
// Toggle actions format: "onEnter onLeave onEnterBack onLeaveBack"
type ToggleActions = string; // e.g., "play pause resume reverse"
// Available actions:
// "play" - Play the animation
// "pause" - Pause the animation
// "resume" - Resume the animation
// "reverse" - Reverse the animation
// "restart" - Restart the animation
// "reset" - Reset to beginning
// "complete" - Jump to completion
// "none" - Do nothingUsage Examples:
ScrollTrigger.create({
trigger: ".box",
start: "top 50%",
end: "bottom 50%",
animation: gsap.to(".box", { x: 100, duration: 1 }),
toggleActions: "play reverse play reverse" // Play on enter, reverse on leave
});
// Different behaviors for each direction
ScrollTrigger.create({
trigger: ".element",
animation: gsap.from(".element", { opacity: 0, y: 50, duration: 1 }),
toggleActions: "play none none reset" // Play once, reset when scrolling back up
});Link animation progress directly to scroll position for smooth, scroll-driven animations.
interface ScrubConfig {
scrub?: boolean | number; // true, false, or lag amount (0.1-3)
}Usage Examples:
// Direct scrubbing (no lag)
ScrollTrigger.create({
trigger: ".section",
start: "top bottom",
end: "bottom top",
scrub: true,
animation: gsap.to(".parallax", { y: -200, ease: "none", duration: 1 })
});
// Smooth scrubbing with lag
ScrollTrigger.create({
trigger: ".hero",
start: "top bottom",
end: "bottom top",
scrub: 1, // 1 second of lag for smooth effect
animation: gsap.to(".hero-bg", { scale: 1.2, ease: "none", duration: 1 })
});Pin elements in place during scroll for immersive scroll experiences.
interface PinConfig {
pin?: boolean | Element | string; // Element to pin
pinSpacing?: boolean; // Whether to add spacing
pinType?: "fixed" | "transform"; // Pinning method
anticipatePin?: number; // Anticipate pin by this amount
}Usage Examples:
// Pin an element
ScrollTrigger.create({
trigger: ".panel",
start: "top top",
end: "+=500", // Pin for 500px of scroll
pin: true,
animation: gsap.to(".panel .content", { rotation: 360, duration: 1 }),
scrub: true
});
// Pin without spacing (overlap next content)
ScrollTrigger.create({
trigger: ".sticky-section",
start: "top top",
end: "bottom bottom",
pin: ".sticky-element",
pinSpacing: false
});Create multiple ScrollTriggers efficiently with batch processing.
/**
* Create ScrollTriggers for multiple elements efficiently
* @param targets - Elements to create ScrollTriggers for
* @param vars - Configuration applied to all targets
* @returns Array of ScrollTrigger instances
*/
ScrollTrigger.batch(
targets: string | Element | Element[],
vars: ScrollTrigger.Vars
): ScrollTrigger[];Usage Examples:
// Batch animate multiple elements
ScrollTrigger.batch(".fade-in", {
onEnter: (elements) => gsap.from(elements, {
opacity: 0,
y: 50,
duration: 1,
stagger: 0.1
}),
onLeave: (elements) => gsap.to(elements, {
opacity: 0,
y: -50,
duration: 0.5,
stagger: 0.1
}),
onEnterBack: (elements) => gsap.to(elements, {
opacity: 1,
y: 0,
duration: 1,
stagger: 0.1
})
});Global ScrollTrigger management and utility methods.
/**
* Refresh all ScrollTriggers (recalculate positions)
*/
ScrollTrigger.refresh(): void;
/**
* Update all ScrollTriggers (check current scroll position)
*/
ScrollTrigger.update(): void;
/**
* Kill all ScrollTriggers
* @param revert - Whether to revert animations
*/
ScrollTrigger.killAll(revert?: boolean): void;
/**
* Get all ScrollTrigger instances
* @returns Array of all ScrollTriggers
*/
ScrollTrigger.getAll(): ScrollTrigger[];
/**
* Get ScrollTrigger by ID
* @param id - ScrollTrigger ID
* @returns ScrollTrigger instance or undefined
*/
ScrollTrigger.getById(id: string): ScrollTrigger | undefined;
/**
* Set global ScrollTrigger configuration
* @param config - Global configuration options
*/
ScrollTrigger.config(config: {
limitCallbacks?: boolean;
syncInterval?: number;
ignoreMobileResize?: boolean;
autoRefreshEvents?: string;
}): void;Usage Examples:
// Refresh after DOM changes
ScrollTrigger.refresh();
// Force update scroll position
ScrollTrigger.update();
// Clean up all ScrollTriggers
ScrollTrigger.killAll();
// Get all instances for debugging
const allTriggers = ScrollTrigger.getAll();
console.log(`Active ScrollTriggers: ${allTriggers.length}`);
// Global configuration
ScrollTrigger.config({
limitCallbacks: true, // Limit callback frequency for performance
ignoreMobileResize: true // Don't refresh on mobile resize
});Responsive ScrollTrigger management with media query support.
/**
* Create media query-based ScrollTriggers
* @param config - Media query configuration
* @returns MatchMedia instance
*/
ScrollTrigger.matchMedia(config: {
[query: string]: Function;
}): gsap.MatchMedia;
/**
* Clear media query ScrollTriggers
* @param query - Specific query to clear (optional)
*/
ScrollTrigger.clearMatchMedia(query?: string): void;Usage Examples:
// Responsive ScrollTriggers
ScrollTrigger.matchMedia({
// Desktop
"(min-width: 768px)": function() {
ScrollTrigger.create({
trigger: ".desktop-element",
start: "top 50%",
animation: gsap.to(".desktop-element", { x: 100, duration: 1 })
});
},
// Mobile
"(max-width: 767px)": function() {
ScrollTrigger.create({
trigger: ".mobile-element",
start: "top 80%",
animation: gsap.to(".mobile-element", { y: 50, duration: 1 })
});
}
});
// Clear mobile queries when no longer needed
ScrollTrigger.clearMatchMedia("(max-width: 767px)");Properties and methods available on ScrollTrigger instances.
interface ScrollTrigger {
// Properties
trigger: Element; // Trigger element
start: number; // Start position in pixels
end: number; // End position in pixels
progress: number; // Current progress (0-1)
direction: number; // Scroll direction (1 or -1)
isActive: boolean; // Whether currently active
// Methods
kill(revert?: boolean): void; // Destroy the ScrollTrigger
refresh(): void; // Recalculate positions
update(force?: boolean): void; // Update current state
enable(): void; // Enable the ScrollTrigger
disable(): void; // Disable the ScrollTrigger
getVelocity(): number; // Get scroll velocity
}Usage Examples:
const st = ScrollTrigger.create({
trigger: ".section",
start: "top 50%",
onUpdate: self => {
console.log(`Progress: ${self.progress}`);
console.log(`Direction: ${self.direction > 0 ? "down" : "up"}`);
console.log(`Velocity: ${self.getVelocity()}`);
}
});
// Control instance
st.disable(); // Temporarily disable
st.enable(); // Re-enable
st.refresh(); // Recalculate if layout changed
st.kill(); // Destroy when no longer neededUnderstanding start and end position syntax for precise trigger control.
// Position format: "trigger-position viewport-position"
// Trigger positions: "top", "center", "bottom", pixel values, percentages
// Viewport positions: "top", "center", "bottom", pixel values, percentages
type PositionString = string; // Examples:
// "top top" - Top of trigger hits top of viewport
// "top center" - Top of trigger hits center of viewport
// "center 80%" - Center of trigger hits 80% down viewport
// "bottom+=100 top" - 100px below bottom of trigger hits top of viewportUsage Examples:
ScrollTrigger.create({
trigger: ".element",
start: "top 80%", // Animation starts when top of element hits 80% of viewport
end: "bottom 20%", // Animation ends when bottom of element hits 20% of viewport
animation: gsap.to(".element", { opacity: 1, duration: 1 })
});
// Using pixel offsets
ScrollTrigger.create({
trigger: ".section",
start: "top+=50 center", // 50px below top of section hits center of viewport
end: "bottom-=100 top", // 100px above bottom of section hits top of viewport
scrub: true,
animation: gsap.to(".bg", { y: -100, ease: "none", duration: 1 })
});