Jump to content
Search Community

Changing height to auto only gets applied at the end instead of during the animation

Chromium test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

I'm doing something very similar to the CodePen and I had that exact setup working before (a couple weeks ago). Now I awake to everyone's favorite type of problem, something that used to work that is not working anymore. I'm not sure if I've added a CSS line somewhere here or there that could have tampered with this but everything seems normal to me so I'm at a loss.

 

While the example in the given CodePen works, the one in my project doesn't (even though it's outrageously similar). Basically when the button is clicked, instead of it animating the height to auto over the duration of 1.5sas you'd expect, it simply waits 1.5s and then adds height: auto; to the element at the end. Any ideas why something like this would happen?

Additionally, changing the following:

.to('.test', 2, {
  height: 'auto'
});


To:

.to('.test', 2, {
  height: '45px'
});

Works. But I don't understand why animating height to 'auto' works in the CodePen example but not in an identical code piece in my project?!

Interestingly, I've seen a lot of posts on the forums asking this exact question and the answers always seem to be some form of a very simple tween example with a .to() doing exactly that, height: 'auto'. What I'm starting to suspect is that it's misleadingly simple (at least on the JS side) because there are certain elusive CSS conditions that need to be met for the animation to really work. For example, changing certain CSS in one of the parents of the element being tween'd could end up breaking the animation. Is this true? If so, can we have a list of CSS conditions that need to always be there OR should not be there (whether it's on the element itself, its direct parent, or any of its ancestors) for this animation to be successful?

See the Pen vYNjogr by mikeK (@mikeK) on CodePen

Link to comment
Share on other sites

  • Chromium changed the title to Changing height to auto only gets applied at the end instead of during the animation

Hm, that sounds pretty frustrating, @Chromium! Can you please verify that you're using the latest version (3.11.3) on the site that's "broken"? 

 

It's super difficult to troubleshoot blind and it sure looks like your CodePen is working correctly so I'm struggling to guess what may be the issue in your setup. Other than having an outdated version that had a regression/bug, I'm at a loss at the moment. There aren't a bunch of conditions that I can think of CSS-wise. 🤷‍♂️

Link to comment
Share on other sites

Gosh, I really wish I could diagnose it for you, but it's pretty much impossible to do blind. Like you said, the CodePen is working as it should, so there must be something in your project that's throwing things off. I'd recommend building up your minimal demo to be closer and closer to the "real" project until it breaks and then you'll have a much better idea of what might be interfering. 

 

Basically, the way it works with "auto" is at the start of the tween it'll temporarily set it to that value, measure the height so the tween can have a valid number to tween to, and then switch it back to what it was. So my only guess would be that you've got something else interfering such that when that tween starts and it temporarily sets it to "auto" to do the measurements, something is preventing it from being the height you expect. 

Link to comment
Share on other sites

Ahahaha! I think you're onto the exact cause here Jack. I believe I've come to the root cause of the issue here. However, I had to put it into a video because I'm not sure how else to replicate it. The video has audio that explains exactly what I've discovered.

First Video here shows the animation working.
The second video here shows how to make the exact same animation (no code changed) fail.
 

Quote

Basically, the way it works with "auto" is at the start of the tween it'll temporarily set it to that value, measure the height so the tween can have a valid number to tween to, and then switch it back to what it was.

To try to sum it up, I believe the root cause of my issue is exactly what you described above. You say "at the start of the tween" but is it possible that this "calculation" happens only the first time the tween is triggered? Because that would then explain why the subsequent animations (after the element's text content is changed) start failing (i.e. the height it calculated the first time is no longer the same as the height required after the content is changed through Inspect Element).

Link to comment
Share on other sites

  • Solution

Hi,

 

Indeed updating the filename as you show after the animation has run is creating the problem. I can assume that if at some point, the code somehow updates he filename, for example if a user changes the name of the file or the app gets a new name from an API, there is going to be an event handler for that, right? You should use that handler to either, kill the timeline and re-create it again with the new name or to invalidate the timeline and clear the initial recorded values:

See the Pen MWGRNwg by GreenSock (@GreenSock) on CodePen

 

Let us know how it works.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

44 minutes ago, Chromium said:

You say "at the start of the tween" but is it possible that this "calculation" happens only the first time the tween is triggered?

Yep, that's how all tweens work - the very first time they render, the start/end values get recorded so they can be interpolated super-fast during the tween. It'd be a huge waste performance-wise to continually read those values. 

 

So are you all set now? Everything is cleared up? 

 

As Rodrigo eluded to, if you want to flush the internally-recorded values and force them to get re-recorded on the next render, just call invalidate() on the tween/timeline. 

  • Like 1
Link to comment
Share on other sites

Thanks guys! That'll definitely help for this one. However, I can't help but wonder if there's a better "in-between" solution for this kind of problem...

 

Quote

You should use that handler to either, kill the timeline and re-create it again with the new name or to invalidate the timeline and clear the initial recorded values

For example, can this not be detected automatically? If the element's content changes, can we not have the tween's values be updated automatically here? I understand if JavaScript is the limit here, that's fine. I'm just a little bitter having spent a whole day trying to solve a problem that didn't exist lol

I guess it could have been worse, I could have spent more than that (I knew it was working before! lol)... got really lucky I even figured it out to be honest.

Link to comment
Share on other sites

Hi,

 

As Jack mentioned is all about performance. For example how do you tell GSAP: "Hey at some point this particular property might change but I can't tell you when, so you have to check regularly". Sure for a couple of elements and animations there shouldn't be a problem to be checking in order to run an update. What happens when you have hundreds of elements in your app and they are all in different GSAP instances, regardless when those animations happen. GSAP now has to start checking for changes in all of those elements and all the properties being animated on each element, that's bye-bye performance.

 

I've been using GSAP for over 10 years and been around the forums that whole time and in over a decade that hasn't changed not because unwillingness to change it, it hasn't because is the best way to keep GSAP super performant and at the top of the game. Trust me over the past decade I've seen a fair share of threads where the suggestion or request is to check for changes in properties. The constant read/write operations is a debate that has popped out around here constantly and I understand that this is something not only GSAP does, but most JS driven animation libraries do because it would clog the CPU.

 

We understand the frustration of spending an entire day trying to solve something, we all have been there and is no fun. The silver lining is that there is learning attached to the experience and now you know even more about GSAP and how it works.

 

Probably @GreenSock can share more information on the subject since Jack knows all the ins and outs of GSAP.

 

Let us know if you  have any other question.

 

Happy Tweening!

Link to comment
Share on other sites

Hello Rodrigo,

 

Thanks for the detailed explanation on this. I appreciate it.

 

Quote

For example how do you tell GSAP: "Hey at some point this particular property might change but I can't tell you when, so you have to check regularly"

I perfectly understand this and I agree with it. What I was thinking is more along the lines of an event-based handler instead. Essentially, your library gets notified automatically when the properties change (instead of the developer having to create an event handler doing that exact thing as you suggested earlier). My guess is that there's a limitation in JavaScript or CSS that prevents this (i.e. when the element's content/properties change, there's no automatic event handler for that) ?

 

Quote

The silver lining is that there is learning attached to the experience and now you know even more about GSAP and how it works.

You'd be shocked, but the reason I even ended up here is because 2-3 weeks ago (when I wrote the GSAP animation) I discovered this very thing... so I made sure to only trigger the animation once I updated the element's text (through Inspect Element) and not before to avoid triggering this issue during testing. Lo and behold 3 weeks later, I completely forgot this bug existed (and thus spent yesterday debugging a ghost problem). I don't know what else can be done here to avoid such a problem, but I sure know now not to rely on my memory since I can't even retain it for 3 weeks 😂

Link to comment
Share on other sites

2 minutes ago, Chromium said:

I perfectly understand this and I agree with it. What I was thinking is more along the lines of an event-based handler instead. But essentially, your library gets notified automatically when the properties change (instead of the developer having to create an event handler doing that exact thing as you suggested earlier). My guess is that there's a limitation in JavaScript or CSS that prevents this (i.e. when the element's content/properties change, there's no automatic event handler for that) ?

As I mentioned I'll leave this one to Jack so He can give you a more detailed and exact response on that. Is worth noticing that He's always open to improving GSAP based on the users suggestions.

 

1 minute ago, Chromium said:

I don't know what else can be done here to avoid such a problem, but I sure know now not to rely on my memory since I can't retain it for more than 3 weeks 😂

A Postit on the screen or with big letters on a whiteboard could solve the issue 🤣

 

Happy Tweening!

  • Haha 1
Link to comment
Share on other sites

Yeah, that kind of thing would be disastrous for performance. If it had to read the property on every tick to check for changes, my guess would be that GSAP would instantly become 5-10 times slower. Like...a radical shift across the board. It would create what's called "layout thrashing".

 

If we tried using some kind of event notification like you suggested, that has its own set of major tradeoffs. For example, GSAP would then have to add a bunch of extra logic to isolate cases where it's even possible (most properties don't dispatch events when they change). And even then, it doesn't seem feasible because typically you're animating the very property that you're wanting to be notified when it changes, thus GSAP itself would literally be triggering that event on every single tick! :)

 

Furthermore, this behavior would introduce even more problems because let's say you've got a 10-second animation of "x" from 0 to 100 linearly and then halfway through, you want it to automatically update because the destination "x" value should now be 500 instead of 100. Where should it render on the next tick? 250? Technically that's where it should be according to the logic of the tween (250 is halfway between 0 and 500). That means you'd suddenly see it JUMP from 50 to 250! Is that desirable? Most people would say "no". And what happens if you call reverse() on that tween after it finishes. So originally it played from 0->50 and then jumped to 250 and continued to 500...so how would you expect it to play in reverse? If it doesn't do that same thing in reverse, is it truly a reverse (accurate)? This opens up all kinds of logic issues and edge cases. 

 

So from a performance standpoint, it'd be a disaster. It would increase file size due to all the extra logic it'd require. It'd also create jumps and logic issues. Hopefully this clarifies why it's something we'd want to avoid.

  • Like 1
Link to comment
Share on other sites

Quote

If we tried using some kind of event notification like you suggested, that has its own set of major tradeoffs.

So I understand why you may have chosen performance over intuitiveness in this case but I still think there's room for improvement.

 

Quote

And even then, it doesn't seem feasible because typically you're animating the very property that you're wanting to be notified when it changes, thus GSAP itself would literally be triggering that event on every single tick!

You're right... but couldn't we exclude height changes made by GSAP itself for the element it's already watching? Would that not solve the problem of triggering the event for every single tick then?

Here's an example of what I mean:

Let's say you're animating the height of a span element with a paused GSAP timeline that plays on click and reverses on the subsequent click (just like the one in my example). On click, the tween changes the span element's height from 0 to 100%. So once you've clicked the element, GSAP is running the tween for the first time, GSAP has now calculated the element's initial height value and stored it for future tweens, correct? That value is initially 0, after the tween runs the first time, GSAP now expects that element's height to be 100%, after it runs again, GSAP now expects the height for that element to be 0 again, etc. Now let's say all of a sudden, the element's content changes, or something changes the element's height to 50px... the element emits a height property change event and GSAP is listening to that event, GSAP checks the height property and based on its value, it can tell that that value was not set by GSAP's last tween and thus will get flagged. Now you may say that GSAP should NOT change the height of the element back to 0 due to this flag because that will lead to unexpected and non-user friendly results, and I agree with that. Instead, I'm thinking it can use this information to output a "warning" to the console that the element being watched by GSAP has had it's height changed to an unexpected value, and could affect the GSAP animation.

I'm just thinking of the top of my head, sorry if I've brainstormed something that you've probably already thought of or debunked before.

Link to comment
Share on other sites

Yeah, trust me, after having done this for over 14 years and working with some of the most talented motion designers around, we've certainly thought this through :)

 

We place a HUGE priority on performance because animation is probably the most performance-sensitive parts of UX. We also try to deliver incredible flexibility, but I've seen tools that try to do everything "automatically" for you and it can quickly turn into a mess of tradeoffs. What you're describing is exactly one of those scenarios that creates pitfalls and ultimately erodes a library (unintentionally of course). 

 

It's super tempting to get myopically focused on one particular scenario and become convinced that in THIS CASE it should work like _____ and forget about the ramifications for the 836 other cases where it could spell disaster. I've been bitten many times by that bug ;) I'm getting better at spotting them in my old age.

 

The key is to build a toolset that's super fast and has a rich set of hooks into it for key functionality, NOT to "automagically" handle every possible scenario like the one you described. For example, in your case the best way to handle that is to tap into the invalidate() method when your sizing changes. Rather than leaning on GSAP to try to do a bunch of extra work (which exacts a cost on every animation for every user), you should wire up the proper minimalistic hooks on your end to invalidate() the animation only when necessary. Shoving that all inside of GSAP just doesn't make sense. Waaay too costly.

 

I could list of another 3 "gotchas" that'd surely bite users if we implemented your suggestion but I don't want to make this post even longer. If you want more details about all the pitfalls I see with this, let me know and I'm willing to explain further. Trust me, though, if we implemented what you're suggesting, it would go very, very badly and we'd get a ton of complaints, we'd lose users and ultimately do a lot of harm to the ecosystem. 

  • Like 2
Link to comment
Share on other sites

Quote

It's super tempting to get myopically focused on one particular scenario and become convinced that in THIS CASE it should work like _____ and forget about the ramifications for the 836 other cases where it could spell disaster. I've been bitten many times by that bug ;) I'm getting better at spotting them in my old age.

Hahahah that definitely sounds like you've been bitten before... been there myself a few times, and it is too easy to fall into such traps.

 

Quote

If you want more details about all the pitfalls I see with this, let me know and I'm willing to explain further.

I appreciate the offer, but I don't think it is necessary cause I do believe you on it. If performance is what you're going for (which I'd go for here myself as well), then we have no choice but to live with the ramifications of our choices.

That being said though, it does not stop me from feeling like this is a problem that shouldn't be a problem... and it's probably JavaScript's fault. Don't ask me why (it's cause everything is JavaScript's fault). Haha, my bitterness here stems from the fact that I believe software in general is not getting smarter fast enough lol... like more experience in the field is fantastic, but that means every now and then my biggest time debugging is spent on something really silly like a syntax error that wasn't highlighted by the editor, an animation or a CSS change that does not get redrawn by the browser, something that works in one browser but not the other, a 3rd-party API that really sucks with asinine support, lack of/weak options for real-time notification systems, and so on. Basically all of these are issues that should be problems of the past already, but I guess that's every generation's gripe; the tools advance slower than our patience for them lol

Thanks for reading my rants lol. I won't hold you off any longer. Thanks for the chat!

  • Like 2
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...