Moti Posted May 29, 2022 Share Posted May 29, 2022 Greetings gsap community, I have recently found gsap scrolltrigger plugin and have been hooked ever since. Before I start with my question/s, I would like to thank the people who created gsap and to those who help out in this forum. I am pretty much new to the plugins and to the framework that I am working on so I will try my best to explain the issue I have created a small threejs animation together with html animations like (fake horizontal scroll) found in scroll trigger demos. What i discovered is that the start pin of the other animations is not located properly after the Canvas for the Three model. see (secondContainer) in the demo. Issue: other animation start pin is not in desired location. Minimal Demo: https://codesandbox.io/s/react-canvas-p07dz3?file=/src/index.tsx Note: 1. Actually using Nextjs (have not been able to make the pen/sandbox work) i stole this demo sandbox from topic(see below) 2. I have read about pinSpacing/ pinSpacer but i have no luck on making it work due to my inexperience Any suggestions/answers are greatly appreciated. Also if this was already answered please point me to the right direction. Regards, Moti See the Pen index.tsx by s (@s) on CodePen Link to comment Share on other sites More sharing options...
GreenSock Posted May 30, 2022 Share Posted May 30, 2022 I'm not a React guy, but I think the problems are: You're creating your ScrollTriggers out of order and you didn't set a refreshPriority to correct for that. You should always create your ScrollTriggers in the order they'd appear on the screen especially when pinning because the pins higher up on the page would push the start/end values of the things below further down. In your case, you're creating the SECOND pin first and at that time, there is no first element at all, so it's the very top thing on the page, thus the start position is 0...and then you create/render your FIRST element which pushes the other one down now, and the pinning should also force it further down still. ScrollTrigger can't know that you're going to do that which is why it's important that you do things in order or set refreshPriority values to reflect things properly. The way your app is working causes things to not be in their final position when the window's "load" event fires (which triggers a ScrollTrigger.refresh() which is the think that does all the start/end calculations). So your Content2's useEffect() fires when the App isn't done, and then Content's useEffect() fires and App still isn't done, and then...nothing. When your App finally renders entirely, there's nothing telling ScrollTrigger "hey, do your refresh() now and calculate all the start/end values now that I've finished moving stuff around and rendering". So you could either call ScrollTrigger.refresh() at the end your Content's useEffect() or add something like setTimeout(ScrollTrigger.refresh, 100) but that feels a little hacky to me (it assumes the page will be rendered fully within 100ms). The entire point here is to just make sure you call ScrollTrigger.refresh() when EVERYTHING is done rendering and the page is in its final state. Does that clear things up? 1 Link to comment Share on other sites More sharing options...
Moti Posted May 30, 2022 Author Share Posted May 30, 2022 Hello GreenSock, Thank you for your response. I was tinkering with the sandbox trying to implement the Scrolltrigger.refresh(), unfortunately I did not get it to work (think i have coded it wrong). I have both tried to call the inside the useEffect and tried to set up a timer; both did not have the desired results. Sandbox: https://codesandbox.io/s/react-canvas-forked-72nxi0?file=/src/index.tsx as for your reply 1. Confused on how this works, I think I have created the scrollTriggers in order. Content1 for the 'firstContainer' then Content2 for the 'secondContainer'. I have also created a third one just to test out the scrollTrigger logic and it behaves as expected. The start pin on Content2 waits for the pinSpacer and all is well. However that is not the case for Content1. ( it does have the pinSpacer upon inspecting but it Content2's scrolltrigger just doesnt see it. ) 2. I have tried to implement Scrolltrigger.refresh() but I did not manage to make it work ( maybe because I had it wrong) Regards, Moti Link to comment Share on other sites More sharing options...
Solution Cassie Posted May 30, 2022 Solution Share Posted May 30, 2022 Heya! Just prefacing this with a disclaimer that we're not really the best place to be offering advice on React. The lunch dev community is great though. Lunch dev discord community Lots of React experts here - very friendly to beginners. --- That being said, with my small amount of React knowledge I'll give it a bash. https://codesandbox.io/s/react-canvas-forked-jst7g8?file=/src/index.tsx So, yep - you have defined the ScrollTriggers in order visually in the code. That would work fine in vanilla JS as the code gets executed top to bottom (unless you're doing async stuff) But, you're using React and there's a bunch of stuff happening behind the scenes (That I can't explain or advise on) For whatever reason your second scrollTriggered component is rendering to the DOM before the first so when it calculates all the positions, it's incorrect because the first component doesn't exist yet. When this happens you have to manually define the order you want them in, and then call ScrollTrigger.refresh() to sort and calculate the right positions. Ideally you'd call scrollTrigger.refresh when the DOM is done loading but again, I don't really know react... I know there's no DOMLoaded function like in other frameworks. I've just called it at the end of each of your useEffects and it works for now. const Content = () => { gsap.registerPlugin(ScrollTrigger); const boxRef = useRef(); useLayoutEffect(() => { let tl = gsap.timeline(); // tl stuff let controller = ScrollTrigger.create({ animation: tl, refreshPriority: 1 }); ScrollTrigger.refresh() }, []); return <Box ref={boxRef} />; }; const Content2 = () => { gsap.registerPlugin(ScrollTrigger); const secondBoxRef = useRef(); useLayoutEffect(() => { let tl2 = gsap.timeline(); // tl stuff let controller2 = ScrollTrigger.create({ animation: tl2, refreshPriority: 0 }); ScrollTrigger.refresh() }, []); return ( <div ref={secondBoxRef} className="secondBox"> test </div> ); }; if you need more React specific help I suggest popping into that discord. I'm sure someone there will be able to advise. Hope this helped a little! 1 Link to comment Share on other sites More sharing options...
Moti Posted May 30, 2022 Author Share Posted May 30, 2022 Hi Cassie, Thanks for the link; I'll check that channel out. Now I understand 2 hours ago, Cassie said: But, you're using React and there's a bunch of stuff happening behind the scenes (That I can't explain or advise on) For whatever reason your second scrollTriggered component is rendering to the DOM before the first so when it calculates all the positions, it's incorrect because the first component doesn't exist yet. It seems that way. How GreenSock Jack and you explained it now makes sense to me. Thank you both for your time and knowledge 2 hours ago, Cassie said: Hope this helped a little! It helped a lot. I was already considering to choose between the two components i've created and now I could keep them both. 9 hours ago, GreenSock said: set refreshPriority values to reflect things properly together with 9 hours ago, GreenSock said: The entire point here is to just make sure you call ScrollTrigger.refresh() when EVERYTHING is done rendering and the page is in its final state. got it working Working sandbox: https://codesandbox.io/s/gsap-reactthreefiber-canvas-working-trq85d Again thank you both for time, effort and support. Cheers! -Moti 1 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