Back

Bring Your UI to Life with ScrollTrigger Animations in GSAP

Bring Your UI to Life with ScrollTrigger Animations in GSAP

Scroll-based animations can transform a static website into an engaging, interactive experience. While CSS animations are useful for basic effects, creating sophisticated scroll-driven animations requires a more powerful tool. GSAP’s ScrollTrigger plugin is exactly that tool—letting you create polished, performant animations triggered by scroll position.

In this guide, you’ll learn how to implement ScrollTrigger animations that respond naturally to user scrolling, with practical examples you can use in your projects today.

Key Takeaways

  • ScrollTrigger connects animations to scroll position for interactive experiences
  • Use scrub to tie animation progress directly to scroll position
  • Pin elements to create advanced scroll-based effects
  • Configure start and end to precisely control when animations trigger
  • Use markers during development to visualize trigger points

What is ScrollTrigger and Why Use It?

ScrollTrigger is a GSAP plugin that connects animations to the scroll position. Unlike basic “on-scroll” libraries that simply trigger animations when elements enter the viewport, ScrollTrigger offers precise control over:

  • When animations start and end based on scroll position
  • How animations progress as users scroll (scrubbing)
  • Pinning elements while users scroll past them
  • Creating complex scroll-based interactions

The result? Animations that feel connected to user scrolling rather than just playing when triggered.

Getting Started with ScrollTrigger

First, let’s set up the basics:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ScrollTrigger Demo</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            font-family: Arial, sans-serif;
        }
        
        section {
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        .spacer {
            height: 100vh;
        }
        
        .box {
            width: 200px;
            height: 200px;
            background-color: #3498db;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <div class="spacer"></div>
    
    <section>
        <div class="box"></div>
    </section>
    
    <div class="spacer"></div>
    
    <!-- GSAP and ScrollTrigger -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
    
    <script>
        // Register the plugin
        gsap.registerPlugin(ScrollTrigger);
        
        // Your animations will go here
    </script>
</body>
</html>

Basic ScrollTrigger Animation

Let’s start with a simple animation that triggers when an element enters the viewport:

gsap.to(".box", {
    scrollTrigger: ".box", // Element that triggers the animation
    x: 300, // Move 300px to the right
    rotation: 360, // Rotate 360 degrees
    duration: 1.5, // Animation duration
    ease: "power2.out" // Easing function
});

This code moves and rotates the box when it enters the viewport. But this is just scratching the surface.

Understanding ScrollTrigger Configuration

To unlock ScrollTrigger’s full potential, we need to understand its configuration options:

gsap.to(".box", {
    scrollTrigger: {
        trigger: ".box", // Element that triggers the animation
        start: "top center", // Start when top of box hits center of viewport
        end: "bottom center", // End when bottom of box hits center of viewport
        toggleActions: "play pause reverse reset", // Actions on enter, leave, enter back, leave back
        markers: true, // Shows markers for debugging (remove in production)
    },
    x: 300,
    rotation: 360,
    duration: 2
});

The start and end properties define when the animation activates and deactivates. The format is "[trigger element position] [viewport position]".

toggleActions controls how the animation behaves at four key moments:

  1. When entering the trigger area
  2. When leaving the trigger area
  3. When entering the trigger area again while scrolling up
  4. When leaving the trigger area while scrolling up

Options include: play, pause, resume, reverse, restart, reset, complete, and none.

Creating Scroll-Driven Animations with Scrub

The real magic happens with the scrub property, which ties animation progress directly to scroll position:

gsap.to(".box", {
    scrollTrigger: {
        trigger: ".box",
        start: "top center",
        end: "bottom center",
        scrub: true, // Links animation progress to scroll position
        markers: true
    },
    x: 300,
    rotation: 360,
    backgroundColor: "#e74c3c"
});

With scrub: true, the animation progresses as the user scrolls, and even reverses when scrolling back up. For smoother animation, use a number value like scrub: 0.5 to add a slight delay.

Pinning Elements During Scroll

One of ScrollTrigger’s most powerful features is pinning elements in place while the user scrolls:

gsap.to(".box", {
    scrollTrigger: {
        trigger: ".box",
        start: "center center",
        end: "+=300", // End 300px after the start position
        pin: true, // Pin the box in place during the animation
        scrub: 1,
        markers: true
    },
    x: 300,
    rotation: 360,
    scale: 1.5,
    backgroundColor: "#9b59b6"
});

This pins the box in place while the animation plays, creating a parallax-like effect. The end: "+=300" means the animation ends after scrolling 300 pixels beyond the start point.

Creating a Reveal Animation

Let’s create a practical reveal animation for text or images:

<div class="spacer"></div>

<section class="reveal-section">
    <div class="reveal-container">
        <h1 class="reveal-text">Scroll-Driven Animations</h1>
        <p class="reveal-text">Create engaging user experiences with GSAP ScrollTrigger</p>
    </div>
</section>

<div class="spacer"></div>
.reveal-section {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

.reveal-container {
    max-width: 800px;
    text-align: center;
    overflow: hidden;
}

.reveal-text {
    opacity: 0;
    transform: translateY(50px);
}
// Reveal animation
gsap.utils.toArray('.reveal-text').forEach(text => {
    gsap.to(text, {
        scrollTrigger: {
            trigger: text,
            start: "top 80%", // Start when top of text is 80% from top of viewport
            toggleActions: "play none none none"
        },
        y: 0,
        opacity: 1,
        duration: 1,
        ease: "power2.out"
    });
});

This creates a clean reveal effect as each text element enters the viewport.

Creating a Parallax Effect

Parallax effects add depth to your website. Here’s how to create one:

<div class="parallax-container">
    <div class="parallax-bg"></div>
    <div class="parallax-content">
        <h1>Parallax Effect</h1>
    </div>
</div>
.parallax-container {
    height: 100vh;
    position: relative;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
}

.parallax-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 120%; /* Extra height for movement */
    background-image: url('your-background-image.jpg');
    background-size: cover;
    background-position: center;
}

.parallax-content {
    position: relative;
    z-index: 1;
    color: white;
    text-align: center;
}
// Parallax effect
gsap.to(".parallax-bg", {
    scrollTrigger: {
        trigger: ".parallax-container",
        start: "top bottom",
        end: "bottom top",
        scrub: true
    },
    y: -100, // Move background up by 100px as we scroll
    ease: "none"
});

This creates a simple parallax effect where the background moves at a different rate than the foreground.

Horizontal Scrolling Section

Creating a horizontal scrolling section is another impressive effect:

<div class="spacer"></div>

<section class="horizontal-scroll">
    <div class="horizontal-container">
        <div class="panel">Panel 1</div>
        <div class="panel">Panel 2</div>
        <div class="panel">Panel 3</div>
        <div class="panel">Panel 4</div>
    </div>
</section>

<div class="spacer"></div>
.horizontal-scroll {
    overflow: hidden;
    height: 100vh;
}

.horizontal-container {
    display: flex;
    width: 400%; /* 100% * number of panels */
    height: 100%;
}

.panel {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2rem;
}

.panel:nth-child(1) { background-color: #3498db; }
.panel:nth-child(2) { background-color: #2ecc71; }
.panel:nth-child(3) { background-color: #e74c3c; }
.panel:nth-child(4) { background-color: #9b59b6; }
// Horizontal scroll
gsap.to(".horizontal-container", {
    scrollTrigger: {
        trigger: ".horizontal-scroll",
        start: "top top",
        end: "+=3000", // Scroll distance
        pin: true,
        scrub: 1,
    },
    x: () => -(document.querySelector(".horizontal-container").offsetWidth - window.innerWidth),
    ease: "none"
});

This creates a section that scrolls horizontally as the user scrolls vertically.

Performance Optimization Tips

ScrollTrigger animations can impact performance if not implemented carefully. Here are some tips:

  1. Use will-change sparingly: Only apply to elements that actually animate
  2. Avoid animating layout properties: Stick to transform and opacity when possible
  3. Batch similar animations: Use gsap.utils.toArray() and loop through elements
  4. Kill ScrollTriggers when not needed: Use scrollTrigger.kill() for single-page applications
  5. Reduce marker usage: Remove markers: true in production

Troubleshooting Common Issues

Animation Starts Too Early/Late

If your animation triggers at unexpected times, check your start and end values. Use markers: true to visualize trigger points.

Jerky Animations

For smoother animations, use scrub: 0.5 or higher instead of scrub: true to add a slight delay.

Mobile Compatibility Issues

Mobile browsers handle scroll events differently. Test thoroughly on mobile devices and consider using ScrollTrigger.matchMedia() to create different animations for different screen sizes.

Conclusion

With these techniques, you can create scroll animations that respond naturally to user interaction, enhancing your UI without overwhelming users. Start with simple effects and gradually incorporate more advanced techniques as you become comfortable with ScrollTrigger’s capabilities.

FAQs

Yes, but you need to properly set up and clean up ScrollTrigger instances when components mount/unmount.

ScrollTrigger is designed for GSAP, but you can use its callback functions to trigger other libraries.

Use percentages for positioning, and refresh ScrollTrigger on window resize with ScrollTrigger.refresh().

Basic functionality is free, but some advanced features require a GreenSock Club membership.

Use markers: true to visualize trigger points and console.log in callback functions to track progress.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers