sunrisejoz Posted October 17, 2023 Share Posted October 17, 2023 (edited) so, i'll start by stating im not the greatest React ninja out there. so much so, that i've had to read up a fair bit on useContext, useLayoutEffect, useRef etc. i've followed a couple of your examples along and nearly have something "working". It looks like i've fell over with the timeline (across components) not using the globalTimeLine that i set in the root el. while the animations do work, they all (components) seem to be singing their own song; running to their own version of a timeline. any tips, assistance, spare brain cells welcomed here https://codesandbox.io/s/elegant-wiles-qyt2jt?file=/src/components/Copy.js&resolutionWidth=320&resolutionHeight=675 Edited October 17, 2023 by sunrisejoz typo Link to comment Share on other sites More sharing options...
Rodrigo Posted October 18, 2023 Share Posted October 18, 2023 Hi @sunrisejoz and welcome to the GSAP forums! I think in this cases is better to create a single timeline in a parent component and either pass the timeline to it's child components in order to add instances to it, use a callback to populate the timeline or use forwardRef in order to bring refs of your child components into a parent one and create the timeline there: https://react.dev/reference/react/forwardRef#forwardref Those different approaches are explored in our Advanced Techniques article: https://gsap.com/resources/react-advanced Hopefully this helps. Happy Tweening! Link to comment Share on other sites More sharing options...
sunrisejoz Posted October 27, 2023 Author Share Posted October 27, 2023 thanks @Rodrigo i am soo struggling here. i've figured out how to useContext (not that that might be relevant anymore). how can i pass the timeline to children. i have tried accessing this via context but cant seem to get at the object values Link to comment Share on other sites More sharing options...
Cassie Posted October 27, 2023 Share Posted October 27, 2023 Have you looked at the guide Rodrigo shared? It's this section in particular https://gsap.com/resources/react-advanced/#passing-down-a-timeline-prop Maybe take a look at that and then share a minimal example of what you're trying so we can see where you're going wrong? 1 Link to comment Share on other sites More sharing options...
sunrisejoz Posted October 27, 2023 Author Share Posted October 27, 2023 indeed i did @Cassie i've setState inside useLayoutEffect like so... useLayoutEffect(() => { let ctx = gsap.context(() => { // all your animations go in here... let tl = gsap.timeline({ duration: 2.5, delay: 1, repeat: 99 }); // tl.set("#logo", { opacity: 1 }); tl.set(".bg", { opacity: 2 }, 0); tl.to(".triangle", { right: 0, duration: 0.6, ease: "power1.in" }, 0); tl.to(".arrow-left", { right: 100, duration: 0.4, opacity: 1 }, 0.4); tl.to(".arrow-right", { right: 30, duration: 0.4, opacity: 1 }, "<"); tl.to(".title-1", { xPercent: 100, duration: 0.6, ease: "power1.in" }, 0); tl.to(".title-2", { opacity: 1, duration: 0.6, ease: "power1.in" }, 1); // tl.to(".cta", { opacity: 1, ease: "power1.in" }, 1.8); setTimeline(tl); }, root); // <- scopes all selector text to the root element setTimeline(ctx); return () => ctx.revert(); }, []); then passed the value={timeline} to my provider. inside the child component i am either trying to access the (animation) timeline OR add to the timeline. in any case i end up with this (logged) Context {data: Array(7), _r: Array(0), isReverted: false, id: 1, selector: ƒ, …} data: (7) [Timeline, Tween, Tween, Tween, Tween, Tween, Tween]id: 1isReverted: falselast: ƒ f()selector: ƒ (v)_r: [][[Prototype]]: Object 'ctx' Link to comment Share on other sites More sharing options...
Rodrigo Posted October 27, 2023 Share Posted October 27, 2023 Hi, The reason for the unexpected log in your console is because you're calling your setTimeline() method twice: Once with the GSAP Timeline you're creating as a parameter and a second time with the GSAP Context instance you're creating as a parameter. useLayoutEffect(() => { let ctx = gsap.context(() => { // all your animations go in here... let tl = gsap.timeline({ duration: 2.5, delay: 1, repeat: 99 }); // tl.set("#logo", { opacity: 1 }); tl.set(".bg", { opacity: 2 }, 0); tl.to(".triangle", { right: 0, duration: 0.6, ease: "power1.in" }, 0); tl.to(".arrow-left", { right: 100, duration: 0.4, opacity: 1 }, 0.4); tl.to(".arrow-right", { right: 30, duration: 0.4, opacity: 1 }, "<"); tl.to(".title-1", { xPercent: 100, duration: 0.6, ease: "power1.in" }, 0); tl.to(".title-2", { opacity: 1, duration: 0.6, ease: "power1.in" }, 1); // tl.to(".cta", { opacity: 1, ease: "power1.in" }, 1.8); setTimeline(tl); }, root); // <- scopes all selector text to the root element // calling setTimeline again here!!! setTimeline(ctx); return () => ctx.revert(); }, []); So the log you're getting in your console is the GSAP Context instance. Honestly there is no need to call that method twice: useLayoutEffect(() => { let ctx = gsap.context(() => { // all your animations go in here... let tl = gsap.timeline({ duration: 2.5, delay: 1, repeat: 99 }); // tl.set("#logo", { opacity: 1 }); tl.set(".bg", { opacity: 2 }, 0); tl.to(".triangle", { right: 0, duration: 0.6, ease: "power1.in" }, 0); tl.to(".arrow-left", { right: 100, duration: 0.4, opacity: 1 }, 0.4); tl.to(".arrow-right", { right: 30, duration: 0.4, opacity: 1 }, "<"); tl.to(".title-1", { xPercent: 100, duration: 0.6, ease: "power1.in" }, 0); tl.to(".title-2", { opacity: 1, duration: 0.6, ease: "power1.in" }, 1); // tl.to(".cta", { opacity: 1, ease: "power1.in" }, 1.8); setTimeline(tl); }, root); // <- scopes all selector text to the root element return () => ctx.revert(); }, []); Hopefully this helps. Happy Tweening! Link to comment Share on other sites More sharing options...
sunrisejoz Posted October 27, 2023 Author Share Posted October 27, 2023 thanks for getting back @Rodrigo i'd already omitted that, guess it didnt update on the url. i cant get that method to work and believe that callback may be a better idea for my use now. i have a parent and component working just dandy, though where i have two html elements referenced, it seems to fall over. const Copy = ({ addAnimation, index, copy }) => { const el = useRef(); useLayoutEffect(() => { console.log("copy effect"); const ctx = gsap.context(() => { const animation = gsap.tl .to("h1", { xPercent: 100, duration: 0.6, ease: "power1.in" }, 0) .to("h2", { opacity: 1, duration: 0.6, ease: "power1.in" }, 1); addAnimation(animation, index); }); return () => ctx.revert(); }, [addAnimation, index]); return ( <CopyWrapper ref={el} className="copy" onClick={(e) => onInlineEditClick(copy.ids, e)} > <Limiter maxHeight={119}>{parse(copy?.value || "")}</Limiter> </CopyWrapper> ); }; export { Copy }; in this instance, how would i manage two (or more) elements that sit within the component? call a useLayoutEffect for each maybe?? Link to comment Share on other sites More sharing options...
Rodrigo Posted October 27, 2023 Share Posted October 27, 2023 Hi, Is really hard for us to debug without a minimal demo. We have a collection of Starter Templates in Stackblitz: https://stackblitz.com/@GreenSockLearning/collections/gsap-react-starters The only thing I can see not going right in your code is this: useLayoutEffect(() => { console.log("copy effect"); const ctx = gsap.context(() => { const animation = gsap.tl .to("h1", { xPercent: 100, duration: 0.6, ease: "power1.in" }, 0) .to("h2", { opacity: 1, duration: 0.6, ease: "power1.in" }, 1); addAnimation(animation, index); }); return () => ctx.revert(); }, [addAnimation, index]); The problem I see is that you are adding a new timeline to the parent timeline using the addAnimation method. Now what happens when you change the index prop? Your entire effect hook runs again and that particular GSAP Context is reverted, then a new animation is added to the parent timeline, but the parent timeline already has a reference to a timeline that no longer exists. This could create some logic issues, breaking errors and most definitely a memory leak. If the index prop is not changing then there is no need to have it as a dependency in your array. If ESLINT is complaining about it, just disable the rule in that particular line. Happy Tweening! 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