Webflow CMS Sliders: Building a Custom Full-Screen Slideshow with GSAP

8.17.2024
by
Web Bae

In Webflow, creating a slider can be a challenge. You might turn to popular JavaScript libraries like SwiperJS or Splide, or even vent your frustrations on Twitter. But there's another, more empowering option: building your own custom slider from scratch using vanilla JavaScript and GSAP (GreenSock Animation Platform). In this post, I'll walk you through how to create a full-screen slideshow with smooth animations and a parallax effect.

DEMO/CLONE
TUTORIAL
CODE

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

Acknowledgements:

Overview

We'll be building a slideshow with custom next and previous buttons. The slideshow will feature a smooth sliding animation as well as a parallax effect that adds a subtle shift in the background images. By the end, you’ll have a reusable slider that behaves exactly how you want it.

Step 1: Set Up Your HTML Structure

Let’s start with the basic HTML structure for our slider. We’ll use a container (.slider) that holds multiple .slide elements, each containing an image.

<div class="slider">
  <div class="slide"><img src="image1.jpg" /></div>
  <div class="slide"><img src="image2.jpg" /></div>
  <div class="slide"><img src="image3.jpg" /></div>
  <!-- Add more slides as needed -->
</div>

<button class="is-prev">Previous</button>
<button class="is-next">Next</button>
  • .slider: This is the main container that holds all of the .slide elements.
  • .slide: Each slide contains an image.
  • .is-prev and .is-next: These buttons allow users to navigate between slides.

Step 2: Styling the Slider and Slides

Now let’s apply some basic CSS to control the appearance of the slides and handle their visibility during the transitions.

.slide {
  opacity: 0; /* Hidden by default */
}

.slide.is-current {
  opacity: 1 !important; /* The current slide is visible */
  pointer-events: auto; /* Allow interaction */
}
  • .slide: Each slide starts off invisible by setting the opacity to 0.
  • .is-current: This class is dynamically added to the slide that is currently active, making it visible by overriding the opacity.

Step 3: Introducing GSAP for Smooth Animations

We’ll now add the functionality to make the slides transition smoothly between each other using GSAP, one of the most powerful animation libraries available.

First, we import GSAP from a CDN:

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

Step 4: Creating the Slideshow Class

We’ll encapsulate all of the slider functionality in a Slideshow class. This class will manage the DOM elements, track the current slide, and handle navigation and animations.

const TRANSITION_DURATION = 0.8; // slide transition time in seconds
const PARALLAX_AMOUNT = 30; // percentage parallax overlap

class Slideshow {
  constructor(slider) {
    // Initialize DOM elements
    this.slider = slider;
    this.slides = Array.from(this.slider.querySelectorAll('.slide'));
    this.slidesInner = this.slides.map((item) => item.querySelector('img'));

    this.current = 0; // Track the current slide index
    this.slidesTotal = this.slides.length; // Total number of slides
    this.isAnimating = false; // Prevents triggering animations multiple times

    // Set the initial slide
    this.slides[this.current].classList.add('is-current');
  }
}
  • DOM Initialization: The constructor initializes key DOM elements, including the slider and the individual slides. It also keeps track of the current slide and checks whether an animation is in progress.
  • Initial State: The first slide (this.current) is made visible by adding the is-current class.

Step 5: Handling Navigation

We add the navigate function inside of the Slideshow class, which determines which slide to show next based on the direction (either forward or backward). It also wraps the navigation when reaching the first or last slide.

  // inside the Slideshow class, after the constructor
  navigate(direction) {
    // Exit if an animation is already in progress
    if (this.isAnimating) return false;

    this.isAnimating = true; // Lock animations

    const previous = this.current;

    // Update the current slide index based on direction
    if (direction === 1) {
    this.current = (this.current < this.slidesTotal - 1) ? this.current + 1 : 0;
    } else {
    this.current = (this.current > 0) ? this.current - 1 : this.slidesTotal - 1;
    }

    // Trigger animation
    this.animate(previous, this.current, direction);
  }
  • Direction Logic: The function increments or decrements the current slide index based on the direction. It also handles wrapping from the last slide to the first (and vice versa).

Step 6: Animating the Transition

The animate method uses GSAP to handle the transition between the current slide and the upcoming slide. It includes a parallax effect for added depth.

  // ...
  // inside the Slideshow class, after the navigate function
  animate(current, upcoming, direction) {
    const currentSlide = this.slides[current];
    const currentInner = this.slidesInner[current];
    const upcomingSlide = this.slides[upcoming];

    gsap
      .timeline({
        defaults: {
          duration: TRANSITION_DURATION,
          ease: 'power4.inOut',
        },
        onStart: () => {
          upcomingSlide.classList.add('is-current');
          gsap.set(upcomingSlide, { zIndex: 1 });
        },
        onComplete: () => {
          currentSlide.classList.remove('is-current');
          gsap.set(currentSlide, { xPercent: 0 });
          gsap.set(currentInner, { xPercent: 0 });
          gsap.set(upcomingSlide, { zIndex: 'auto' });
          this.isAnimating = false;
        },
      })
      .to(currentSlide, {
        xPercent: -direction * 100, // Move the current slide out
      })
      .to(
        currentInner,
        {
          xPercent: direction * PARALLAX_AMOUNT, // Parallax effect on the image
        },
        '<' // Start simultaneously
      )
      .fromTo(
        upcomingSlide,
        {
          xPercent: direction * 100, // Start the upcoming slide off-screen
        },
        {
          xPercent: 0, // Slide it into place
        },
        '<'
      );
  }
} // close the class here
  • Timeline Setup: GSAP’s timeline orchestrates the animations. The current slide moves out, and the upcoming slide moves in simultaneously.
  • Parallax Effect: The image inside the slide is given a slight parallax effect, shifting by a percentage to give the illusion of depth.
  • Resetting States: Once the animation is complete, the states are reset so the slider is ready for the next transition.

Step 7: Connecting the Navigation Buttons

Finally, we tie everything together by adding event listeners to the navigation buttons, so they can trigger the navigate function in the correct direction.

const slider = document.querySelector('.slider');
const slideshow = new Slideshow(slider);

document.querySelector('.is-prev').addEventListener('click', () => slideshow.navigate(-1));
document.querySelector('.is-next').addEventListener('click', () => slideshow.navigate(1));
  • Navigation Buttons: The is-prev button moves to the previous slide, and the is-next button moves to the next slide.

Conclusion

Building a custom slider in Webflow using vanilla JavaScript and GSAP gives you full control over the design and functionality. This method not only saves you from the limitations of pre-built sliders but also provides a flexible, highly customizable solution that you can adapt to various projects. Plus, the addition of parallax effects and smooth animations elevates the user experience to a new level.

By understanding the logic and code behind this slideshow, you can start creating even more complex and engaging interactions in Webflow.

Happy building, and welcome to the world of custom sliders!