Jump to content
Search Community

Does ScrollTrigger work with react-locomotive-scroll?

Pollux Septimus test
Moderator Tag

Recommended Posts

I am trying to set up locomotive scroll with ScrollTrigger but it seems that the ScrollTrigger does not work. I would like for the animation to run as scrub, only when scrolling but the tl runs as normal when refreshing the page, ignoring the ScrollTrigger. 

 

I am wondering if ScrollTrigger still works with another smooth-scrolling libraries, since gsap now has a SmoothScroll plugin. I made my setup base on a video I found on youtube and I think I set up everything properly.

 

As it seems that a minimal demo is requested often, I have created a sandbox.

 

Thank you!

Link to comment
Share on other sites

Sure, you can use ScrollTrigger with other smooth-scrolling libraries. You don't have to use ScrollSmoother (but it is much easier to implement and it has features unmatched by others). 

 

Your demo was very difficult for me to follow. I don't see anything that's scrollable at all. My recommendation would be to completely ignore ScrollTrigger at first. Just see if you can get the LocomotiveScroll stuff working (that's not a GreenSock product, so we can't really support that here). Then, once you've got a smooth scrolling page working, THEN start trying to sprinkle in some ScrollTrigger stuff. 

 

Also, since you're using React, you should definitely start leveraging gsap.context() because it's you're new best friend in React. It makes cleanup super easy. You've gotta be careful in React to do proper cleanup. React 18 actually calls useEffect()/useLayoutEffect() TWICE which can really mess things up. You might be creating multiple competing animation/ScrollTriggers accidentally. I would STRONGLY recommend reading this article: 

 

One other thing I noticed is that LocomotiveScroll changed their API in version 4 slightly, so your scrollerProxy() needs tweaking: 

// old
locoScroll.scrollTo(value, 0, 0)

// new
locoScroll.scrollTo(value, {duration: 0, disableLerp: true})

Good luck!

Link to comment
Share on other sites

Hello @GreenSock, Thank you for you replay!

 

I know that ScrollSmoother is easier to work with, but purchasing the GreenSock Club does not make sense for me right now. Sorry. Regardless I am still locking for a wait to have a smooth scroll while being able to use everything else that gsap offers. 

 

I am sorry that it was difficult to navigate the demo. It's part of a bigger project and I tool only what's necessary. I forgot to add content underneath the hero section so you could scroll. I have fix the issue and now you should be able to scroll 😁.  Also, there ware some different styling for mobile so please enlarge the window in order to see the animation. Maybe this will help. I have also added a 3s duration so the animation doesn't fly off the screen. 

 

I did ignore the ScrollTrigger and implemented the locomotive-scroll. It seems to work fine. I don't think that the locomotive package is the problem as I wasn't able to make the ScrollTrigger run even before adding it.  Before adding the locomotive scroll, I had overflow: hidden on the App and body and had a separate component with overflow-y scroll. I figure that ScrollTrigger doesn't work on divs with overflow-y so I added the locomotive scroll because I planned to do it anyways. The difference between before the locomotive scroll and after is the before the animation wont event run. Now the animation runs but ScrollTrigger gets ignored. 

 

Can you please take a quick look at pages > homepage > hero > index, right before where the jsx starts and see if you can spot anything? If you don't see anything at the first glimpse I will try to find other solutions. 

 

Thank you! 

Link to comment
Share on other sites

It looks like you still aren't using gsap.context() and you're not doing proper cleanup. Please read that article I linked to - I think it will make it much more clear how to use GSAP in React. 

 

You also didn't associate your animation with a ScrollTrigger. You created an animation...and then you created a totally separate ScrollTrigger...with no animation at all. :)

 

For example: 

let tl = gsap.timeline();
tl.to(...);
      
ScrollTrigger.create({
      trigger: heroContainerRef.current,
      scroller: ".scrollContainer",
      start: "top top",
      scrub: 1
      // animation: tl // <-- YOU FORGOT THIS!
});

But in my opinion, it's clearer to just associate the ScrollTrigger directly on the timeline: 

let tl = gsap.timeline({
  scrollTrigger: {
    trigger: heroContainerRef.current,
    scroller: ".scrollContainer",
    start: "top top",
    scrub: 1
  }
});

And again, make sure you wrap stuff in a gsap.context() so that you can do proper cleanup: 

useEffect(() => {
  let ctx = gsap.context(() => {
    // all your GSAP/ScrollTrigger code in here...
  });
  return () => ctx.revert(); // <- CLEANUP!
}, []);

Does that clear things up? 

Link to comment
Share on other sites

Hello @GreenSock,

Thank you very much for your help!

 

I have created a custom hook that integrates locomotive-scroll and ScrollTrigger using the video I linked in the original question,

See the Pen zYrELYe?editors=1010 by GreenSock (@GreenSock) on CodePen

, gsap docs for scrollerProxy, and some other Youtube videos. I have also took into consideration your comment from above. Everything works now, and having the hook where I could simply turn locomotive-scroll on and off helped me debug stuff.

 

I will leave the code below for anyone else struggling. Maybe this thread will help. I hope you don't mind.

 

Here is the custom hook:

import React, { useEffect } from 'react';
import LocomotiveScroll from 'locomotive-scroll';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';

const useLocoScroll = (start) => {
  gsap.registerPlugin(ScrollTrigger);

  useEffect(() => {
    if (!start) return;

    const scrollEl = document.querySelector('.App');

    let locoScroll = new LocomotiveScroll({
      el: scrollEl,
      smooth: true,
      multiplier: 1.5,
    });

    locoScroll.on('scroll', ScrollTrigger.update);

    ScrollTrigger.scrollerProxy(scrollEl, {
      scrollTop(value) {
        if (locoScroll) {
          return arguments.length
            ? locoScroll.scrollTo(value, 0, 0)
            : locoScroll.scroll.instance.scroll.y;
        }
        return null;
      },
      scrollLeft(value) {
        if (locoScroll) {
          return arguments.length
            ? locoScroll.scrollTo(value, 0, 0)
            : locoScroll.scroll.instance.scroll.x;
        }
        return null;
      },
      getBoundingClientRect() {
        return {
          top: 0,
          left: 0,
          width: window.innerWidth,
          height: window.innerHeight,
        };
      },
    });

    const lsUpdate = () => {
      if (locoScroll) {
        locoScroll.update();
      }
    };

    ScrollTrigger.addEventListener('refresh', lsUpdate);
    ScrollTrigger.refresh();
  }, [start]);
};

export default useLocoScroll;

To use this hook you simply call it inside theApp.js file like so: useLocoScroll(true) or any condition that will result to true

You'll also need to add data-scroll-container to the".App" div and data-scroll-section to the most outer div of any of your sections. For simple use I have created a Section component that does that. For a batter understanding of the hook, please check

See the Pen zYrELYe?editors=1010 by GreenSock (@GreenSock) on CodePen

.

import React from 'react';

const Section = ({ children }) => {
  return <section data-scroll-section>{children}</section>;
};

export default Section;

To use this, simply wrap your jsx into this component like so:

return(
  <Section>
    code...
  </Section>
)

For the gsap / ScrollTrigger code I've done exactly what Jack suggested. Here is another code example:

  useEffect(() => {
    let ctx = gsap.context(() => {
      const tl = gsap.timeline({
        scrollTrigger: {
          trigger: heroContainerRef.current,
          scroller: '.App',
          start: 'top top',
          scrub: 1,
        },
      });

      tl.to(textColRef.current, {
        x: 200,
        opacity: 0,
      });
      tl.to(
        imageWrapperRef.current,
        {
          x: -200,
          opacity: 0,
        },
        0
      );
    });
    return () => ctx.revert();
  }, []);

Please note the scroller: '.App' inside the ScrollTrigger. This is very important as you need to specify what the new scroller is.

 

Also, don't forget to add  into your App.css or index.css the CSS that locomotive-scroller recommends.

 

Hope this helps

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...