beau_dev Posted May 24, 2020 Share Posted May 24, 2020 First of all, I hope everyone is well & healthy. My apologies for not creating a codepen usecase–the complexity is a bit over my head for this example to put into codepen. I am posting a URL for an app (for phones only at the moment), I hope it's okay. (App URL) I have the beginning of an app displaying images illustrating the effects of mix-blend-mode properties. The bottom right button cycles through 15 mix-blend-modes. Sadly, it has various animation problems atm 🙄😭... Principally, I'm running into the animation of the<div> component (displaying mode names) that slides up (y-axis) upon click. const ModeNameDisplay = ({ mode }) => { let txtBox = useRef(null) useEffect(()=> { gsap.set(txtBox, { y:25}) const tl = gsap.timeline({ defaults: { delay: .3 } }) tl .to(txtBox, { duration: .2, y: 0 }, '-=0') .to(txtBox, { duration: 1, y: 25 }, '+=3') }) return ( <ModeNameContainer ref={elem => txtBox = elem}> <h1 width="100%" height="100%">{mode}</h1> </ModeNameContainer> ); } The simple animation, itself, works nicely: upon new props received, the timeline runs quite smoothly. –Until, however, user clicks again before the initial TL finishes. An impatient user clicks, sends a new prop, all hell breaks loose...and I feel like I should put away GSAP before I hurt myself. The issue, I recognize, is that I'm not handling events (new user clicks) that occur before the timeline ends. From searching the forums, I suspect it may be an issue with cleaning the cache of a timeline upon a new event(click). The ideal behavior that I'd like to achieve would be: IF User interrupts previous TL with a click, THEN a new TL restarts from scratch (textbox hidden) and a new prop (mode) displaying a new mode name that slides into view. I have an inclination that either of the following might solve my problem: .invalidate() .kill().restart() Although they are well written, I simply can't decipher the docs so as to apply their lessons to this case. I would very much appreciate any help with this mess & hope everyone is healthy and staying safe. Many, many thanks in advance if you come across this and are able to offer some advice. Cheers, Beau REPO Link to the relevant file Link to comment Share on other sites More sharing options...
PointC Posted May 24, 2020 Share Posted May 24, 2020 I'm not sure I follow everything completely, but you can always use the isActive() method to prevent clicks. If you want to allow a user to interrupt the playback, that's certainly doable too. In that case, I'd probably check if the timeline is active and force it to finish with .progress(1). Then you can use it again with new targets, properties etc. You could also kill or invalidate if you like. Here's a simplified example which might help. See the Pen mdeoNZb by PointC (@PointC) on CodePen You can see that the timeline is populated on click. If one of the boxes is active, the timeline is immediately set to progress(1). This prevents any weird overlaps or elements getting out of position. Hopefully that gives you some ideas. Happy tweening. 3 Link to comment Share on other sites More sharing options...
beau_dev Posted May 24, 2020 Author Share Posted May 24, 2020 @PointC That looks like it will help lots. Thanks so much. I'll take a look at it tonight! -Beau 2 Link to comment Share on other sites More sharing options...
beau_dev Posted May 25, 2020 Author Share Posted May 25, 2020 After spending the day with this. I couldn't seem to get if (tl && tl.isActive()) { tl.progress(1); } to run... the .isActive()... I couldn't get it to return true & thus executing the .progress(1) method. I discovered the error in my code, however... (for posterity, I'll note it). It's the nature of useEffect() in react. useEffect requires a dependency for the behavior that I expected. therefore: const ModeNameDisplay = ({ mode }) => { let txtBox = useRef(null) useEffect(()=> { gsap.set(txtBox, { y:25}) const tl = gsap.timeline({ defaults: { delay: .3 } }) tl .to(txtBox, { duration: .2, y: 0 }, '-=0') .to(txtBox, { duration: 1, y: 25 }, '+=3') }, [mode]) return ( <ModeNameContainer ref={elem => txtBox = elem}> <h1 width="100%" height="100%">{mode}</h1> </ModeNameContainer> ); } the deconstructed mode prop (visible just above the return statement) is the 2nd arg placed in the useEffect callback. Everything runs nicely, now. I elected not to go with the .to(txtBox, { duration: 1, y: 25 }, '+=3') I simply couldn't get that method to run. Many thanks for your help. I'd love to know if there's anything else I could try. It could be a react quirk. 🤔 Anyway... Thanks again. Cheers, Beau 2 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