szyablo Posted April 9, 2021 Share Posted April 9, 2021 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 More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 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 More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 Also in your tween you can put the duration and stagger right in the vars object. Link to comment Share on other sites More sharing options...
szyablo Posted April 9, 2021 Author Share Posted April 9, 2021 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 1 Link to comment Share on other sites More sharing options...
szyablo Posted April 9, 2021 Author Share Posted April 9, 2021 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 More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 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. 1 Link to comment Share on other sites More sharing options...
Rodrigo Posted April 9, 2021 Share Posted April 9, 2021 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!!! 2 Link to comment Share on other sites More sharing options...
szyablo Posted April 9, 2021 Author Share Posted April 9, 2021 @BrianCross and @Rodrigo here you go guys. At first load if you scroll down it works , when you change the component it stops working Click on the button to see what happens See the Pen LYxzyWB by siinghd (@siinghd) on CodePen Link to comment Share on other sites More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 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. 1 Link to comment Share on other sites More sharing options...
Solution Rodrigo Posted April 9, 2021 Solution Share Posted April 9, 2021 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!!! 3 Link to comment Share on other sites More sharing options...
szyablo Posted April 9, 2021 Author Share Posted April 9, 2021 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 More sharing options...
Rodrigo Posted April 9, 2021 Share Posted April 9, 2021 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!!! 2 Link to comment Share on other sites More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 Well said @Rodrigo. Sometimes the DRY principle gets taken a little too far and then you're left with code that's incomprehensible. 1 Link to comment Share on other sites More sharing options...
OSUblake Posted April 9, 2021 Share Posted April 9, 2021 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 }); 3 Link to comment Share on other sites More sharing options...
BrianCross Posted April 9, 2021 Share Posted April 9, 2021 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. 2 Link to comment Share on other sites More sharing options...
szyablo Posted April 9, 2021 Author Share Posted April 9, 2021 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 More sharing options...
Rodrigo Posted April 9, 2021 Share Posted April 9, 2021 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!!! 3 Link to comment Share on other sites More sharing options...
szyablo Posted April 10, 2021 Author Share Posted April 10, 2021 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 More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now