Share Posted June 23, 2022 Hi! I'm using GSAP with React + Typescript and have a basic animation where I move a few elements on the click of a button. However, I noticed that the animation glitches out when the button is pressed again before the animation has fully completed. So, I decided to add a state that would disable the button on the start of the animation, and re-enable it upon completion. However, this seems to have broken the animation to the point where it won't function at all. Thanks for any help! Here's the function I use on button click: function animateButtonRight() { gsap.timeline({ onStart: () => setRightDisabled(true), onComplete: () => { tempDiv = cardFiveRef; cardFiveRef = cardThreeRef; cardThreeRef = cardOneRef; cardOneRef = cardTwoRef; cardTwoRef = cardFourRef; cardFourRef = tempDiv; setRightDisabled(false); } }) .to(cardThreeRef.current, { left: "", right: offscreenDistanceFromEdgeLR, bottom: offscreenDistanceFromEdgeBottom, rotation: 18, duration: 1.2, ease: Power3.easeInOut, transformOrigin: "right 50%" }) .set(cardThreeRef.current, { left: offscreenDistanceFromEdgeLR, right: "", bottom: offscreenDistanceFromEdgeBottom, rotation: -18, transformOrigin: "left 50%" }) .to(cardOneRef.current, { left: "", right: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: 9, duration: 1.2, ease: Power3.easeInOut, transformOrigin: "right 50%", delay: 0.2 }, 0) .to(cardTwoRef.current, { right: "", left: width / 2 - width * 0.22 / 2, bottom: distanceFromEdgeBottomM, rotation: 0, duration: 1.2, ease: Power3.easeInOut, delay: 0.4 }, 0) .to(cardFourRef.current, { right: "", left: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: -9, duration: 1.2, ease: Power3.easeInOut, delay: 0.6, transformOrigin: "left 50%" }, 0) } Link to comment Share on other sites More sharing options...
Share Posted June 23, 2022 Very hard to debug without more context. If you have strict mode on, turn it off. Outside of that I don't know what you are trying to do, how you are trying to do it, or what is going wrong. Link to comment Share on other sites More sharing options...
Author Share Posted June 23, 2022 I'm not trying much beyond attempting to animate divs on a button click, and disable the button on animation start + re-enable when the animation is complete. It's a simple timeline with changes to the right, left, bottom, and rotation properties. In this new component, the animation works with the lines in the onStart/onComplete commented out, but as soon as I attempt to set a state there, the animation doesn't run. I have a video of the behavior not working correctly when I use states: Link I don't know if this is helpful but here is pretty much the complete context to the situation: function CardCarousel({ className }: Props) { // Middle Card let cardOneRef = useRef<HTMLDivElement>(null); // Left Card let cardTwoRef = useRef<HTMLDivElement>(null); // Right Card let cardThreeRef = useRef<HTMLDivElement>(null); // Left Offscreen Card let cardFourRef = useRef<HTMLDivElement>(null); // Right Offscreen Card let cardFiveRef = useRef<HTMLDivElement>(null); let tempDiv = useRef<HTMLDivElement>(null); let width = useWindowSize(); const distanceFromEdgeBottomM = "-40%"; const distanceFromEdgeLR = "5%"; const distanceFromEdgeBottomLR = "-50%"; const offscreenDistanceFromEdgeBottom = "-70%"; const offscreenDistanceFromEdgeLR = "-20%"; // Have this update on screen width change useEffect(() => { if (typeof window !== "undefined") { gsap.set(cardOneRef.current, { left: width / 2 - width * 0.22 / 2, bottom: distanceFromEdgeBottomM, rotation: 0 }); gsap.set(cardTwoRef.current, { left: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: -9, transformOrigin: "left 50%" }); gsap.set(cardThreeRef.current, { right: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: 9, transformOrigin: "right 50%" }); gsap.set(cardFourRef.current, { left: offscreenDistanceFromEdgeLR, bottom: offscreenDistanceFromEdgeBottom, rotation: -18, transformOrigin: "left 50%" }); gsap.set(cardFiveRef.current, { right: offscreenDistanceFromEdgeLR, bottom: offscreenDistanceFromEdgeBottom, rotation: 18, transformOrigin: "right 50%" }); } }, [width]) function animateButtonRight() { gsap.timeline({ // onStart: () => setRightDisabled(true), onComplete: () => { tempDiv = cardFiveRef; cardFiveRef = cardThreeRef; cardThreeRef = cardOneRef; cardOneRef = cardTwoRef; cardTwoRef = cardFourRef; cardFourRef = tempDiv; // setRightDisabled(false); } }) .to(cardThreeRef.current, { left: "", right: offscreenDistanceFromEdgeLR, bottom: offscreenDistanceFromEdgeBottom, rotation: 18, duration: 1.0, ease: Power3.easeInOut, transformOrigin: "right 50%" }) .set(cardThreeRef.current, { left: offscreenDistanceFromEdgeLR, right: "", bottom: offscreenDistanceFromEdgeBottom, rotation: -18, transformOrigin: "left 50%" }) .to(cardOneRef.current, { left: "", right: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: 9, duration: 1.0, ease: Power3.easeInOut, transformOrigin: "right 50%", delay: 0.1 }, 0) .to(cardTwoRef.current, { right: "", left: width / 2 - width * 0.22 / 2, bottom: distanceFromEdgeBottomM, rotation: 0, duration: 1.0, ease: Power3.easeInOut, delay: 0.2 }, 0) .to(cardFourRef.current, { right: "", left: distanceFromEdgeLR, bottom: distanceFromEdgeBottomLR, rotation: -9, duration: 1.0, ease: Power3.easeInOut, delay: 0.3, transformOrigin: "left 50%" }, 0) } return ( <div className={classnames(styles.CardCarousel, className)}> <button onClick={animateButtonRight}>Move</button> <div ref={cardOneRef} className={styles.card}><h1>This is main initial text</h1></div> <div ref={cardTwoRef} className={styles.card}><h1>This is left initial text</h1></div> <div ref={cardThreeRef} className={styles.card}><h1>This is right initial text</h1></div> <div ref={cardFourRef} className={styles.card}><h1>This is offscreen left initial text</h1></div> <div ref={cardFiveRef} className={styles.card}><h1>This is offscreen right initial text</h1></div> </div> ) } Link to comment Share on other sites More sharing options...
Solution Author Solution Share Posted June 23, 2022 Ok so Instead of using states, I simply used gsap.set(buttonRef.current, {disabled:true}) inside of the timeline which served the same purpose. I'm not sure how to handle states in the future, but this worked for now. Link to comment Share on other sites More sharing options...
Share Posted June 23, 2022 Consider putting your animateButtonRight function in a useCallback. Currently, any time your component re-renders, it will recreate that function. Changing component state will cause that component to re-render. So every time you are changing state, you are creating a new animateButtonRight function with a new timeline, etc. const animateButtonRight = useCallback(() => { // gsap here }, []) // <- empty dependency array so that your function never re-renders While your solution to set the button to disabled works, it's better to handle this with state to keep the DOM and VDOM synced. 2 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