Jump to content
Search Community

React : Do i have to register gsap.registerPlugin(ScrollTrigger); in every component?

szyablo test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

So i have many sections and for each section i have one component, now i want to animate every section , do i have to use :

 

import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
 

for every section?

like my main component is like : 

const Gali_mobile_main = () => {
    return (
      <>
        <Gali_mobile_section1 />
        <Gali_mobile_section2 />
        <Gali_mobile_section3 />
        <Gali_mobile_section4 />
        <Gali_mobile_section6 />
        <Gali_mobile_section7 />
        <Gali_mobile_section8 />
      </>
    );
}
 
i have created a hook like : 
import { useEffect } from "react";
import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
 
export default function opacityManager(trigger, content) {
  gsap.registerPlugin(ScrollTrigger);
  useEffect(() => {
    gsap.to(
      content,
      5,
      {
        scrollTrigger: {
          trigger: trigger,
          start: "top center",
          end: "bottom center",
          scrub: true,
          markers: true,
        },
        yoyo: true,
        repeat: 1,
        opacity: 1,
        y: 0,
      },
      0
    );
    return () => {
      ScrollTrigger.kill();
    };
  }, []);
 
  return true;
}
 
 
and i dont know if its correct
and in every section i do 
          const opm = opacityManager(
            ".prod_mobile_section3",
            ".prod_mobile_section3_content"
          );
Link to comment
Share on other sites

I would just import the plugin into the hook like you're doing. It doesn't hurt anything to import a plugin multiple times. I would also rename the hook to useOpacityManager so React knows it's a hook.

 

I would use refs instead of class names as well:

 

import { useEffect, useRef } from "react";
import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

export default function useOpacityManager() {
  const trigger = useRef();
  const content = useRef();
  
  useEffect(() => {
    gsap.to(
      content.current,
      5,
      {
        scrollTrigger: {
          trigger: trigger.current,
          start: "top center",
          end: "bottom center",
          scrub: true,
          markers: true,
        },
        yoyo: true,
        repeat: 1,
        opacity: 1,
        y: 0,
      },
      0
    );
    return () => {
      ScrollTrigger.kill();
    };
  }, []);
 
  return [trigger, content];
}

and then in the component:

const [triggerRef, contentRef] = useOpacityManager();
<TriggerComponent ref={triggerRef} />
<ContentComponent ref={contentRef} />

Hopefully that makes sense.

Link to comment
Share on other sites

34 minutes ago, BrianCross said:

I would just import the plugin into the hook like you're doing. It doesn't hurt anything to import a plugin multiple times. I would also rename the hook to useOpacityManager so React knows it's a hook.

 

I would use refs instead of class names as well:

 



import { useEffect, useRef } from "react";
import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

export default function useOpacityManager() {
  const trigger = useRef();
  const content = useRef();
  
  useEffect(() => {
    gsap.to(
      content.current,
      5,
      {
        scrollTrigger: {
          trigger: trigger.current,
          start: "top center",
          end: "bottom center",
          scrub: true,
          markers: true,
        },
        yoyo: true,
        repeat: 1,
        opacity: 1,
        y: 0,
      },
      0
    );
    return () => {
      ScrollTrigger.kill();
    };
  }, []);
 
  return [trigger, content];
}

and then in the component:



const [triggerRef, contentRef] = useOpacityManager();


<TriggerComponent ref={triggerRef} />
<ContentComponent ref={contentRef} />

Hopefully that makes sense.

Hi thanks for your answer ,ill check and get back to you

  • Like 1
Link to comment
Share on other sites

44 minutes ago, BrianCross said:

I would just import the plugin into the hook like you're doing. It doesn't hurt anything to import a plugin multiple times. I would also rename the hook to useOpacityManager so React knows it's a hook.

 

I would use refs instead of class names as well:

 


import { useEffect, useRef } from "react";
import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

export default function useOpacityManager() {
  const trigger = useRef();
  const content = useRef();
  
  useEffect(() => {
    gsap.to(
      content.current,
      5,
      {
        scrollTrigger: {
          trigger: trigger.current,
          start: "top center",
          end: "bottom center",
          scrub: true,
          markers: true,
        },
        yoyo: true,
        repeat: 1,
        opacity: 1,
        y: 0,
      },
      0
    );
    return () => {
      ScrollTrigger.kill();
    };
  }, []);
 
  return [trigger, content];
}

and then in the component:


const [triggerRef, contentRef] = useOpacityManager();

<TriggerComponent ref={triggerRef} />
<ContentComponent ref={contentRef} />

Hopefully that makes sense.

I'm having issues with 

 return () => {
      ScrollTrigger.kill();
    };     

 

I dont know why when it is there and i change the page ,  the animation in the page doesnt work . The problem is that if i remove it there are still trigger of the last page and the animation have a strange beahaviour , any suggestion?

https://i.imgur.com/zxG2bXU.gif

Link to comment
Share on other sites

That’s the useEffect cleanup function that runs when the component unmounts, so it’s removing the ScrollTrigger instance when that happens. Are you able to distill the problem down into a CodePen? It’s not clear what the problem is from the gif. 

  • Like 1
Link to comment
Share on other sites

Hi,

 

Can you create a live reduced sample, showing just what's causing your issues, in codesandbox? Just select create a new sandbox using the regular react template and then add gsap as a dependency.

 

With the information you have provided so far is quite difficult to see what could be the issue.

 

Happy Tweening!!!

  • Like 2
Link to comment
Share on other sites

I think when your useEffect is running its cleanup function, it's killing all ScrollTrigger instances globally. I'm not an expert, so hopefully one of the real experts will chime in. 😀

 

Edit: sorry, looks like one of the experts already chimed in! @Rodrigo maybe you can check my answer here!

 

I made the following change to the hook and it appears to work:

 

useEffect(() => {
    const tween = gsap.to(contentRef.current, {
      scrollTrigger: {
        trigger: triggerRef.current,
        start: "top center",
        end: "bottom center",
        scrub: true,
        markers: true,
      },
      yoyo: true,
      repeat: 1,
      opacity: 1,
      y: 0,
    });
    return () => {
      tween.kill(); //Kill the tween instead of the ScrollTrigger
    };
  }, []);

  return { triggerRef, contentRef };
}

It just kills the tween instance instead of the ScrollTrigger. 

  • Like 1
Link to comment
Share on other sites

  • Solution

As Brian points, basically you're killing all the ScrollTrigger instances you have.

 

Also your set up a is a little bit funky, IMHO.

 

If I was you I'd create the ScrollTrigger instance on each component in the useEffect() hook, passing an empty array to run the code only in the initial render and store it in a constant or a ref if you prefer. In the same hook, use the cleanup function to kill that specific ScrollTrigger instance, something  like this:

const myScrollTrigger = useRef();
useEffect(() => {
  myScrollTrigger.current = gsap.to(contentRef.current, {
      scrollTrigger: {
        trigger: triggerRef.current,
        start: "top center",
        end: "bottom center",
        scrub: true,
        markers: true,
      },
      yoyo: true,
      repeat: 1,
      opacity: 1,
      y: 0,
    });
  return (() => {
    myScrollTrigger.current.kill();
  });
}, []);

Of course you have to keep in mind the trigger element and pass that either as a prop or perhaps context, depending on the depth of your component's tree.

 

Happy Tweening!!!

  • Like 3
Link to comment
Share on other sites

18 minutes ago, Rodrigo said:

As Brian points, basically you're killing all the ScrollTrigger instances you have.

 

Also your set up a is a little bit funky, IMHO.

 

If I was you I'd create the ScrollTrigger instance on each component in the useEffect() hook, passing an empty array to run the code only in the initial render and store it in a constant or a ref if you prefer. In the same hook, use the cleanup function to kill that specific ScrollTrigger instance, something  like this:


const myScrollTrigger = useRef();
useEffect(() => {
  myScrollTrigger.current = gsap.to(contentRef.current, {
      scrollTrigger: {
        trigger: triggerRef.current,
        start: "top center",
        end: "bottom center",
        scrub: true,
        markers: true,
      },
      yoyo: true,
      repeat: 1,
      opacity: 1,
      y: 0,
    });
  return (() => {
    myScrollTrigger.current.kill();
  });
}, []);

Of course you have to keep in mind the trigger element and pass that either as a prop or perhaps context, depending on the depth of your component's tree.

 

Happy Tweening!!!

Hi thanks for your answer. I'm sorry but i didn't got the array part ? can you show me a little example ? I also noticed that when changing the page my trigger and content markers are in wrong position but when i reload the page they became normal.any suggestion to fix/improve it?) Thanks you for your patience. 

Link to comment
Share on other sites

31 minutes ago, szyablo said:

I'm sorry but i didn't got the array part ? can you show me a little example ?

https://reactjs.org/docs/hooks-effect.html

 

The array in the useEffect hook is a set of dependencies in the component's state or props that will trigger that code to run.

 

Finally as I mentioned before, I recommend you to move your GSAP logic inside each component. As I understand how easy it makes development to have some sort of factory function and I applaud the fact that you created one, it seems to be creating more problems than it's solving. I once worked in a very large app and at some point the project manager stopped almost everything, because some specific methods that would prevent us from writing lots of lines of code, were causing us to create a lot of convoluted conditions and state management in order to make them work. At the end we had to write a lot of code over and over, but the development logic and code maintenance became super easy and there wasn't a noticeable performance drop. Sometimes super elegant code is not the best choice. The code has to do what you need it to do and then you start refine it, keeping in mind that such code at some point will be looked by another developers that never worked in that project and makes sense to them.

 

Happy Tweening!!!

  • Like 2
Link to comment
Share on other sites

Speaking of DRY, this is how I handle registering plugins in a large project. I make a gsap file, let's call it gsap.js. I import everything, register everything, and then export anything I might need a reference to.

 

gsap.js

import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { InertiaPlugin } from "gsap/InertiaPlugin";

gsap.registerPlugin(Draggable, InertiaPlugin);

export * from "gsap";
export { Draggable };

 

some-component.js

import { gsap, Draggable } from "./gsap";

// no need to register!
Draggable.create(".foo", {
  inertia: true
});

gsap.to(bar, { x: 100 });

 

  • Like 3
Link to comment
Share on other sites

1 hour ago, OSUblake said:

Speaking of DRY, this is how I handle registering plugins in a large project. I make a gsap file, let's call it gsap.js. I import everything, register everything, and then export anything I might need a reference to.

 

gsap.js


import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { InertiaPlugin } from "gsap/InertiaPlugin";

gsap.registerPlugin(Draggable, InertiaPlugin);

export * from "gsap";
export { Draggable };

 

some-component.js


import { gsap, Draggable } from "./gsap";

// no need to register!
Draggable.create(".foo", {
  inertia: true
});

gsap.to(bar, { x: 100 });

 

And this is why this forum rocks.

  • Like 2
Link to comment
Share on other sites

5 hours ago, Rodrigo said:

https://reactjs.org/docs/hooks-effect.html

 

The array in the useEffect hook is a set of dependencies in the component's state or props that will trigger that code to run.

 

Finally as I mentioned before, I recommend you to move your GSAP logic inside each component. As I understand how easy it makes development to have some sort of factory function and I applaud the fact that you created one, it seems to be creating more problems than it's solving. I once worked in a very large app and at some point the project manager stopped almost everything, because some specific methods that would prevent us from writing lots of lines of code, were causing us to create a lot of convoluted conditions and state management in order to make them work. At the end we had to write a lot of code over and over, but the development logic and code maintenance became super easy and there wasn't a noticeable performance drop. Sometimes super elegant code is not the best choice. The code has to do what you need it to do and then you start refine it, keeping in mind that such code at some point will be looked by another developers that never worked in that project and makes sense to them.

 

Happy Tweening!!!

Hi i did as you said but the markers position is still weird but i probably know why , the problem are images. Probably as they took time to load the containers heigth is < than container with images heigth. Do you know how i can solve this problem ? setTimeout?

Link to comment
Share on other sites

If you have a notion of the image height, or the height you're using in the styles, then add that to the <img> tag in order to avoid layout shifting, which could be the cause of the issue you're mentioning. Perhaps this article can help you:

 

https://www.afasterweb.com/2019/10/30/a-new-way-to-prevent-layout-jank-during-image-load/

 

Happy Tweening!!!

  • Like 3
Link to comment
Share on other sites

I'm updating this if in future anyone gets in similar problem like mine , for me the solution was  this 

 

export default function useOpacityManager() {
  const triggerRef = useRef();
  const contentRef = useRef();
 
  const myScrollTrigger = useRef();
 
  useEffect(() => {
    let interval = setTimeout(() => {
      contentRef.current.style.opacity = 0;
      myScrollTrigger.current = gsap.to(contentRef.current, {
        scrollTrigger: {
          trigger: triggerRef.current,
          start: "top center",
          end: "bottom center",
          scrub: true,
          markers: true,
        },
        yoyo: true,
        repeat: 1,
        opacity: 1,
        y: 0,
      });
    }, 1500);
 
    return () => {
     clearInterval(interval);
      if(myScrollTrigger.current!=undefined){
        myScrollTrigger.current.kill();
      }
    };
  }, []);
 
  return { triggerRef, contentRef };
}

Has the opacity is 1 of the content/containers in the beginning , user dont have to wait 1.5 sec to see the content.

But without setting the opacity to 0 the animation wouldn't work so in callback function of setTimeout first thing i do is setting the opacity to 0 of the content .

Probably this isn't the best solution but after doing a lot of stuff this is the one which only works correctly for me. Thanks to anyone whom answered.

 

 

 

 

 

 

 

 

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