How to Create Scrolling Sticky Sections with GSAP and Webflow CMS

8.27.2024
by
Web Bae

In this step-by-step guide, we’ll be creating sticky sections in Webflow, and animating them with GSAP's ScrollTrigger plugin. You’ll learn how to create smooth scrolling transitions that animate elements as they become sticky on the page. We’ll also explore how to modify and fine-tune the animations for a dynamic, interactive experience.

DEMO/CLONE
CODE
TUTORIAL

See the Pen Splide Slider by learyjk (@learyjk) on CodePen.

Step 1: Webflow Setup

Before we dive into the code, we need to structure our Webflow project. Here’s the basic setup:

  1. Sticky List Container: This is a CMS collection list set to position: relative.
  2. Sticky Sections: Each CMS item (section) is given a minimum height of 100vh (viewport height) and position: sticky with the top property set to 0.

This ensures that each section will stick to the top of the viewport as the user scrolls.

Step 2: Importing GSAP and ScrollTrigger

We'll be using GSAP (GreenSock Animation Platform) for our animations, along with its ScrollTrigger plugin to manage scroll-based interactions.

Here's how you import these libraries via the Skypack CDN:

import { gsap } from "https://cdn.skypack.dev/gsap";
import { ScrollTrigger } from "https://cdn.skypack.dev/gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);


Make sure to include this at the top of your JavaScript file to load the necessary libraries.

Step 3: Querying the Sticky Sections

Once the GSAP libraries are set up, the next step is to grab all the sticky sections (panels) from the DOM. We'll use querySelectorAll to select them by class name and convert the NodeList into an array using Array.from():

const panels = Array.from(document.querySelectorAll(".sticky-section_panel"));

Step 4: Creating the Scroll Animation

Now, we need to create a scroll animation that triggers when the user scrolls to a specific panel. We’ll do this inside a forEach loop that iterates through each panel:

panels.forEach((panel, index) => {
  const isLast = index === panels.length - 1;

  gsap
    .timeline({
      scrollTrigger: {
        trigger: panel,
        start: "top top",
        scrub: true,
      },
    })
    // Animate panel
    .to(
      panel,
      {
        ease: "none",
        startAt: { filter: "brightness(100%) blur(0px)" },
        filter: isLast ? "none" : "brightness(50%) blur(10px)",
        scale: 0.9,
        borderRadius: 40,
      },
      "<"
    );
});


Explanation:

  • Timeline and ScrollTrigger: We create a gsap.timeline() for each panel and configure a scrollTrigger object. This tells GSAP to trigger the animation when the panel reaches the top of the viewport.
  • Scrub: The scrub: true option gives a smooth lag effect, so the animation is tied to the scroll progress.

Step 5: Applying Custom Animations to Each Panel

We want to add a nice fade and blur effect as each sticky section scrolls into view. For each panel, we animate its brightness, blur, scale, and borderRadius. The first panel fades out as the next one appears on top.

.to(
  panel,
  {
    ease: "none",
    startAt: { filter: "brightness(100%) blur(0px)" },
    filter: isLast ? "none" : "brightness(50%) blur(10px)",
    scale: 0.9,
    borderRadius: 40,
  },
  "<"
);

This code animates the panels by:

  • Reducing the brightness to 50%
  • Adding a 10px blur
  • Scaling the panel down to 90% of its original size
  • Applying a 40px border-radius

These effects won’t be applied to the last panel, as it remains static.

Step 6: Adding More Dynamic Animations

Let’s spice things up by adding a second animation that affects the image inside each panel. We’ll add rotation and vertical movement to the images for a dynamic scrolling effect.

Here’s how we do that:

panels.forEach((panel, index) => {
  const isLast = index === panels.length - 1;

  gsap
    .timeline({
      scrollTrigger: {
        trigger: panel,
        start: "top top",
        scrub: true,
      },
    })
    .to(
      panel,
      {
        ease: "none",
        startAt: { filter: "brightness(100%) blur(0px)" },
        filter: isLast ? "none" : "brightness(50%) blur(10px)",
        scale: 0.9,
        borderRadius: 40,
      },
      "<"
    )
    // Animate the image inside the panel
    .to(
      panel.querySelector(".sticky-section_image"),
      {
        yPercent: -40,
        rotation: index % 2 === 0 ? 20 : -20,
        ease: "power1.inOut",
      },
      "<"
    );
});

Explanation:

  • Image Animation: We use yPercent to move the image up by 40% as it scrolls, and rotation to rotate the image by 20 degrees to the left or right depending on the index. This creates a fun alternating effect.
  • Timing: The animations are chained together using the "<" symbol, so they all happen in sync.

Step 7: Final Touches and Testing

Once all the animations are in place, you can tweak the easing functions, timing, and other properties to fit your design. To test your project, simply scroll through your page and see the sticky sections come to life.

Here’s the full code:

import { gsap } from "https://cdn.skypack.dev/gsap";
import { ScrollTrigger } from "https://cdn.skypack.dev/gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

const scroll = () => {
  const panels = Array.from(document.querySelectorAll(".sticky-section_panel"));

  panels.forEach((panel, index) => {
    const isLast = index === panels.length - 1;

    gsap
      .timeline({
        scrollTrigger: {
          trigger: panel,
          start: "top top",
          scrub: true,
        },
      })
      // Animate panel
      .to(
        panel,
        {
          ease: "none",
          startAt: { filter: "brightness(100%) blur(0px)" },
          filter: isLast ? "none" : "brightness(50%) blur(10px)",
          scale: 0.9,
          borderRadius: 40,
        },
        "<"
      )
      // Animate the image inside the panel
      .to(
        panel.querySelector(".sticky-section_image"),
        {
          yPercent: -40,
          rotation: index % 2 === 0 ? 20 : -20,
          ease: "power1.inOut",
        },
        "<"
      );
  });
};

document.addEventListener("DOMContentLoaded", () => {
  scroll();
});

Conclusion

With just a few lines of GSAP and ScrollTrigger, you can add smooth, visually engaging animations to your Webflow projects. Experiment with different animation properties and timing to create unique, interactive experiences. Be sure to clone the project from the Webflow template and tweak the settings for your next project.

Related Topics

If you are just getting started with GSAP + ScrollTrigger, be sure to check out this post to get an introduction to how it works. If it's your first time ever working with GSAP, then this post will surely help as well! I love GSAP because you can animate anything with it, even crazy stuff like Clip Path. People rave about Clip Path Bae, used for easily creating clip path shapes in Webflow.