Jump to content
Search Community

Timeline.invalidate() doesn't seem to...

pragdave test
Moderator Tag

Recommended Posts

The example contains an SVG element.

 

  • After 1 second, it uses a timeline to animate a move to the right, taking five seconds.
  • Two seconds later (that is, in the middle of the animation), a timer fires, pauses the animation, and clears and invalidates the time line.
  • Two seconds after that, it manually moves the element back to the start position (by deleting the transforms added by GSAP and manually setting x and y).
  • Two seconds after that, it repeats the initial move.

 

Because I called invalidate() in the middle, I was expecting the timeline to pick up the current position of the element in the DOM and animate it from there.

 

Instead, you'll see that the animation causes the element to immediately jump to its last location in time timeline, and it looks as if it is simply continuing the initial animation.

 

Am I misunderstanding what invalidate() does?

 

See the Pen MWawxqL by pragdave (@pragdave) on CodePen

  • Thanks 1
Link to comment
Share on other sites

That's what I expected it to do, yes. Shouldn't invalidate() force it to reread the coordinates from the DOM?

 

Also, is the call to kill() needed: the documentation says that the timeline is then eligible for GC. Does that just mean the prior _contents_ of the timeline, but not the timeline itself?

Link to comment
Share on other sites

I noticed several problems:

  1. You were removing the "transform" and "style" attributes and expecting that to reset the position but keep in mind that GSAP caches transform values in order to maximize performance and ensure that the components are exactly correct (rotation can be ambiguous when parsing CSS matrices). The way you did it had no effect on GSAP's cached values, thus it pulled from those on subsequent animations (as it should). If you want to reset the x, I'd just do gsap.set(mover, {x:0}) which will give you best performance, but if you really want to wipe out all the recorded values you can do gsap.set(mover, {clearProps:"all"})
  2. It looks like you were setting "x" and "y" properties directly on the element - did you think that would affect the transforms? It won't. If your goal is to set those, the best way is to just go directly through gsap.set() so that it records it in the proper place(s). In this case, the GSAP cache and the element's "transform" attribute. If it was a non-SVG element, it'd be in the CSS inline style. 
  3. There was a bug that caused the playhead position to get remembered if you mixed clear() and pause() on a timeline. So then when you call play() subsequently, it resumed from that old playhead position. You happened to stumble across this edge case which I'm thankful for - it'll be fixed in the next release. In the mean time, there are plenty of ways for you to get around it. You don't even need to call pause() and invalidate() if you calling clear() on that timeline. Or after you play() again, you could force the playhead to 0 like play().time(0).

Sorry about any confusion there.

 

Does that clear things up? 

  • Like 2
Link to comment
Share on other sites

15 minutes ago, GreenSock said:

I noticed several problems:

  1. ... keep in mind that GSAP caches transform values in order to maximize performance and ensure that the components are exactly correct (rotation can be ambiguous when parsing CSS matrices). The way you did it had no effect on GSAP's cached values, thus it pulled from those on subsequent animations (as it should). If you want to reset the x, I'd just do gsap.set(mover, {x:0}) which will give you best performance, but if you really want to wipe out all the recorded values you can do gsap.set(mover, {clearProps:"all"})

 

Isn't that what invalidate() is supposed to remove, though.

 

I set the x and y attributes simply because those are the attributes that are set when I first added the element to the timeline. 

 

I wanted to stop everything, clear the timeline, and re-add that element to the start. Given that invalidate() was supposed to clear the cache, I figured that by removing the transforms that gsap had added, I'd be back where I started out.

 

I ended up using the kill() solution, which works fine. I guess now that it's all working for me, it's more of an academic question of jujst what invalidate() actually does... :)

 

 

Dave

Link to comment
Share on other sites

18 minutes ago, pragdave said:

Isn't that what invalidate() is supposed to remove, though.

No no - there are two totally different things: 

  1. When a tween begins, it must record the start/ending values so that it can very quickly interpolate dynamically. This is tween-specific
  2. Any element that you animate with GSAP gets an element-specific object containing its up-to-date transforms. You could have 100 tweens of the same element and they'd all pull from that one cache object. In other words, this is element-specific, not tween-specific. 

When you invalidate() an animation, it only affects #1 (clears only that animation's start/end recorded values). 

 

When you clearProps on an element, it affects #2. 

 

Make sense? 

  • Like 3
Link to comment
Share on other sites

Ah, yes it does. And that explains why the two got out of step when I just used invalidate. Thanks for the explanation.

 

Does GSAP keep track of all elements that have been decorated with the cached transformations? I know I can recursively traverse a timeline, but that won't find elements that are not currently participating but that were in the past. If there was an API for this, I could then do a global reset. If not, I'll just maintain a list as I create them.

 

Cheers

 

 

Dave

Link to comment
Share on other sites

16 minutes ago, pragdave said:

Does GSAP keep track of all elements that have been decorated with the cached transformations? I know I can recursively traverse a timeline, but that won't find elements that are not currently participating but that were in the past. If there was an API for this, I could then do a global reset. If not, I'll just maintain a list as I create them.

Can you help me understand the use case? 

 

You want to find all the elements that have ever been animated by GSAP, even if the animations are done/killed? If so...why? Maybe if I understand your end goal I can better offer advice. 

Link to comment
Share on other sites

I have a document containing dozens of animations. I prerecord timings for these, but the viewer can interact with them too. For example, imagine a Towers of Hanoi animation where they can type a digit to change the number of disks, speed it up and down with a slider, and so on. The elements are all SVG, and they are all dynamically generated when the document loads.

 

This is true for all the other animations as well.

 

After spending time playing, they want to read it again, so I have a RESET button, which I want to use to rewind everything, and reset all the elements to their initial state, so that the document behaves just as it would if they'd reloaded it.

 

I first bumped into these issues with the Hanoi example. I prepopulate the model with 5 disks, and then let the viewer choose how many to use; every time they hit a digit I restart the animation with that number of disks. What was happening is that if the animation had got to a certain point (say with disk 1 on peg C), then when I restarted and repositioned the disks, they would initially be drawn on the starting peg, but once you pushed PLAY they'd jump around all over the place, retaining their idea of where they'd been last.

 

Link to comment
Share on other sites

It's probably my mistake in the way I structure the code, but... take the Hanoi example

 

I have a function that sets the stage: it draws the towers, creates the disks, and loads the disks onto the first tower. At this point, there's a timeline, but it isn't running and it contains nothing.

 

The user hits the play button and we're off to the races.

 

When they hit the reset button, I want to move the disks back onto the first tower. But the original stage was not created using GSAP.set: I'm using svg.js.

 

So if I have to use gsap.set to clear the elements, I'm either going to have to have to write the setup code twice (once positioning normally and the second time positioning using gsap), or find some way of setting things into the timeline, then running it for just one update() to get them to draw, then pause until the user presses play.

 

The solution that seems to be working right now is that I use kill() on the original timeline, then replace it with a brand new timeline. As long as I then animate positions using {attr: {x:, y: }} and not just x:, it seems to work. I suspect rotations and scaling will also need some TLC, but I'm not currently using them.

 

Probably a stupid idea, but couldn't you use attribute settings on the underlying elements to detect when someone changes an attribute that was the subject of tweening, and then invalidate its entry in the cache if so?

Link to comment
Share on other sites

If your goal is to have things return to where they started, .time(0) should do exactly that, then you could clear() or kill() it. There's nothing wrong with using your current strategy of just calling kill() on the timeline and creating a whole new one. No biggie. 

 

56 minutes ago, pragdave said:

Probably a stupid idea, but couldn't you use attribute settings on the underlying elements to detect when someone changes an attribute that was the subject of tweening, and then invalidate its entry in the cache if so?

Animation is probably the most performance-sensitive aspect of UX so we're obsessed with performance. If GSAP had to record the original attribute value and constantly check "did it change yet? How about now?" again and again on every tick, that'd be terrible for performance :) And honestly, this is part of the reason we advise people to do all their transforms via GSAP so that you're not swapping things out from under it. I think that's generally a cleaner approach anyway. There are a bunch of other reasons I could go into about managing transforms and all the work GSAP does for you, but it'd turn this into a very long post :) 

 

  • 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...