NickWoodward Posted February 11, 2022 Share Posted February 11, 2022 Hello! So I've got some admittedly poor code for adding content to a DOM element when a menu item is clicked. The flow goes a like this: `click menu item => remove children from wrapper => add new items to wrapper`. I'd like to click the menu element, have the wrapper animate its opacity to zero, remove and add items (as above), and then animate back in. Pretty simple. The problem is my thought process is always quite basic when it comes to animations. I immediately jump to something like this: gsap.fromTo('.container', {opacity: 1}, {opacity: 0}, onComplete: async() => { await doAsyncThings(); changeContent(); gsap.fromTo('.container', {opacity:0}, {opacity:1}) } ); Which even I'm sure is fairly rubbish 😄 A glaring problem that I can see is that an async function takes time, and clicking another menu item won't stop the previous animation running. I could set a flag to prevent any further menu items from being clicked while the animation is running - but that's another rubbish approach (who wants to click a menu item, then not be able to change your mind before the content has animated in?) I no doubt should be trying to use and reverse a timeline, but the fact that I'm altering the DOM element, rather than replacing it, is confusing me somewhat. Would appreciate help with a more sensible approach (for the animation rather than the editing/adding of elements) if anyone has a second 😊 Thanks, Nick See the Pen BamZNod?editors=1011 by nwoodward (@nwoodward) on CodePen Link to comment Share on other sites More sharing options...
mikel Posted February 11, 2022 Share Posted February 11, 2022 Hey @NickWoodward, How about this concept? See the Pen yLPXeWj??editors=0110 by mikeK (@mikeK) on CodePen Happy tweening ... Mikel 2 Link to comment Share on other sites More sharing options...
NickWoodward Posted February 11, 2022 Author Share Posted February 11, 2022 Hi @mikel, Thanks, that's a very cool way of doing it, and one that I'm definitely going to put in a pen so I can use it/learn for next time, but is there any easy way that the wrapper can remain the same and have the content edited (as is done in the changeContent() function), rather than having 3 distinct sections? I'm trying to animate code from an old project and I made some bad design decisions when learning 😬 No worries if not, it'll probably be good for me to refactor my terrible old code Link to comment Share on other sites More sharing options...
Cassie Posted February 11, 2022 Share Posted February 11, 2022 Hey Nick! Maybe something like this? I wouldn't worry about doing async / await for the content being changed (unless it's taking ages, in which case, you probably need to look into making that faster) See the Pen YzEQqOV?editors=1011 by GreenSock (@GreenSock) on CodePen 3 Link to comment Share on other sites More sharing options...
NickWoodward Posted February 11, 2022 Author Share Posted February 11, 2022 Hi @Cassie! Nice solution, but it's unfortunately not quite what I'm after, although I might be able to adapt it 😊 I'm not actually trying to toggle the content by pressing the same button, I'm trying to have new content displayed when a different button is pressed. That content is fetched from a server, so I'm kind of stuck with the async call, and the problem comes when the button is pressed but the animation can't happen until the results are back from the server. In the mean time you can press a different button, which leads to both animations running. IE my code looks ugly because of both ignorance and necessity! Hope that makes sense! Nick Link to comment Share on other sites More sharing options...
Cassie Posted February 11, 2022 Share Posted February 11, 2022 Ah, alright I get you! Maybe invalidate() and.then() would be a good fit for this? See the Pen abVwZXV?editors=0011 by GreenSock (@GreenSock) on CodePen https://greensock.com/docs/v3/GSAP/Timeline/then() https://greensock.com/docs/v3/GSAP/Timeline/invalidate() Link to comment Share on other sites More sharing options...
Cassie Posted February 11, 2022 Share Posted February 11, 2022 ah wait no that won't work. 🤔 Link to comment Share on other sites More sharing options...
Cassie Posted February 11, 2022 Share Posted February 11, 2022 Mmm. I'm not great with promises, but I guess the issue is more that if you're calling an animation when a promise resolves, that animation's still going to get called when it resolves - regardless of whether another button's been pressed. invalidate() is the GSAP piece of the puzzle - but I guess you also need a way to cancel the first promise?https://stackoverflow.com/questions/30233302/promise-is-it-possible-to-force-cancel-a-promise You could do a count to see if it's the last button press but that doesn't feel good to me. Lots of wasted effort for fetches that you never actually see. See the Pen bGYRBvM by GreenSock (@GreenSock) on CodePen 1 Link to comment Share on other sites More sharing options...
NickWoodward Posted February 12, 2022 Author Share Posted February 12, 2022 Hi @Cassie, Having messed around with @mikel's approach with sections, and looking at your code, this is definitely an async problem that was happening despite the animations - I think it was just hidden because I was wiping the divs in my original project so I couldn't see the effects. I looked at the link you provided and it reminded me that Axios (which I'm using) has cancel tokens, so I guess that's how I should approach it? IE have any button press cancel the previous request, and have the animation run in the `.then()` block. Although from memory Axios is a PITA when it comes to what it considers a failed response 😄 Anyway, thanks a lot! 1 Link to comment Share on other sites More sharing options...
mikel Posted February 12, 2022 Share Posted February 12, 2022 Hey NICK, Instead of sections you could also use the GSAP TextPlugin. See the Pen oNowzWm?editors=0010 by mikeK (@mikeK) on CodePen Happy tweening ... Mikel Link to comment Share on other sites More sharing options...
GreenSock Posted February 12, 2022 Share Posted February 12, 2022 I didn't have a lot of time to read through everything in detail, but the way I'd typically approach a scenario that boils down to multiple states that may have an animation going between them (and you need to be able to dynamically redirect) is sorta like: let stateTransition; function gotoState(state) { if (stateTransition && stateTransition.isActive()) { // do whatever to get it back to the "from" state, so that could be // stateTransition.reverse() or just edit the DOM here (whatever your app requires) // then just call this function again when it's done, maybe in an onComplete // or calculate the time it'll take and just use a delayedCall()... gsap.delayedCall(0.5, () => gotoState(state); return } // do state transition... stateTransition = gsap.to(...); } Link to comment Share on other sites More sharing options...
NickWoodward Posted February 13, 2022 Author Share Posted February 13, 2022 10 hours ago, GreenSock said: I didn't have a lot of time to read through everything in detail, but the way I'd typically approach a scenario that boils down to multiple states that may have an animation going between them (and you need to be able to dynamically redirect) is sorta like: let stateTransition; function gotoState(state) { if (stateTransition && stateTransition.isActive()) { // do whatever to get it back to the "from" state, so that could be // stateTransition.reverse() or just edit the DOM here (whatever your app requires) // then just call this function again when it's done, maybe in an onComplete // or calculate the time it'll take and just use a delayedCall()... gsap.delayedCall(0.5, () => gotoState(state); return } // do state transition... stateTransition = gsap.to(...); } Hi @GreenSock Appreciate the explanation, but for the time being that definitely feels above my paygrade - I'll definitely come back to this thread at a later date when I inevitably have an issue similar to this again, and hopefully I'll be good enough to understand! Link to comment Share on other sites More sharing options...
NickWoodward Posted February 13, 2022 Author Share Posted February 13, 2022 I've solved the async problem by cancelling the Axios tokens in the real project. I think that sorts me out. But I still need a simple solution to this: Can I have a timeline where animation fades an element out, pauses, I add the content, then fades in? A bit like this: const content = await getContent(); const tl = gsap.timeline() .to(element, { autoAlpha:0, duration:0.5, onComplete: () => { clearElement(); element.addAdjacentHTML('afterbegin', content); tl.reverse(); } }) tl.play(); ^ that's in theory at least anyway. I'm just creating a more useful codepen with actual REST requests Link to comment Share on other sites More sharing options...
GreenSock Posted February 13, 2022 Share Posted February 13, 2022 Sure. My brain goes to this: let tl = gsap.timeline(); tl.to(parent, { autoAlpha: 0 }) .add(() => { clearElement(); element.addAdjacentHTML('afterbegin', content); }) .to(parent, { autoAlpha: 1 }) But if the content is gonna take some time to load and you want to wait to fade it back in until it's ready, just separate that logic a bit - start the content loading while you do a simple fade out. Then when your content is ready (doesn't matter if you're using a callback, promise, whatever), just put the fade in animation there. Don't feel like you need to mash it all into a single timeline because that model doesn't fit when you've got an arbitrary amount of lag introduced in the middle. 1 Link to comment Share on other sites More sharing options...
NickWoodward Posted February 13, 2022 Author Share Posted February 13, 2022 @GreenSock Nice, thanks - just had a look at the add() function - had no idea you could add a callback like that - very cool.@mikel @Cassie Thanks to both of you too, really helped out (especially that link Cassie - the comments pointed me to an edge case for axios instances). Anyway, here's my ugly codepen that works kind of the way I want. Limit the network and click between the buttons and it *should* work correctly (I might have to debounce the button clicks for the quicker network responses, but slower ones work 👍). See the Pen BamZNod?editors=1111 by nwoodward (@nwoodward) on CodePen 3 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