Claude-skill-registry gsap-scrolltrigger
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.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gsap-scrolltrigger" ~/.claude/skills/majiayu000-claude-skill-registry-gsap-scrolltrigger && rm -rf "$T"
manifest:
skills/data/gsap-scrolltrigger/SKILL.mdsource content
GSAP ScrollTrigger
Scroll-driven animations and interactions.
Quick Start
npm 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', // Element that triggers the animation start: 'top center', // When trigger hits viewport center end: 'bottom center', // When trigger leaves viewport center toggleActions: 'play pause resume reset' } });
Start/End Positions
// Format: "trigger-position viewport-position" start: 'top center' // Trigger's top hits viewport center start: 'top 80%' // Trigger's top hits 80% down viewport start: 'center center' // Trigger's center hits viewport center start: 'bottom top' // Trigger's bottom hits viewport top start: 'top top+=100' // Trigger's top hits 100px below viewport top
Position Reference
| Value | Description |
|---|---|
| Top edge |
| Center |
| Bottom edge |
| 80% from top |
| Plus 100 pixels |
| Minus 50 pixels |
Scrub Animations
Basic Scrub
// Animation progress tied to scroll position gsap.to('.progress-bar', { scaleX: 1, scrollTrigger: { trigger: '.content', start: 'top top', end: 'bottom bottom', scrub: true // Directly linked to scroll } });
Smooth Scrub
gsap.to('.element', { x: 500, scrollTrigger: { trigger: '.section', scrub: 1, // 1 second smoothing // scrub: 0.5 // 0.5 second smoothing // scrub: 2 // 2 second smoothing (laggy feel) } });
Scrub with Timeline
const tl = gsap.timeline({ scrollTrigger: { trigger: '.container', start: 'top top', end: '+=3000', // Scroll distance 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 for 500px of scroll 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, // Default: adds space for pinned duration // pinSpacing: false // No extra space (content overlaps) // pinSpacing: '500px' // Custom spacing });
Toggle Actions
Action Syntax
// Format: "onEnter onLeave onEnterBack onLeaveBack" toggleActions: 'play pause resume reset' // Common combinations: toggleActions: 'play none none none' // Play once toggleActions: 'play reverse play reverse' // Toggle direction toggleActions: 'restart none none none' // Restart each time toggleActions: 'play complete reverse reset'
Action Values
| Action | Effect |
|---|---|
| Play forward |
| Pause |
| Resume from paused |
| Play backward |
| Restart from beginning |
| Reset to start (no animation) |
| Jump to end |
| Do nothing |
Snap Points
Basic Snap
ScrollTrigger.create({ trigger: '.sections', start: 'top top', end: 'bottom bottom', snap: 1 / 4 // Snap to quarters });
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], // Snap to specific points duration: { min: 0.2, max: 0.6 }, // Snap duration range delay: 0, // Delay before snap ease: 'power1.inOut', // Snap easing directional: true // Snap in scroll direction }
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) => { // self.progress: 0 to 1 // self.direction: 1 (down) or -1 (up) // self.velocity: scroll speed updateElement(self.progress); } });
Parallax Effects
Basic Parallax
// Background moves slower than scroll gsap.to('.background', { yPercent: -50, ease: 'none', scrollTrigger: { trigger: '.section', scrub: true } }); // Foreground moves faster 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, // Show visual markers // markers: { startColor: 'green', endColor: 'red', fontSize: '12px' } });
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, // Time between batches batchMax: 3, // Max items per batch 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
// Reveal countdown sections as user scrolls 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 #00F5FF, 0 0 100px #00F5FF', scale: 1.1 }) .to('.particles', { opacity: 1, filter: 'blur(0px)' }, '<');
Performance Tips
// Disable on mobile if needed ScrollTrigger.matchMedia({ '(min-width: 768px)': function() { // Desktop animations }, '(max-width: 767px)': function() { // Simpler mobile animations } }); // Refresh on resize ScrollTrigger.refresh(); // Kill all ScrollTriggers ScrollTrigger.killAll(); // Kill specific trigger const st = ScrollTrigger.create({ ... }); st.kill();
Reference
- See
for animation basicsgsap-fundamentals - See
for timeline compositiongsap-sequencing - See
for React integration with ScrollTriggergsap-react