Jump to content
Search Community

Horizontal Scroll GSAP + Scrolltrigger + React - Dude

Jhosep R test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hello everyone, i try to make this example  in react js, but i don't understand why what I did does not work, I am applying what I saw in the documentation but it has not worked, I want to learn how to use this great tool but I have not been able to apply a more complex example in react js. I need some guidance, thanks. :')

 

Here the code sanbox

https://codesandbox.io/s/gsap-react-horizontal-scroll-mr4gb1?file=/src/App.js

See the Pen mdrrbyo by oldskool123 (@oldskool123) on CodePen

Link to comment
Share on other sites

  • Solution

Welcome to the neighborhood, @Jhosep R

 

I noticed several things: 

  1. You're not doing proper cleanup which is pretty essential in React, especially because React 18 double-invokes useEffect()/useLayoutEffect() which could cause you to create duplicate competing animations/ScrollTriggers. gsap.context() makes it super easy. It's your new best friend in React. Please read this article:
  2. You were using the same "panel" ref for multiple elements. That can't work (it's a React thing, totally unrelated to GSAP). A ref is for a unique element (not multiple ones - it's not like a CSS class). 
  3. You used class="..." instead of className="...". Again, that's totally a React thing. 
  4. You were animating the wrong element. You animating the container and used percentages that were based on the child elements. For example, if that container is 400vw and each of the 4 children are 100vw wide, but then you animate the container's xPercent to -100 * (panels.length - 1), that would make the container animate 300% of its width, meaning -3 x 400vw which is 1200vw. I assume you meant to animate the panels each by -300%
  5. You were using a "panel" ref as if it's an Array of elements. Totally different. You should get an Array of the panels, like gsap.utils.toArray(".panel"). 

There may have been a few other problems too, but here's a fork: 

https://codesandbox.io/s/gsap-react-horizontal-scroll-forked-unsp6r?file=/src/App.js

 

I hope that helps. Enjoy the tools!

  • Like 2
Link to comment
Share on other sites

  • 5 weeks later...

Hi @codelab and welcome to the GreenSock forums!

 

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Hi @GreenSock

 

I'm hosting a demo project on CodeSandbox here , if I comment out the tailwind directives (line 1 - 3) in index.css, it works perfectly fine. This is the live website https://uscy59-3000.preview.csb.app/ . I tried building the project step by step and it was immediately I added the tailwind directives it stopped working well. Without tailwindCSS, it works perfectlly fine, but I need the implementation with tailwindCSS

Link to comment
Share on other sites

Hi @codelab,

 

Not sure I understand exactly what the effect is that you want to achieve, but it sounds like you've identified the issue - something to do with styling rules in tailwindCss. It's probably a matter of finding the lines of styling that are causing the problem and overwriting them in your custom stylesheet.

 

Having had a quick look at the css, maybe these are worth a try:
- remove the overflow rules for the body tag in the custom stylesheet. Messing with overflow can create weird results, especially when you're combining it with things like scrolltrigger.

- remove flex-wrap: wrap; from .container. You want the panels side by side, right?

- change the rule for .panel from width: 100vw to flex: 0 0 100vw

Hope that helps!

  • Like 3
Link to comment
Share on other sites

@codelab yeah, I don't have a lot of time to dig into that (your CodeSandbox won't allow any tweaks/changes without forcing a Github sign-in which is super annoying - it'd be much better to just provide a regular CodeSandbox without that restriction), but my best guess is that maybe Tailwind is assigning CSS transitions to the elements that GSAP is trying to affect. So, for example, part of what ScrollTrigger has to do as a part of its refresh() (to measure start/end positions) is to restore everything to the original state, then go from the top to the bottom in order and measure things, implement pinning, etc. So if GSAP tries setting a CSS property like width/height/top/left/whatever, and then attempts to measure but you've got a CSS transition applied, the browser will REFUSE to render that element in the proper position because it's like "nope, I'm gonna make that change gradually over the course of X seconds due to the CSS transition", making it impossible for GSAP to do its work. 

 

Maybe try to overrule any tailwind settings that might be applying CSS transitions or animations. 

 

I also noticed that your App element gets a scrollbar (in addition to the main body/viewport) which is odd. Something in the CSS is messing with that. Tailwind seems to make it really hard to decipher, though, as it uses tons of CSS variables. Obviously I'm not a Tailwind guy, so let me know if I'm missing something. 

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Hi @GreenSock , I have been trying to add one extra effect in the below codesandbox 

https://codesandbox.io/s/gsap-react-horizontal-scroll-mr4gb1?file=/src/App.js

 

What I was looking for is once the horizontal scrollable 'slider' is pinned, then the horizontal animation should not start untill user scrolls some distance. ie. the animation to be paused for some pixels. then the scrolling can start and also when it reaches the last container in the slider div, also then some pause in scrolling should happen. 
 The purpose is to have some buffer so that 1st & last slides in the horizontal scrolling section is not missed by user. 

Any Help appreciated..

Link to comment
Share on other sites

Hi @Mohammed Sameeh and welcome to the GreenSock forums!

 

You could create an extra ScrollTrigger instance that pins the container and not pin the container in the one that is moving the panels.

 

This seems to work the way you intend:

useLayoutEffect(() => {
  let ctx = gsap.context(() => {
    const pixelsPause = 300;
    let panels = gsap.utils.toArray(".panel");
    gsap.to(panels, {
      xPercent: -100 * (panels.length - 1),
      ease: "none",
      scrollTrigger: {
        trigger: slider.current,
        scrub: 1,
        snap: 1 / (panels.length - 1),
        start: `top+=${pixelsPause} top`,
        end: () => "+=" + window.innerWidth * panels.length,
        markers: { startColor: "fuchsia", endColor: "fuchsia", indent: 200 },
      },
    });
    ScrollTrigger.create({
      trigger: slider.current,
      end: () => "+=" + (window.innerWidth * panels.length + pixelsPause),
      markers: true,
      pin: true,
    });
  }, component);
  return () => ctx.revert();
});

The value at the start point of the first ScrollTrigger and the end point of the second ScrollTrigger are the ones that sets how long the user will scroll in pixels before the horizontal animation starts. Is very important that they match so is better to use a constant.

 

Here you can see it in action:

https://codesandbox.io/s/gsap-react-horizontal-scroll-forked-zw6qy4?file=/src/scenes/Scene.jsx

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

  • 2 weeks later...

Hi @GreenSock , I had implemented the first option to my website and it worked great. But I got several feedback from users when they were using the sites in mobile. the Scroll Delay worked well for the 1st & Last Container. But Is there a way we can add similar scroll pause to the 2nd & 3rd Slides also...? I tried several options but couldnt make it work. Any help would be appreciated.

Link to comment
Share on other sites

Hi,

 

This seems to work the way you expect:

useLayoutEffect(() => {
  let ctx = gsap.context(() => {
    let panels = gsap.utils.toArray(".panel");
    let pauseRatio = 0.1;
    let tl = gsap.timeline({
      defaults: { ease: "none" },
      scrollTrigger: {
        trigger: slider.current,
        pin: true,
        scrub: 1,
        snap: 1 / (panels.length - 1),
        end: () => "+=" + slider.current.offsetWidth,
        markers: true
      }
    });
    panels.forEach((panel, i) => {
      if (i) {
        tl.to(panels, {
          xPercent: -100 * i
        }, ((i - 1) * 0.5) + pauseRatio * i )
      }
    });
    tl.to({}, { duration: pauseRatio });
  }, component);
  return () => ctx.revert();
});

Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Hi @GreenSock  : One more Help!! I am building a pagination kinda thing for the horizontal slides which I have build using the above code. I just need the active / snapped slide/page number for that to function properly. In the above code is there any way we can grab the number [1,2,3,4] of the slide which is in the viewport...?

Link to comment
Share on other sites

Hi @GreenSock, I tried the above code. The Index is getting consoled perfectly. But the problem is that when I take the index out from the function to use it as a state variable, the whole animation is failing and behaving randomly. Is there a way that I can use that index value as a state without calling the uselayoutEffect again...?

Link to comment
Share on other sites

9 hours ago, Mohammed Sameeh said:

Hi @GreenSock, I tried the above code. The Index is getting consoled perfectly. But the problem is that when I take the index out from the function to use it as a state variable, the whole animation is failing and behaving randomly. Is there a way that I can use that index value as a state without calling the uselayoutEffect again...?

Hi,

 

Do you have a minimal demo that shows the issue you're having?

 

This could be just a JS scoping problem and not a GSAP related issue, but without a live editable demo there is not much we can do.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Hi @Rodrigo, Thank you for your reply.

 

Below is the Demo for the code given by @GreenSock. Its working perfectly. The index is getting logged.

https://codesandbox.io/s/gsap-react-horizontal-scroll-forked-lgj0t6?file=/src/scenes/Scene.jsx

 

Below is the same code + 2 Lines I have added to store that page index as a state variable. But when the setState is called, the animation is abrupted. 

 

https://codesandbox.io/s/gsap-react-horizontal-scroll-forked-jid5oy?file=/src/scenes/Scene.jsx

 

 

Link to comment
Share on other sites

Hi,

 

The issue is that you're missing the empty dependencies array in the effect hook:

useLayoutEffect(() => {
  let ctx = gsap.context(() => {
    /* ... */
  }, component);
  return () => ctx.revert();
}, []);

So basically what happens here is that everytime that the state is updated that entire layout effect is executed again causing that behaviour you're describing. Using an empty dependencies array ensures that the layout effect hook runs once when the component is first mounted.

 

Hopefully this clear things up.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

  • 6 months later...

Hi,

 

Most of the time this is about what you are  pinning. If the element's height is less than the screen, then you have to wrap that element and the next one in a common parent and pin that element.

 

These are a few demos of that approach:

See the Pen LYMYYEL by GreenSock (@GreenSock) on CodePen

 

See the Pen qBLRzVX by GreenSock (@GreenSock) on CodePen

 

See the Pen ZEVpqjb by GreenSock (@GreenSock) on CodePen

 

Hopefully this helps.

Happy Tweening!

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...