Chromium Posted March 30, 2023 Share Posted March 30, 2023 So I'm going to try to explain this but I can't even understand what's causing it so I probably won't explain it right. I've got a timeline with a few tweens in it. The timeline is created on page load but the tweens are only added on the click of a button. Every time the button is clicked, this line is executed first: if (t1) t1.progress(1).kill() As I understand it, the above line is meant to kill any previously (still) running timelines (in scenarios where the button is clicked too fast for example). So far so good, this all worked fine. This may be irrelevant but I should mention that I do run a t1.pause() by the end of the timeline (because one of the tweens is an infinite tween that I'd wanted to stop once the timeline finished). Of relevance: one of the tweens in this timeline is running the TextPlugin and setting a text value provided through a variable in JS. What this meant is that on each subsequent button click, I'd need to restart the timeline but with a different value for the TextPlugin tween. I assumed that the easiest way to do this is to simply re-run all the functions that added the tweens to the timeline (as this would automatically also add a new tween with the updated TextPlugin value). However, this didn't work out as expected... and I'm not sure how to explain it either. On subsequent button clicks, I tried:t1.progress(1).kill(), t1.progress(0).kill(), t1.progress(0), t1.play(), and a mix of them together... none of these solved the issue. The only thing that finally worked was simply re-defining the timeline variable (t1) on each subsequent button click (after running the t1.progress(1).kill() in case anything is running already). Since I've already resolved it, I'm only posting this hoping that someone here knows exactly why re-defining the timeline variable was necessary to get this to work? And if so, is there a more appropriate way for such a scenario (as opposed to re-defining the timeline) that I've missed? Link to comment Share on other sites More sharing options...
mvaneijgen Posted March 30, 2023 Share Posted March 30, 2023 Hard to say without a minimal demo, but you could also try use tl.restart().invalidate() https://greensock.com/docs/v3/GSAP/Timeline/invalidate() https://greensock.com/docs/v3/GSAP/Timeline/restart() Link to comment Share on other sites More sharing options...
Chromium Posted March 30, 2023 Author Share Posted March 30, 2023 This is unfortunate, because I was hoping you'd have a better understanding of what was happening. If I could make a demo of this, then I'd have the answer to what is happening and wouldn't even have needed to post this ? I've tried adding t1.invalidate() after the t1.progress(1).kill() but that didn't seem to do anything. I've also tried t1.restart().invalidate() as you suggested but that doesn't make sense to me for my use case... as that seems to simply restart the timeline with the same old value for the TextPlugin tween. I've also tried t1.time(0).invalidate() to no avail. But at this point I'm just guessing, as I simply can't wrap my head around why there would ever be a case (such as this) where I'd need to re-write the t1 variable... I thought t1.progress(1).kill() would simply be enough. I was hoping that having found a solution to the problem would replace the need for a demo, and it would be one of those things that immediately clicks to you guys because (and I'm presuming) that there's only very few case scenarios that would require a re-write of the t1 variable (and that there's some explanation to having the need to do this and/or an equivalent standard timeline method to re-writing the t1 variable). This sucks but I guess I'll have to settle to never understanding why my solution worked! Thanks anyway Note: That demo on the invalidate page really helped clarify the purpose of that method in my head. I previously thought it might be part of the more appropriate solution to my use case, but after looking at that demo, I don't think it applies to my use case. At least, from the way I understand it, what I simply require is that on subsequent clicks, my timeline needs to reset and add (and immediately start playing) new tweens in place of the old ones (they're more or less the same tweens with the exception of the TextPlugin tween just having a different value). It's just that in my head, I didn't expect that this use case would require a re-write of the t1 variable, or for that matter, I didn't think any use case required that, but I may be wrong and that my use case is an exception among some others haha. Link to comment Share on other sites More sharing options...
mvaneijgen Posted March 30, 2023 Share Posted March 30, 2023 20 minutes ago, Chromium said: This is unfortunate, because I was hoping you'd have a better understanding of what was happening I might, but I have no idea what you're doing, because I can't see the code. 20 minutes ago, Chromium said: If I could make a demo of this, then I'd have the answer to what is happening and wouldn't even have needed to post this ? You can make a demo of your code, right. With the current issue/solution you have? Or several with all the versions you've tried, that way we have a better understanding what is going on. It is a bit like the saying "a picture is worth a thousand words", same goes here for minimal demos. 1 Link to comment Share on other sites More sharing options...
Chromium Posted March 30, 2023 Author Share Posted March 30, 2023 Quote It is a bit like the saying "a picture is worth a thousand words", same goes here for minimal demos. I don't disagree with this, but I've already addressed why this isn't available. Also, I don't think any more of my code would reveal anything different as I've already identified what I believe is the choking point in it (as evidently portrayed by my provided solution above). Plus, I get a headache just thinking of trying to wrap my head around trying to replicate what I don't understand. ? At the risk of restating myself, I was hoping for more of an abstract discussion on the issue. But if you're not interested and/or unable to, I understand. Thanks for your time. Link to comment Share on other sites More sharing options...
Cassie Posted March 30, 2023 Share Posted March 30, 2023 Reading abstract descriptions about code is incredible confusing I'm afraid. The volunteers in here answer a lot of questions and from experience, all of use find it easier to quickly tinker with a demo and gain our own understanding. There's often a little disconnect between the perceived problem and the actual problem and that is never evident on a description alone. https://xyproblem.info/ An explanation of what you're trying to achieve and a simple demo is far more helpful than explanations of the perceived issue or solution. Sorry we haven't been able to help here yet, but I'm sure we'll be able to if you can provide us with a demo and an explanation of your goal. 1 Link to comment Share on other sites More sharing options...
Chromium Posted March 30, 2023 Author Share Posted March 30, 2023 Quote Reading abstract descriptions about code is incredible confusing I'm afraid. I'll have to respectfully disagree. I have literally received helpful explanations on here with even less information provided before. Not every question requires code to be answered! 2 hours ago, Cassie said: and an explanation of your goal I've provided that... twice! But you've both ignored 90% of my explanation and asked for code or nothing. What you're saying is that a simple question like "are there cases which would require a re-write of the timeline variable? And if so, are they few or many? Any examples would be nice." requires code from my end for you to answer? If so, I'll have to respectfully disagree. I feel you either wanna help your community achieve their best potential using your tools (by going above and beyond which has been my experience here up until now) or you wanna argue about something we both agree about but is not always an option... your call. Link to comment Share on other sites More sharing options...
Solution GreenSock Posted March 30, 2023 Solution Share Posted March 30, 2023 When you kill() an animation, it's to make it completely dead...not to reuse it (resurrect) it and add new tweens to it. I bet you still had all the original tweens in there, and you were just adding more...and more...and more each time and trying to play a timeline that you said should be killed which is generally a bad idea. Perhaps you meant to clear() the timeline? 13 hours ago, Chromium said: Of relevance: one of the tweens in this timeline is running the TextPlugin and setting a text value provided through a variable in JS. What this meant is that on each subsequent button click, I'd need to restart the timeline but with a different value for the TextPlugin tween. I assumed that the easiest way to do this is to simply re-run all the functions that added the tweens to the timeline (as this would automatically also add a new tween with the updated TextPlugin value). However, this didn't work out as expected... and I'm not sure how to explain it either. This is exactly why we need a minimal demo. I'd want to see how you're doing this. It sounds like maybe you kept all the old tweens in the timeline and added a bunch of new ones to the end, and perhaps you restarted it so that it was playing from the beginning with the old tweens (the new ones you added later in the timeline...and maybe they got placed AFTER your infinitely-repeating stuff which means WAAAAAAAY out time-wise?) 7 hours ago, Chromium said: (and I'm presuming) that there's only very few case scenarios that would require a re-write of the t1 variable (and that there's some explanation to having the need to do this and/or an equivalent standard timeline method to re-writing the t1 variable). It has nothing to do with the variable itself, and it's actually FAR more common to just create a new timeline when you want to animate to new values than it is trying to empty and then re-use the same timeline instance each time. In fact, I'd say it's a mistake I see people make - thinking they're supposed to just have one big timeline for their entire project, and just keep shoving more and more and more tweens into it based on interactions. It's quite wasteful, actually, because typically the user has no intention of doing anything with the old tweens that were at the start of that timeline...so they're just keeping those around in the timeline for no reason and making the timeline longer and longer. Those old ones should be released for garbage collection, but you're not allowing that if you just keep them in the timeline forever. If you just create a new timeline and kill() the old one, that old one (and its contents) can be garbage collected and you needn't worry about them anymore. GSAP was built to be very efficient in that way. 2 hours ago, Chromium said: I'll have to respectfully disagree. I have literally received helpful explanations on here with even less information provided before. Not every question requires code to be answered! @Chromium trust me - we're trying to help. We do this every day for hours and hours. When we ask you for a minimal demo, it's because it's very important for us to understand the context of your confusion and decide how to best address any misunderstandings we identify via the code. When everything is just conceptual and abstract, it can obfuscate the real issues. I can't tell you how many times I've seen people ask questions here and they're totally confused about something and very reluctant to provide a minimal demo because in THEIR minds, the minimal demo won't help, but then when they finally provide one we instantly see why they were having the problem and it allows everyone to get on the same page. Please trust the process - we're not trying to annoy you by pestering you for a minimal demo. I totally get that you don't know why things were breaking and that's completely fine. If you just took the time to throw together a very simple CodePen that illustrated the problem...a BROKEN CodePen...I think you'd find that we'd be able to offer you far better insight that'd serve you longer-term. In my experience, questions like this almost always stem from an engineering problem with the way you may be structuring your code (as mentioned earlier, I see people coding as if they're supposed to always reuse the same timeline for everything, or maybe they think that defining a variable, passing that variable to a tween and then later changing that variable value would somehow magically make the tween animate to that new value...stuff like that is hard to identify when speaking high-level abstractions rather than just seeing what they're doing in code). Some people (not saying this is you) are reluctant to post a minimal demo because they're self-conscious about their code, assuming they'll be harshly judged for mistakes. I can assure you that won't be the case here. Others don't want to provide a minimal demo because they think it'll take too long, so they'd rather have the volunteers here spin their wheels trying to imagine what the problem might be, but it almost always ends up taking everyone a lot more time and frustration compared to if a minimal demo was created. 2 hours ago, Chromium said: I feel you either wanna help your community achieve their best potential using your tools (by going above and beyond which has been my experience here up until now) or you wanna argue about something we both agree about but is not always an option... your call. We definitely want to help the community achieve their best potential using GSAP which is why these forums exist. I think you'll find that in general, the people who help in this community go WAY above and beyond. Most of the people who post here aren't Club GreenSock members like you (thank you! ?) and never pay us a dime, but we do our best to help them too. I'm glad to hear that your experience thus far has been so positive, and I'm sorry to hear this most recent interaction left you disappointed. As a brand, what we really try to do at GreenSock is not only deliver the best tools but value people's TRUST. We want you to feel like it's not just some library that'll be irrelevant in 2 years like most other ones. We want you to feel like we'll be there for you for the next decade+ and when you're stuck, we've got your back. It's a very unique thing in the JS world. I hope you'll offer some grace and understanding when it comes to our efforts to deliver the help you need by requesting some information that we believe would be essential for doing so. 5 Link to comment Share on other sites More sharing options...
Chromium Posted March 30, 2023 Author Share Posted March 30, 2023 Quote I bet you still had all the original tweens in there, and you were just adding more...and more...and more each time and trying to play a timeline that you said should be killed which is generally a bad idea. Understood. However, my understanding of when to use t1.progress(1).kill() is in cases such as when a timeline is played on a button click and we want to prevent spamming the button click from breaking the timeline. Is this still the case? Quote Perhaps you meant to clear() the timeline? Good suggestion, I thought for sure that was what made more sense to use in my case. But it didn't work when running it in place of t1.progress(1).kill() or when running it after. Quote and it's actually FAR more common to just create a new timeline when you want to animate to new values than it is trying to empty and then re-use the same timeline instance each time. In fact, I'd say it's a mistake I see people make - thinking they're supposed to just have one big timeline for their entire project, and just keep shoving more and more and more tweens into it based on interactions. This is exactly what I was looking to find out. I was looking for the most semantic/efficient approach and presumed that my killing and re-initializing the timeline variable to a new one, was not the semantic/traditional way to go about this. Quote Please trust the process - we're not trying to annoy you by pestering you for a minimal demo. While I completely understand that, I'd more likely be inclined to cooperate if I felt that an attempt was at least made to answer some of my concerns that really don't require a demo such as the question answered by you as quoted above, which really covers my main concern (and this in turn saves me from having to debug this issue any further and/or create a demo at all!). Quote If you just took the time to throw together a very simple CodePen that illustrated the problem...a BROKEN CodePen...I think you'd find that we'd be able to offer you far better insight that'd serve you longer-term. 100% agreed! Except for scenarios like this one where I'm seeking a simple affirmation that "I'm not doing something wrong by redefining a timeline variable" and that "it's a perfectly valid approach (even preferred over others)", as you've mentioned. Would literally solve this ticket! Haha Quote Others don't want to provide a minimal demo because they think it'll take too long, so they'd rather have the volunteers here spin their wheels trying to imagine what the problem might be, but it almost always ends up taking everyone a lot more time and frustration compared to if a minimal demo was created. And I completely agree, which is why I would usually provide a Codepen once I've noticed that we're not arriving at any fruitful conclusion (or that I'm asked to provide one in a follow-up to a conversation that is leading to more confusion). The thing is, in this case, I feel I wasn't met half-way like I usually am. I was immediately asked to provide a CodePen... and when I provided a reason as to why I don't have one, I was met with a 2nd request to provide a CodePen, immediately, prior to a decent attempt at answering at least some of my questions. And then when I responded with this: Quote I'll have to settle to never understanding why my solution worked! Thanks anyway I merely had a mild concern, and so by this point, I figured it's not worth hassling you guys anymore nor the time spent to write a CodePen, as I was simply exhausted by the time I figured out the solution to my problem in production. Haha And yet, I was met with a response that answered none of my concerns and only re-iterated the need for a CodePen (for the 3rd time in a row!)... I feel this is a bit too pushy, don't you? Your response was the exception here, so I appreciate that. That being said, I very much appreciate the community you guys have created, and love the standard of support you always manage to deliver (it's very rare and makes you one of the top if not the top support communities out there!). I appreciate your detailed and thorough response, Jack, and it has (once again!) delivered exactly what I needed; the peace of mind that I am not misusing the timeline variable by re-initializing it. ? 1 Link to comment Share on other sites More sharing options...
GreenSock Posted March 30, 2023 Share Posted March 30, 2023 1 hour ago, Chromium said: my understanding of when to use t1.progress(1).kill() is in cases such as when a timeline is played on a button click and we want to prevent spamming the button click from breaking the timeline. Is this still the case? Probably, but I'm not sure why you're doing the .progress(1) part - are you sure you want to force things to completion? Imagine your element animates to x: 100 on mouseenter and back to 0 on mouseleave. Let's say they mouse over...so it starts animating to 100 and then the user mouses out when it gets to 50...do you really want it to suddenly jump to 100 before it starts animating back to 0? This is a more typical approach: let enterAnim; function onMouseEnter() { enterAnim && enterAnim.kill(); enterAnim = gsap.timeline(); enterAnim.to("#el", {x: 100}); // I assume you'd add more to the timeline, otherwise it's simpler to just use a gsap.to() } function onMouseLeave() { enterAnim.kill(); enterAnim = gsap.timeline(); enterAnim.to("#el", {x: 0}); } That ensures that the old timeline/animation is killed and then you just create something new that animates from the CURRENT position, so it's all nice and smooth. No jumps. 1 hour ago, Chromium said: Good suggestion, I thought for sure that was what made more sense to use in my case. But it didn't work when running it in place of t1.progress(1).kill() or when running it after. Without a minimal demo I can't say for sure, but my guess is that you cleared the timeline and dropped new stuff in there, but forgot to restart() it or something. Let's say the playhead was in-progress at a time of 5 seconds and then you clear() it and add 8 seconds worth of animation to that timeline. Well, it'd jump right to the 5-second spot in those animations (as it should) because that's where the playhead is. Remember, every animation has a parent animation (except the root timeline), so the timeline that you were adding things to had a startTime() on the parent timeline that didn't magically change when you emptied it and added more animations. The playheads align all the way up the chain. 1 hour ago, Chromium said: Except for scenarios like this one where I'm seeking a simple affirmation that "I'm not doing something wrong by redefining a timeline variable" and that "it's a perfectly valid approach (even preferred over others)", as you've mentioned. Would literally solve this ticket! Maybe, but part of the problem here is that you're assuming we know exactly what you mean by "redefining a timeline variable". What I think you actually meant was "creating an entirely new timeline and assigning it to an existing variable after killing the original timeline instance"(?) Sample code would have made that crystal clear whereas talking in general concepts allows a lot of fuzziness to creep in. 1 hour ago, Chromium said: I was met with a response that answered none of my concerns and only re-iterated the need for a CodePen (for the 3rd time in a row!)... I feel this is a bit too pushy, don't you? I actually don't I totally understand your frustration but I honestly think it was very wise for the other moderators to persist in their request for a minimal demo. One of the main challenges in these forums is that when people post a question, they totally understand what they're trying to explain. It makes perfect sense in their head. But often it's as clear as mud to most of the rest of us, especially if we're sleep-deprived As @mvaneijgen said, a CodePen is the equivalent of "a picture is worth a thousand words". Had we seen what you were doing in the code, I bet one of us moderators would very quickly identify the problem and politely explain the solution and offer valuable advice. You wanted them to meet you halfway but I'm not entirely sure what that'd look like if they didn't understand the challenge you were facing. 1 hour ago, Chromium said: That being said, I very much appreciate the community you guys have created, and love the standard of support you always manage to deliver (it's very rare and makes you one of the top if not the top support communities out there!). I appreciate your detailed and thorough response, Jack, and it has (once again!) delivered exactly what I needed; the peace of mind that I am not misusing the timeline variable by re-initializing it. ? Yay! ? And you're exactly correct - it's absolutely fine to kill() an old timeline instance and create a new one in cases like this. I do it all the time. Glad this is cleared up now. Happy tweening! 3 Link to comment Share on other sites More sharing options...
Chromium Posted March 31, 2023 Author Share Posted March 31, 2023 Quote Probably, but I'm not sure why you're doing the .progress(1) part - are you sure you want to force things to completion? Because t1.kill() alone won't stop the timeline from executing if it's still executing while the button is clicked again, so I either have to precede it with a .progress(0) or .progress(1). However, most of the time .progress(0) just makes things worse for some reason (in such cases .progress(1) works). Quote Let's say the playhead was in-progress at a time of 5 seconds and then you clear() it and add 8 seconds worth of animation to that timeline. So for my understanding, in this scenario you mentioned, when should I restart the timeline? Right after clearing it or after adding the 8 seconds worth of animations? And is it an option to do t1.clear().time(0) or t1.clear().progress(0) in order to resolve this very issue? Quote Maybe, but part of the problem here is that you're assuming we know exactly what you mean by "redefining a timeline variable". What I think you actually meant was "creating an entirely new timeline and assigning it to an existing variable after killing the original timeline instance"(?) Sample code would have made that crystal clear whereas talking in general concepts allows a lot of fuzziness to creep in. Fair enough. However, I do think this can be followed up with a question for clarification instead of jumping to a CodePen request only... after all, I didn't put so much effort into typing and perfecting a fully comprehensible (to the best of my capability) post, only to be met with a brick wall asking for a CodePen or nothing (in all 3 responses that followed). Quote I totally understand your frustration but I honestly think it was very wise for the other moderators to persist in their request for a minimal demo. I'd like to stress the fact that I have no problem with persisting on a CodePen request, but making this the ONLY answer is something I take issue with. When many things could have been answered without it as you yourself have proficiently proven in your first response... unless you'd like to say that you're capable of unique understanding that others aren't... and I have a feeling that this is something that some might +1 about you! lol I may be spoiled to say this, but your response is the kind of (meeting me half-way) response I expected when I came here... you asked for a CodePen, but you also provided valuable clues and insights as to what might be going on. It might have been a wild guess from your end, but I'm the type of person who likes to experiment and fiddle around on their own before jumping to a blank slate and a CodePen. And wild but curated guesses from experts like you play a huge role in getting me closer, if not to a solution, then to a problem that I can actually understand and convey in a CodePen. Suffice to say, I was not at the point where I had enough information about my own problem when I began writing this post, and that is why I like to ask some high-level abstract questions on here first, prior to jumping into a CodePen. I hope this makes sense! Thanks again for keeping up with me, I appreciate it. Link to comment Share on other sites More sharing options...
GreenSock Posted March 31, 2023 Share Posted March 31, 2023 23 minutes ago, Chromium said: Because t1.kill() alone won't stop the timeline from executing if it's still executing while the button is clicked again, so I either have to precede it with a .progress(0) or .progress(1). However, most of the time .progress(0) just makes things worse for some reason (in such cases .progress(1) works). tl.kill() would just immediately stop that timeline from doing anything else. Let's say you're animating x from 0 to 100 linearly and you call kill() when it's halfway done - it would just leave x at 50. Why do you think calling tl.kill() "won't stop the timeline from executing if it's still executing while the button is clicked again"? I'm lost. Got a CodePen demo? (sorry, I couldn't resist). 23 minutes ago, Chromium said: So for my understanding, in this scenario you mentioned, when should I restart the timeline? Right after clearing it or after adding the 8 seconds worth of animations? And is it an option to do t1.clear().time(0) or t1.clear().progress(0) in order to resolve this very issue? Is your goal to start it from the beginning? I assume so. You can do that many different ways, but in my opinion the simplest and clearest is .restart(). 23 minutes ago, Chromium said: unless you'd like to say that you're capable of unique understanding that others aren't... and I have a feeling that this is something that some might +1 about you! lol Ha. Well there may be a little bit of truth in that, but only because I authored all the tools and have been actively supporting them in these forums for almost 15 years...it's definitely not because I'm smarter or more capable than anyone around here. Trust me, that ain't true. I'm just old seasoned with experience. @Cassie, @mvaneijgen and the other mods are super sharp and our community is incredibly blessed to have them. ? Cheers! Link to comment Share on other sites More sharing options...
Chromium Posted March 31, 2023 Author Share Posted March 31, 2023 Quote tl.kill() would just immediately stop that timeline from doing anything else. Let's say you're animating x from 0 to 100 linearly and you call kill() when it's halfway done - it would just leave x at 50. So then this is a semantic/communication error on my end. I need the timeline to reset as well. And if I understood everything correctly, you need to tell the timeline to reset after killing it (and that can be through many ways). .progress(1) probably works best for my case because it would arrive at the expected end result (with all CSS properties added at the end), whereas .progress(0) fails mostly for me because it would reset the timeline to 0 before finishing to add the rest of said CSS properties to the DOM. So when the timeline runs again following a .progress(0), some properties (but not all) would be still there in the DOM added by the previous iteration of the timeline. If I understood all of the above correctly, this explains probably more than half my confusions and woes with timeline restarts! I'll have to experiment some more with other methods such as combining a t1.clear() with a t1.restart() (since those are probably cleaner to avoid that animation jump you mentioned). Thanks again Jack, this has been very enlightening. I'm excited to broaden my understanding of GSAP timelines! 1 Link to comment Share on other sites More sharing options...
GreenSock Posted March 31, 2023 Share Posted March 31, 2023 9 minutes ago, Chromium said: I need the timeline to reset as well. And if I understood everything correctly, you need to tell the timeline to reset after killing it (and that can be through many ways). You shouldn't do anything with a timeline after killing it. Consider it murdered...permanently. Technically it is possible to get some things to happen, but it's better to just consider kill() to be permanent. So tl.progress(0).kill() is more correct than tl.kill().progress(0). You said you want to "reset" the timeline which (at least in my head) means rewinding it to the beginning or maybe reverting...and you also said you wanted to do progress(1) which is the exact opposite - forcing it to the very end and completing. 13 minutes ago, Chromium said: So when the timeline runs again following a .progress(0), some properties (but not all) would be still there in the DOM added by the previous iteration of the timeline. This was another confusing thing for me to read. .progress(0) simply rewinds the playhead to the start. Why would only some (but not all) properties still be there? I assume by "in the DOM" you mean inline CSS styles(?) If you're running into something like that, please provide a minimal demo so we can see what's going on in your context. Link to comment Share on other sites More sharing options...
Chromium Posted March 31, 2023 Author Share Posted March 31, 2023 2 hours ago, GreenSock said: You shouldn't do anything with a timeline after killing it Got it. Will keep that in mind! Quote Why do you think calling tl.kill() "won't stop the timeline from executing if it's still executing while the button is clicked again"? I'm lost. If this is supposed to work, then I'll have to create a CodePen to see if I can replicate it (this might be a different ticket if I don't find time soon enough). In the meantime, do you happen to have any tutorials (preferrably with CodePens) showing a side-by-side difference of when it is best to use t1.clear().kill(), t1.progress(1).kill(), t1.progress(0).kill(), t1.restart().kill(), etc. And maybe some more samples of handling the click spam of a timeline? I feel like I've a bit of a foggy confusion in this area that causes me to fall into traps that I'm hoping I can avoid (if not now then at least in the future). On another note, my membership renewal is coming up and I wanted to let you know that it would be a lot more enticing if there was some kind of a renewal discount of like 20% or so (ideally applied by default). It might seem dumb, but this kind of stuff is usually a good incentive for me lol. Otherwise, it feels like the logical thing to do would be to wait until the next enticing GSAP update to renew again since it's the same price as a brand new membership fee. Link to comment Share on other sites More sharing options...
Cassie Posted March 31, 2023 Share Posted March 31, 2023 Yes, nail on the head here. Jack's capable of unique understanding that others aren't - He wrote the library, the rest of us are about a lightyear behind him in terms of understanding. Quote When many things could have been answered without it as you yourself have proficiently proven in your first response... unless you'd like to say that you're capable of unique understanding that others aren't... and I have a feeling that this is something that some might +1 about you! lol Quote You wanted them to meet you halfway but I'm not entirely sure what that'd look like if they didn't understand the challenge you were facing. 100% - I Read through your post multiple times and my brain just blanked. I couldn't picture what you were asking at all. In order to answer most questions in here I have to look at the code, run demos with console logs, check the docs, read old forum posts, write several attempts at a solution and test my own assumptions in code. I assume similarly with the other mods. We weren't being obtuse and I'm sorry it came off like that, we were just trying to help, and the way that people understand problems is different. 2 Link to comment Share on other sites More sharing options...
Chromium Posted March 31, 2023 Author Share Posted March 31, 2023 Quote Quote Why do you think calling tl.kill() "won't stop the timeline from executing if it's still executing while the button is clicked again"? I'm lost. If this is supposed to work, then I'll have to create a CodePen to see if I can replicate it (this might be a different ticket if I don't find time soon enough). Explanations like these, and code snippets like the one below, make it seem so simple, when in reality... well I'll just show you. Quote let enterAnim; function onMouseEnter() { enterAnim && enterAnim.kill(); enterAnim = gsap.timeline(); enterAnim.to("#el", {x: 100}); // I assume you'd add more to the timeline, otherwise it's simpler to just use a gsap.to() } function onMouseLeave() { enterAnim.kill(); enterAnim = gsap.timeline(); enterAnim.to("#el", {x: 0}); } Also, the comment in the snippet above mentions that it's simpler to use gsap.to() in the sample above... but how would you handle the hover spam if you did that? Here's an attempt at a CodePen that managed to give me a headache: See the Pen YzObmqE by fuad-zeyad-tareq (@fuad-zeyad-tareq) on CodePen 1) The above CodePen works perfectly fine, right? Not even an animation jump when click spamming... but according to you, Jack, and to my own logical sense of things... t1.progress(1) should create an animation jump, but somehow, it doesn't here... 2) Let's try to replicate your code snippet from above by making a simple change to this CodePen. Let's simply remove .progress(1) so we'd end up with only t1.kill() inside the onclick handler. According to you, Jack, and to your code snippet above, this should be sufficient to handle click/hover spamming, right? Well try that for this Code snippet. With .progress(1) removed, it will freeze mid-animation when you spam click it... 3) Maybe our CodePen is a little more complex than your code snippet from above, so maybe the change we made in our attempt 2 above is incomplete. Upon further inspection, logic dictates to me that the following code snippet might be best put outside of the onclick handler: const split = new SplitText("ul li", { type: "lines" }); So in an attempt to still make the spam click handling work without .progress(1), let's also move the code snippet above to outside of the click handler and try again. Just to recap, we now should have something like this: let t1 = gsap.timeline(); const split = new SplitText("ul li", { type: "lines" }); $('.click-me').on('click', function() { if (t1) t1.kill(); t1 = gsap.timeline(); t1.fromTo(split.lines, { opacity: 0, rotationX: -120, force3D: true, transformOrigin: 'top center -150', }, { duration: 3, opacity: 1, rotationX: 0, stagger: .02, onComplete: function() { split.revert(); } } ); }); Again, according to you Jack, this should be enough to handle the spam clicking of a button. And you know what, at first glance, spam clicking now seems to work just fine (without even an animation jump!). However, if you wait for the animation to finish after the first click, and then try another click... it will not work. So why's t1.kill() not enough on its own here? And why's t1.progress(1).kill() the most valid approach as seen in the initial CodePen? Quote In the meantime, do you happen to have any tutorials (preferrably with CodePens) showing a side-by-side difference of when it is best to use t1.clear().kill(), t1.progress(1).kill(), t1.progress(0).kill(), t1.restart().kill(), etc. And maybe some more samples of handling the click spam of a timeline? I feel like I've a bit of a foggy confusion in this area that causes me to fall into traps that I'm hoping I can avoid (if not now then at least in the future). This is why I think something like this should be a mandatory tutorial for anyone beginning work on a gsap timeline. Link to comment Share on other sites More sharing options...
GreenSock Posted March 31, 2023 Share Posted March 31, 2023 8 hours ago, Chromium said: 1) The above CodePen works perfectly fine, right? Not even an animation jump when click spamming... but according to you, Jack, and to my own logical sense of things... t1.progress(1) should create an animation jump, but somehow, it doesn't here... Yes it does jump. Let the animation partially finish and click again - everything jumps back to the start before playing again. That's precisely what I meant. But that's mainly because you're using a .fromTo() tween so of course that'd always start over at the beginning. I'm so glad you shared a CodePen because I was able to see exactly what the problem was within about 5 seconds. But before I get to that, let me illustrate what I was talking about with the jumping... Here's a fork where I use a normal "to()" tween and I use your technique of calling .progress(1).kill(), but this is a mouseenter/mouseleave animation to random x values, so just hover over/off the button: See the Pen poOXEGg?editors=0010 by GreenSock (@GreenSock) on CodePen Gross, right? Forcing the animation to the end every time makes it jump. And here's the same thing, but without the .progress(1) See the Pen abagmYJ?editors=0010 by GreenSock (@GreenSock) on CodePen So much better! Okay, so the problems in your demo are: You're splitting apart all the text and then calling revert() on the SplitText instance inside an onComplete! So if you stop (kill) the animation before it gets to the end, the SplitText never gets reverted. So the issue had nothing to do with progress(1) - it was merely about reverting your SplitText. You split the text again and again and again (once on every click). So let's say you clicked and it started animating everything from an opacity of 0...but you clicked again right away, so maybe the opacity is 0.02 at this point. You split the text again, so everything is nested one layer deeper now, and you start animating all those inner <div> elements from opacity: 0...but they're each INSIDE a div whose animation was killed when its animation was 0.02 (and it's rotated, etc.). So that wrapper div won't ever become more than 0.02 opacity. That's why things looked broken. So it's all just logic stuff in your code and this is a highly unusual case where you're literally manipulating the DOM before the animation and then trying to revert it all inside an onComplete. The vast majority of people are just animating CSS properties. Here's one approach where it ensures that you only use one SplitText instance, and only split when necessary: See the Pen zYJVKRo?editors=0010 by GreenSock (@GreenSock) on CodePen 9 hours ago, Chromium said: Again, according to you Jack, this should be enough to handle the spam clicking of a button. And you know what, at first glance, spam clicking now seems to work just fine (without even an animation jump!). However, if you wait for the animation to finish after the first click, and then try another click... it will not work. Hopefully I already answered all this above. It's a logic flaw in your code where you're reverting the SplitText in an onComplete, so then if you click again after that, you're attempting to animate a bunch of split.lines elements that aren't even in the DOM because you reverted. 9 hours ago, Chromium said: This is why I think something like this should be a mandatory tutorial for anyone beginning work on a gsap timeline. This has nothing to do with timelines really. It's just a logic thing with your splitting/reverting. 9 hours ago, Chromium said: Also, the comment in the snippet above mentions that it's simpler to use gsap.to() in the sample above... but how would you handle the hover spam if you did that? I showed this in two of the CodePens above. And just to be clear, a timeline is simply a wrapper for tweens that lets you control the whole group as one. So... // this let tl = gsap.timeline(); let tween = gsap.to("#el", {x: 100}); tl.add(tween); // is the same as this: let tl = gsap.timeline(); tl.to("#el", {x: 100}); // and functionally, since there's only one tween in the timeline you could simplify it to: let tl = gsap.to("#el", {x: 100}); It's totally fine if you want to use a timeline. I was just pointing out that it's unnecessary if you're only putting one tween into it. A single tween instance has all the control methods too (play(), pause(), kill(), timeScale(), reverse(), etc.) I hope that clears things up. Thanks for providing that CodePen demo. It made things crystal clear and much, much faster to address. ? 3 Link to comment Share on other sites More sharing options...
Chromium Posted April 1, 2023 Author Share Posted April 1, 2023 Let me preface this by saying that I appreciate the very thorough and detailed explanation with examples, Jack. Quote Here's one approach where it ensures that you only use one SplitText instance, and only split when necessary: See the Pen zYJVKRo by GreenSock (@GreenSock) on CodePen Thank you for providing the above CodePen, because that was going to definitely be my next question. However, I've some feedback regarding this CodePen: 1) It seems that in the CodePen you've provided, while it resolves the splittext issue you mentioned, when spamming the button, it only restarts the animation from about half-way up as opposed to from the very bottom as expected. What could be causing that? 2) If the tween was just a .to() instead of the .fromTo() used in the CodePen, would the t1.kill() (to handle the button spam) still be sufficient or would there be a need for something like a gsap.set(element, {clearProps: 'all'}) to also be executed right after the t1.kill()? And I mean this question in general, not just specific to the splittext tween in the CodePen. 19 hours ago, GreenSock said: It's totally fine if you want to use a timeline. I was just pointing out that it's unnecessary if you're only putting one tween into it. A single tween instance has all the control methods too (play(), pause(), kill(), timeScale(), reverse(), etc.) Got it. Although, since most of my animations usually happen in the click/hover event of a button, I'd still have to initialize the timeline/tween variable outside of the handler anyway. So unless initializing a timeline variable is more resource costly than a tween variable, I might as well keep using timeline variables for consistency's sake (even if it's just for a single tween). After reading all of this, I guess I just wish there was more of a plug and play method of clearing everything added by a timeline... like a .kill() equivalent, but one that somehow also detects a splittext, textplugin, etc instance and reverts it. But that's probably more of a magic solution that doesn't exist. A more realistic solution is probably more examples for each of these plugins showing the proper way of handling the click/hover spam of a timeline/tween containing one of the above instances. I say this because after reading this: Quote So if you stop (kill) the animation before it gets to the end, the SplitText never gets reverted. I went to GreenSock | Docs | Plugins | SplitText to see about how to detect if a splittext has already happened on an element and how to revert it prior to reading the follow-up CodePen you posted. This was nowhere to be found on the official documentation for the splittext plugin, let alone an example of handling a click/hover spam containing a splittext element. It might be worth adding that isSplitto the configurations properties on that page (maybe under a subsection labeled read-only properties or something). Additionally, I understand wanting to keep things simple and minimal... but some of the CodePens on that page have a click handler example that doesn't even do this spam check... and if they even do that check, they severely downplay the importance of it! That I feel like should be a must for any production-viable code snippet, otherwise it would be misleading and potentially more harmful than helpful. The first code snippet that I found on the SplitText page with a click handler that includes any killing of the timeline/splittext instance is the following one (under Nested elements and emojis): function kill(){ splitTextTimeline.clear().time(0); mySplitText.revert(); } $("#chars").click(function() { kill(); mySplitText.split({type:"chars, words"}) splitTextTimeline.from(mySplitText.chars, {duration: 0.6, scale:4, autoAlpha:0, rotationX:-180, transformOrigin:"100% 50%", ease:"back", stagger: 0.02}); }) As you can see, this snippet simply runs a mySplitText.revert() at every single click. Now this is different from your example wherein you check first if the SplitText instance has been split or not. So I'm assuming that attempting to revert the instance at each click is another valid approach to deal with click spamming because it will simply not revert the SplitText instance unless it has been split? Surprisingly, the code snippet right after the above one on that page, has a click handler that simply doesn't even check to kill the tween nor the SplitText instance! And the button only works on the first click... now I don't know if this is intentional, but I feel like the purpose of a button is to usually be clicked more than once unless it was a hyperlink taking you to another page. And if for some reason, adding the timeline/tween/Plugin instance kill statement is too complicated/irrelevant to include in the minimal demos on the plugins' page itself, then maybe my earlier idea of having a page dedicated solely to handling a click/hover spam of timelines/tweens and their plugin instance logic is worth adding after all. It might even save the moderators a decent amount of time to simply link to that page whenever a click/hover based spam question issue is posted. Maybe it's just me, but I feel like 99% of my issues are around this... even one example on how to handle the click/hover spam per plugin would probably solve at least 70% of my questions haha. I'm sorry if this feedback comes off a bit harsh, I'm not trying to sound unappreciative. I just feel very strongly about this. And I'd like GSAP to stay the best web developer tool out there! Quote On another note, my membership renewal is coming up and I wanted to let you know that it would be a lot more enticing if there was some kind of a renewal discount of like 20% or so (ideally applied by default). It might seem dumb, but this kind of stuff is usually a good incentive for me lol. Otherwise, it feels like the logical thing to do would be to wait until the next enticing GSAP update to renew again since it's the same price as a brand new membership fee. I'm bringing this up again since I feel like it might have gotten buried up there. I understand if this isn't something you can do, I just wanted to make sure my vote is added in favor of this (if there's any internal loop for this feedback haha). Link to comment Share on other sites More sharing options...
GreenSock Posted April 1, 2023 Share Posted April 1, 2023 39 minutes ago, Chromium said: 1) It seems that in the CodePen you've provided, while it resolves the splittext issue you mentioned, when spamming the button, it only restarts the animation from about half-way up as opposed to from the very bottom as expected. What could be causing that? I don't understand - this is behaving exactly the way it did in YOUR CodePen, right? And it does the same animation every time. You said it only "restarts" from about halfway up - do you mean the first time it runs differently? I'm not seeing any of this. It's behaving exactly as I'd expect. In terms of how far "down" it starts, that's controlled by the z part of your transformOrigin. Increase that if you want it to appear to start further down (that's only affecting the rotational radius of course). 44 minutes ago, Chromium said: 2) If the tween was just a .to() instead of the .fromTo() used in the CodePen, would the t1.kill() (to handle the button spam) still be sufficient or would there be a need for something like a gsap.set(element, {clearProps: 'all'}) to also be executed right after the t1.kill()? And I mean this question in general, not just specific to the splittext tween in the CodePen. It depends on what behavior you want. When you kill() an animation, it simply stops immediately. It doesn't revert or remove any of the styles that were applied. If you want to revert those, then yes you can either call revert() on the tween or use clearProps in a set() as you described. It's totally up to you and the behavior you want. In my .to() demo, I wouldn't want to do any of that because it'd cause everything to jump. For example, if it was mid-tween and had an x of 102.451 and then I did a clearProps or reverted, it'd immediately jump back to x: 0. 48 minutes ago, Chromium said: I just wish there was more of a plug and play method of clearing everything added by a timeline... like a .kill() equivalent, but one that somehow also detects a splittext, textplugin, etc instance and reverts it. But that's probably more of a magic solution that doesn't exist. That sounds like exactly what gsap.context() does. let ctx = gsap.context(() => { // all your GSAP-related code goes in here. }); // then later... ctx.revert(); // any tweens/timelines/SplitText created inside the ctx will get reverted. 1 hour ago, Chromium said: but some of the CodePens on that page have a click handler example that doesn't even do this spam check... and if they do even do that check, they severely downplay the importance of it! That I feel like should be a must for any production-viable code snippet, otherwise it would be misleading and potentially more harmful than helpful. There was only one CodePen on that entire page that didn't have the revert functionality in place (because it wasn't meant to do that), but I just added that functionality and even illustrated the context() option: See the Pen pbWwLe?editors=1010 by GreenSock (@GreenSock) on CodePen You said the CodePens "severely downplayed the importance" of spam-clicking...can you help me understand why you think they "severely downplay" it exactly? All except the one I mentioned above showed button spam handling (and now that one does too). 1 hour ago, Chromium said: It might even save the moderators a decent amount of time to simply link to that page whenever a click/hover based spam question issue is posted. I honestly think yours is the only question of this type that has been posted in years, although I'm old and my memory is fading so I could be wrong. Either way, it is most definitely not a common problem. We're working on a huge rewrite of our learning center and docs ecosystem, so we'll certainly keep this in mind as we work through that, though, and we'll aim to improve things as we go by adding more demos and explanations. It's always a balancing act, though, because we want to provide plenty of information without overwhelming people. Thanks for the feedback. 1 hour ago, Chromium said: I'm bringing this up again since I feel like it might have gotten buried up there. I understand if this isn't something you can do, I just wanted to make sure my vote is added in favor of this (if there's any internal loop for this feedback haha). Again, thanks for the feedback and we'll keep it in mind. If you're not thrilled with the value you get from the membership or the support we've delivered, we don't want you feeling obligated to renew. We work very hard on the tools and the support and we trust that the market will recognize our efforts and reward them. We're quite confident in the value and we aim not to be the kind of company that's constantly having massive "fire sales" and discounts, etc. because that may convey a lack of confidence or it may even cause people to be reluctant to ever sign up at "regular" price (assuming there'll always be a sale right around the corner). But I totally see your reasoning with a renewal discount. Another problem, though, is that we already offer a 10% discount when you activate auto-renewals so what you're suggesting would decimate any motivation to do that (why not just wait for the renewal discount code if that's twice as much?). The feedback is always welcome, though. ? 1 Link to comment Share on other sites More sharing options...
Chromium Posted April 1, 2023 Author Share Posted April 1, 2023 1 hour ago, GreenSock said: I don't understand - this is behaving exactly the way it did in YOUR CodePen, right? Hahaha, yes, you're right, my bad. I simply can't remember what I was going for with that CodePen animation! ? 1 hour ago, GreenSock said: In my .to() demo, I wouldn't want to do any of that because it'd cause everything to jump. For example, if it was mid-tween and had an x of 102.451 and then I did a clearProps or reverted, it'd immediately jump back to x: 0. Understandable. But I guess what I meant is that are there scenarios where .kill() is not enough because there are leftover inline styles by the previous button click (timeline/tween animation) ? And if so, how would you best deal with that without causing an animation jump (since that's what clearProps and .revert would do) ? Quote That sounds like exactly what gsap.context() does. That sounds awesome. I've read about this before, initially, I thought this was for very advanced use cases of GSAP (like scenarios that implemented dozens of tweens)... but after what I've learned today, and correct me if I'm wrong, I'm thinking this would be good to use even for a couple of tweens and a plugin instance to solve a click/hover spam scenario. 1 hour ago, GreenSock said: There was only one CodePen on that entire page that didn't have the revert functionality in place (because it wasn't meant to do that), but I just added that functionality and even illustrated the context() option: I appreciate the gsap.context() example. Though in that example, I tried switching the clickContext.revert() to clickContext.kill() just to see what that does, but that simply stops the animation completely. But I found that odd, considering that the context is re-initialized right after that, so why wouldn't it just re-run the animation? 1 hour ago, GreenSock said: You said the CodePens "severely downplayed the importance" of spam-clicking...can you help me understand why you think they "severely downplay" it exactly? All except the one I mentioned above showed button spam handling (and now that one does too). First, I'd like to say before answering this, that I've been detecting a subtle hint of hostility from you, Jack, for the past couple of responses or so. And so, I'd like to try and clear the air by saying that I am simply trying to seek more knowledge about how GSAP works and what it expects from me because I find myself falling into these click-handling traps more often than I feel like I should be. I am not trying to make your day miserable. So if I am asking too many questions (and that maybe annoying you?), I apologize. As for your question, I just feel like they're mentioned in more of an afterthought demeanor, like they're there, but don't have to be there... and if you're annoyed about my seemingly exaggerated number of CodePens that did not properly handle click-spamming on that page, I do not feel I exaggerated that; as there was exactly 2 CodePens with a click handler in them and one of them had no handling for click-spamming, that makes 50% of the CodePens! But again, I could be wrong about the importance of handling click-spamming, as they may truly be an afterthought when creating GSAP animations... and I'm happy to be corrected/informed. 1 hour ago, GreenSock said: I honestly think yours is the only question of this type that has been posted in years I'm not sure I understand this. Are you saying that GSAP is rarely used to animate content on the click of a button? Or that everyone who uses GSAP is a code wizard that knows how to handle click-spamming? I'm really curious to know because this scenario is like 99% of my use cases haha. So I either have a very repeatedly unique use case or everyone who uses GSAP knows something I don't ? Again, thanks for the detailed answers thus far, Jack. And I apologize for the massive block of texts (as I hate reading those myself). I guess I either have too many questions left un-asked from the last couple of years or some voodoo on this page makes my texts seem longer (I like to think it's the latter). 1 hour ago, GreenSock said: Another problem, though, is that we already offer a 10% discount when you activate auto-renewals Ah, haha. I'm definitely biased here because I very much hate auto-renewals. There's too many subscription based things nowadays so I cannot keep track if they were on auto-renew. And many services make you go through hoops in order to cancel an auto-renewing subscription (this is definitely not you guys but the companies that do this have definitely left a poor mouth taste for many). Link to comment Share on other sites More sharing options...
GreenSock Posted April 2, 2023 Share Posted April 2, 2023 6 minutes ago, Chromium said: But I guess what I meant is that are there scenarios where .kill() is not enough because there are leftover inline styles by the previous button click (timeline/tween animation) ? And if so, how would you best deal with that without causing an animation jump (since that's what clearProps and .revert would do) ? How would that even be possible? Maybe I'm misunderstanding the question, but it kinda sounds like "how can I remove inline styles but make elements act as if they're still there?" Why would you even want to remove inline styles if you want don't want things to reset (jump)? Sorry, I suspect there's a miscommunication at play here. 12 minutes ago, Chromium said: That sounds awesome. I've read about this before, initially, I thought this was for very advanced use cases of GSAP (like scenarios that implemented dozens of tweens)... but after what I've learned today, and correct me if I'm wrong, I'm thinking this would be good to use even for a couple of tweens and a plugin instance to solve a click/hover spam scenario. Sure. The alternative is for you to just call revert() on the individual instances. It's totally up to you on whatever feels most intuitive or keeps your code more concise. It makes little sense to use a context if you've only got 1 tween. If you've got 2 things, it's borderline (roughly the same). Beyond that, yes, context could probably help reduce your code. 14 minutes ago, Chromium said: I appreciate the gsap.context() example. Though in that example, I tried switching the clickContext.revert() to clickContext.kill() just to see what that does, but that simply stops the animation completely. But I found that odd, considering that the context is re-initialized right after that, so why wouldn't it just re-run the animation? That's just a logic thing again. Let's say you've got an animation like gsap.to(el, {x: 100}) which makes el.x animate from whatever it is currently to 100. Great. You do that in a gsap.context() which you then kill() that after it runs. If you then run the code that creates that context again, so gsap.to(el, {x: 100}), well el.x is now already at 100! So you're saying "animate from 100 to 100", thus it doesn't go anywhere. If you revert(), that's like rewinding the animation(s) and removing any inline styles that were added. So after that, if you ran gsap.to(el, {x: 100}) it'll animate from whatever it currently is (0 in this new case because it got reverted) to 100. 24 minutes ago, Chromium said: Are you saying that GSAP is rarely used to animate content on the click of a button? Or that everyone who uses GSAP is a code wizard that knows how to handle click-spamming? Haha. No, sorry, let me explain... It's extremely common for people to have buttons animate things with GSAP. It's also very common to trigger GSAP animations on mouseenter/mouseleave (hover). You're not alone in that. Even the most simplistic GSAP implementations handle that with ease. Often people just create an animation and play()/reverse() it. Nothing really special is needed. What's somewhat unique about your case is that you are literally changing the DOM structure on click and then changing it back again after the animation runs. THAT is uncommon. What's even more uncommon is to have someone complain about how unintuitive it is with the revert() stuff and the logic with DOM changes which are done outside the animations themselves (tweens/timelines). I hope we've cleared things up now. Let us know if there are any other GSAP-related questions we can help with. Happy tweening! Link to comment Share on other sites More sharing options...
Chromium Posted April 2, 2023 Author Share Posted April 2, 2023 1 hour ago, GreenSock said: If you then run the code that creates that context again, so gsap.to(el, {x: 100}), well el.x is now already at 100! So you're saying "animate from 100 to 100" Ah! Yes, that finally got through to me! Thanks for this example. I don't know why, but reading about the timeline playhead vs this just never truly clicked! Or maybe it did, but inline styles just always felt like a separate thing to deal with for some reason ? 1 hour ago, GreenSock said: The alternative is for you to just call revert() on the individual instances. Is that possible in a timeline with multiple tweens? As in can I revert only specific tweens in a timeline (and in any order as opposed to the order they were added/created in the timeline) ? If so, then this is probably the key ingredient for all my woes with click handlers haha. 1 hour ago, GreenSock said: you are literally changing the DOM structure on click Haha what brought me here was following this CodePen: See the Pen wvxbvjg by GreenSock (@GreenSock) on CodePen The only change to this I made, was that the TextPlugin's value was not a static one as used in this CodePen, but rather a dynamic variable pulled in through AJAX. Is this really that uncommon?! 1 hour ago, GreenSock said: What's even more uncommon is to have someone complain about how unintuitive it is with the revert() stuff and the logic with DOM changes which are done outside the animations themselves Did I require some clarification about how GSAP timelines/tweens work? Yes Did I inquire about how revert/kill works and their effects on the DOM? Yes Did I struggle to deliver the right questions initially? Yes Did I inquire about how CSS styles work on a DOM? NO Did I inquire about how to move element A inside element B or any DOM-related question? NO I'm sorry, but I will have to disagree with your last statement (and I find it quite offending in fact). If at some point my questions seemed like they weren't within GSAP's purview to you, and were logic questions, that is only because I was confused about GSAP's effects on the DOM. For example: Quote That's just a logic thing again. Let's say you've got an animation like gsap.to(el, {x: 100}) which makes el.x animate from whatever it is currently to 100. Great. You do that in a gsap.context() which you then kill() that after it runs. If you then run the code that creates that context again, so gsap.to(el, {x: 100}), well el.x is now already at 100! So you're saying "animate from 100 to 100", thus it doesn't go anywhere. If you revert(), that's like rewinding the animation(s) and removing any inline styles that were added. So after that, if you ran gsap.to(el, {x: 100}) it'll animate from whatever it currently is (0 in this new case because it got reverted) to 100. If this answer is JavaScript logic to you, it is GSAP to me... if only because, let's see... the word "gsap" alone was mentioned in it 4 times! That is not to say that I haven't inquired about JavaScript logic before unknowingly on here, Blake was the legend that helped me out big time on here once, it is just that today (or anything within this ticket) was not one of those days. Link to comment Share on other sites More sharing options...
GreenSock Posted April 2, 2023 Share Posted April 2, 2023 7 minutes ago, Chromium said: Is that possible in a timeline with multiple tweens? As in can I revert only specific tweens in a timeline (and in any order as opposed to the order they were added/created in the timeline) ? I guess technically the answer is "yes" but that seems super weird to me. When you revert() a timeline, it reverts everything inside of it. If you just revert() one of those child tweens, it'll also get removed from the parent timeline. I can't really think of a scenario where this would be useful or a good idea. If you sequence three 1-second tweens in a timeline, and then you revert() the middle one, you'd end up with a timeline that has a 1-second gap in the middle. And that individual reverted tween would revert the inline styles of its target(s) but if either of those other tweens affected the same target(s) it isn't as if it'd go back and alter things inside those other tweens. I dunno...just seems very strange to me but maybe there's a use case for it somewhere. 12 minutes ago, Chromium said: The only change to this I made, was that the TextPlugin's value was not a static one as used in this CodePen, but rather a dynamic variable pulled in through AJAX. Is this really that uncommon?! No, that's not terribly uncommon. It should work great. I'm not sure what any of this has to do with my original comment about changing the DOM structure outside of the animation(s) though. The text animation in that demo had some DOM manipulation occurring in the animation itself. 15 minutes ago, Chromium said: Did I inquire about how CSS styles work on a DOM? NO Yes, your questions involved inline styles in various ways (which is fine!), like: 4 hours ago, Chromium said: because there are leftover inline styles by the previous button click (timeline/tween animation) ? And if so, how would you best deal with that without causing an animation jump (since that's what clearProps and .revert would do) ? And several of the confusions you had early on were related to inline styles either sticking around or not. It sounds like maybe you thought I was objecting to you asking anything related to inline styles, but that's not at all what I meant. I never claimed in this thread that you were asking "out of scope" questions. It's totally fine that you were asking those things. 21 minutes ago, Chromium said: Did I inquire about how to move element A inside element B or any DOM-related question? NO The crux of the problem you originally had was directly related to DOM manipulation issues. Remember how you were splitting again and again, so things were getting more and more deeply nested, new wrappers were getting added, etc.? But again, I have zero objections to any of that. No problem-o. Sorry if I gave any other impression. 25 minutes ago, Chromium said: I'm sorry, but I will have to disagree with your last statement (and I find it quite offending in fact). If at some point my questions seemed like they weren't within GSAP's purview to you, and were logic questions, that is only because I was confused about GSAP's effects on the DOM. It sounds like there was a miscommunication here - did you think I was shaming you for asking logic-related questions? I sure hope not. Now that I re-read what I wrote, I think the misunderstanding happened here: "...and the logic with DOM changes which are done outside the animations themselves". Perhaps that sounded like I was saying "we only answer questions here about things inside the animations themselves...all that other stuff is your problem to deal with". Not at all what I meant. I was saying that things inside the animations are pretty much automatically handled by GSAP but since the DOM manipulations in your case were being done outside the animations, that's the wrench here that must be managed with a little extra code/logic. You asked me to elaborate on what I meant by your original question being uncommon. It seemed as if you were under the impression that using GSAP on interactions like click/hover was fraught with challenges, confusions, pitfalls, and we must be facing a lot of similar questions in these forums since it's so hard. I was just trying to explain what seemed uncommon here. That's twofold: the DOM manipulation aspect and the complaints about how confusing and hard it is - that's just not something we hear very often. Usually we hear quite the opposite - that GSAP is a joy to use and makes things so much easier. But the nature of having such a flexible tool is that there will be various edge cases that are more complex. You happened to run into one with the DOM manipulation aspect that was happening outside of the animations, but I sure hope I've adequately explained it now and you feel equipped to go make some really cool animations. ? 1 Link to comment Share on other sites More sharing options...
Chromium Posted April 2, 2023 Author Share Posted April 2, 2023 39 minutes ago, GreenSock said: If you just revert() one of those child tweens, it'll also get removed from the parent timeline. I can't really think of a scenario where this would be useful or a good idea. I was thinking that on subsequent clicks, I can revert parts of the timeline, kill the timeline, and then re-initialize it with brand new tweens... but maybe this is over-complicating things rather than making them simpler haha. 39 minutes ago, GreenSock said: The text animation in that demo had some DOM manipulation occurring in the animation itself. Because the demo that I posted was a random demo that I picked up just to portray the .kill() on the timeline not working for me haha. But the one I was using in production code was the typewriter animation (that was also giving me trouble with killing the timeline on spam clicks). It just so happens that this one also manipulates the DOM... as it appends a cursor div to the end of another div in the onUpdate() of the tween. Call me Mr. Edge Case lol 39 minutes ago, GreenSock said: that GSAP is a joy to use and makes things so much easier. But the nature of having such a flexible tool is that there will be various edge cases that are more complex. You happened to run into one with the DOM manipulation aspect that was happening outside of the animations Ironically, that is literally why I started this post... because I felt like GSAP should be so easy to use, but it's giving me such a hard time nearly every time I've had to deal with spam clicks, so I must be doing something very wrong or missing something. Instead (and what I've learned today) is that it seems like I've just been working on edge case after edge case after edge case... which I'm not really surprised to learn because in this job it seems I've been working on edge cases with pretty much 70% of the plugins I've used. Thank you for your patience with me, Jack. I do believe it was well worth it. As at the very least, I've solidified my core understanding on how tweens work (whether that's with inline-styles, timeline/tween kills/reverts, etc). As for spam click handling, I'd hoped to find some shortcuts, but while I didn't really find any, I do think that my strengthened understanding of timeline/tween handling learned today should come in handy for the next GSAP task that involves any spam click handling. I look forward to the next GSAP task! And in spite of these issues, GSAP is still a joy to use and one of the greatest things to exist since sliced bread! Keep up the great work! 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