javascriptgsapscrolltrigger

GSAP and ScrollTrigger sections overlapping and executing before previous section has finished


I have a problem that keeps popping up with GSAP and ScrollTrigger.

I have multiple pinned sections where, as the user scrolls into a section, images slide in from the side.

When I make these images slide in from the same side everything is fine, but as soon as I make a section slide in from the right the sections start to overlap as though it is executing before the previous pin has finished.

Strangely, if the top section slides in from the right then the next section is fine, but any further down the page cause issues.

I have a Codepen here: https://codepen.io/rob-wahlberg-beaney/pen/BamNxaN?editors=1010

Here's the code to slide things left:

gsap.utils.toArray(".product-container--slide-left").forEach((productContainer) => {
const productImages = productContainer.querySelector(".product-images");
const allLists = productContainer.querySelectorAll(".product-images__list");

const allEls = productContainer.querySelector(".product-images__list").querySelectorAll("li");
const allElsLength = allEls.length;

var totalWidth = allEls[0].offsetWidth * allElsLength + 90;

allLists.forEach(function (list) {
    list.style.width = totalWidth;
});

gsap.set(productContainer.querySelectorAll(".product-images__list"), {
    right: 0,
    x: totalWidth,
});
gsap.to(productContainer.querySelectorAll(".product-images__list"), {
    scrollTrigger: {
        trigger: productContainer,
        //start: "top center",
        pin: true,
        start: "top top", // when the top of the trigger hits the top of the viewport
        scrub: 1,
        toggleActions: "play none none reverse",
    },
    x: 0,
    duration: 1,
    offset: 500,
});
});

And here's the code to make them slide right:

gsap.utils.toArray(".product-container--slide-right").forEach((productContainer) => {
const productImages = productContainer.querySelector(".product-images");
const allLists = productContainer.querySelectorAll(".product-images__list");

const allEls = productContainer.querySelector(".product-images__list").querySelectorAll("li");
const allElsLength = allEls.length;

var totalWidth = allEls[0].offsetWidth * allElsLength + 90;

allLists.forEach(function (list) {
    list.style.width = totalWidth;
});

gsap.set(productContainer.querySelectorAll(".product-images__list"), {
    left: 0,
    x: -totalWidth,
});
gsap.to(productContainer.querySelectorAll(".product-images__list"), {
    scrollTrigger: {
        trigger: productContainer,
        //start: "top center",
        pin: true,
        start: "top top", // when the top of the trigger hits the top of the viewport
        scrub: 1,
        toggleActions: "play none none reverse",
    },
    x: 0,
    duration: 1,
    offset: 500,
});
});

As you can see, they're both exactly the same, the movement direction is the only thing that is different so it doesn't make sense.

The sections causing the issue have the class '.product-container--slide-left' and the issue shows when you scroll into the second section.


Solution

  • Your pinning sections are all in the same HTML level and have no parent to actually pin them on. And you initialize them not sequentially which makes the HTML structure messed up.

    If your sections are in this order left-right-left-right, you should run the ScrollTrigger in the same order. As of now, your script is running like left-left-right-right which is not good.

    The solution for your case is to initialize the ScrollTrigger sequentially from top to bottom, we can do that by merging your scripts into one like this.

    gsap.utils
        // select all the left and right sections
        .toArray('[class*="product-container--slide-"]')
        .forEach((productContainer) => {
            const allEls = productContainer
                .querySelector(".product-images__list")
                .querySelectorAll("li");
            const allElsLength = allEls.length;
    
            var totalWidth = allEls[0].offsetWidth * allElsLength + 90;
            const allLists = productContainer.querySelectorAll(".product-images__list");
    
            allLists.forEach(function(list){
                list.style.width = totalWidth;
            });
    
            // check if this section is left or right
            const isLeft = productContainer.classList.contains(
                "product-container--slide-left"
            );
    
            // change the value based on direction
            gsap.set(productContainer.querySelectorAll(".product-images__list"), {
                right: isLeft ? 0 : "",
                left: isLeft ? "" : 0,
                x: isLeft ? totalWidth : -totalWidth
            });
            gsap.to(productContainer.querySelectorAll(".product-images__list"), {
                scrollTrigger: {
                    trigger: productContainer,
                    //start: "top center",
                    pin: true,
                    start: "top top", // when the top of the trigger hits the top of the viewport
                    scrub: 1,
                    toggleActions: "play none none reverse",
                    markers: true
                },
                x: 0,
                duration: 1
            });
        });
    

    See this CodePen.