Jump to content
Search Community

onComplete is called before animation is complete

karljohan108 test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I have a TimelineMax called animationTimeline with 3 other TimelineMax's in it. On my animationTimeline I have an onComplete callback set on it. The problem is that the onComplete callback is called before the last animation has ended. So there's a tiny bit left on the last animation and then onComplete is called. In my onComplete, I'm re-rendering some items, so everything looks kind of choppy, because onComplete is called before time.

 

I think it might have something to do with what they're talking about in these 2 posts:

https://greensock.com/forums/topic/8337-oncomplete-is-firing-before-the-last-animation-frame/

https://greensock.com/forums/topic/6528-tweeneventcomplete-fires-before-rendering-complete/

 

Is onComplete generally called before the animation has completely finished (before the last frame of the animation has been rendered)? How can I get a callback for complete render?

 

I'm using the latest version of GreenSock with the latest version of Chrome.

Link to comment
Share on other sites

I can't make a reduced test case, but I can show you in the example I have. 

 

I'm using GreenSock in a game - https://online-solitaire.herokuapp.com/spider - so if you go to that url and click "New game", then the cards will animate to the board. You'll notice that the last card to the right doesn't turn fully. There's a lag in the turning of that card. That happens because "onComplete" is called before the "TimelineMax" animation is completed.

 

Any idea what it could be about? Am I misunderstanding something regarding how "onComplete" works? Is "onComplete" called when the last frame of the animation renders or is it called at some other point?

Link to comment
Share on other sites

A TimelineMax's onComplete will be fired after all of its child tweens complete. I can't imagine how a TimelineMax's onComplete could possibly fire BEFORE one of its child tweens finishes. I'm pretty sure that's impossible, but if you've got a reduced test case we can peek at, I'd love to. I definitely want to make sure there aren't any bugs. But the codebase is specifically written such that what you're describing couldn't happen, so it sounds to me like something else must be at play here. That's why a reduced test case would be essential in troubleshooting this. 

  • Like 2
Link to comment
Share on other sites

4 hours ago, karljohan108 said:

There's a lag in the turning of that card. That happens because "onComplete" is called before the "TimelineMax" animation is completed.

 

I see the lag, but what makes you think that onComplete is firing prematurely? I noticed that you're using React, and that works asynchronously, meaning updates can be an issue if you're not careful. That's why there's a .componentDidUpdate() method.

 

But like Jack said, that should never happen. I made this demo to show the callback order and the position of the boxes. 

 

See the Pen YOMxGr by osublake (@osublake) on CodePen

 

 

I also noticed you're using timing mechanisms like setTimeout and _.defer. That can also cause problems if you're not careful. To see what I mean, open this animation, go to another tab for like 5-10 minutes, and then go back to the animation. It's been shooting off but not animating a bunch of fireworks the whole time you were on another tab because setTimeout is not synced with animation frame updates.

 

https://codepen.io/rachsmith/pen/YNKVxG

 

I would recommend using TweenLite.delayedCall() instead. It's driven by GSAP's ticker, so it will keep everything synced, even if you go to another tab.

https://greensock.com/docs/TweenLite/static.delayedCall()

 

 

 

 

  • Like 4
Link to comment
Share on other sites

7 hours ago, OSUblake said:

 

I see the lag, but what makes you think that onComplete is firing prematurely? I noticed that you're using React, and that works asynchronously, meaning updates can be an issue if you're not careful. That's why there's a .componentDidUpdate() method.

 

Because in my "onComplete" function, I'm setting the new state and some other stuff. If I comment out everything in the "onComplete" function, the animations finishes as it should, with no lag. Which, from what I can gather, means that "onComplete" is called before the animation finishes.

 

Nothing else, besides the animation is happening, so I'm not sure why any updates should be issued?

 

7 hours ago, OSUblake said:

I also noticed you're using timing mechanisms like setTimeout and _.defer. That can also cause problems if you're not careful. To see what I mean, open this animation, go to another tab for like 5-10 minutes, and then go back to the animation. It's been shooting off but not animating a bunch of fireworks the whole time you were on another tab because setTimeout is not synced with animation frame updates.

 

See the Pen YNKVxG by rachsmith (@rachsmith) on CodePen

 

I would recommend using TweenLite.delayedCall() instead. It's driven by GSAP's ticker, so it will keep everything synced, even if you go to another tab.

https://greensock.com/docs/TweenLite/static.delayedCall()

 

 

 

 

 

I'm using setTimeout and _.delay on "onComplete", because "onComplete" fires before the animation has finished everywhere. So if I set a 10ms delay, then the animation has time to finish before whatever is in the "onComplete" function executes.

Link to comment
Share on other sites

I've added some console statements, so you can track the tween of the cards turning on the "New game" animation - https://online-solitaire.com/spider - so if you go and click "New game", you should be able to see the same logs as in the codepen example above.

 

I managed to take a screenshot of the lag of the last card:

 

https://imgur.com/BM7Jd7A

 

You can see that the last card hasn't completed it's animation.

 

Then I take a look at the new logs:

 

https://imgur.com/ml8fNlC

 

And I can see that the second last "onUpdate" log has a rotateY of -19.11.

 

If I manually give the last card that rotation, I can see that's where the lag is:

 

https://imgur.com/3iszMCo

 

In other words, the last frame being rendered before "onComplete" is called (even though it doesn't look like that in the logs), is the "rotateY" = -19.11 frame.

Link to comment
Share on other sites

Here's what it looks like in the performance timeline.

 

cppt4dw.jpg

 

 

GSAP is correctly setting the final rotation value for the last card, and onComplete is firing at the correct time. The problem is the stack for the onComplete is massive. It took over 250ms in that recording. The browser won't paint anything until the animation frame callback has finished, so that's why the card appears to lag on the second to last frame. The browser had to wait 250ms to paint the final state of the card.

 

 

 

  • Like 2
Link to comment
Share on other sites

I'm not sure what you mean that the "stack" is massive?

 

I'm not sure what you're saying here, because it sounds like you’re saying that the browser is painting the last bit of the animation after "onComplete" has been called? 

 

Is there a difference between GSAP finishing an animation and the animation actually rendering on the screen?

Link to comment
Share on other sites

46 minutes ago, karljohan108 said:

I'm not sure what you mean that the "stack" is massive?

 

Look at the image I posted. The x axis represents time. The y axis represents the call stack. And all those rectangles are function calls. Ideally, a call stack should not take longer than 16.67ms. Anything longer than that, and there's probably going to be some lag.

 

Your updateCardElements method is creating a huge call stack. Here's a closer up view. You can see that all those calls are related to updating react. Each column is a card.

 

UoljR9c.jpg

 

 

 

1 hour ago, karljohan108 said:

I'm not sure what you're saying here, because it sounds like you’re saying that the browser is painting the last bit of the animation after "onComplete" has been called? 

 

Exactly. That's how rendering works. Changing the style of element doesn't immediately force the screen to update. The browser will try to update the screen around 60 times/second (16.67ms). 

 

1 hour ago, karljohan108 said:

Is there a difference between GSAP finishing an animation and the animation actually rendering on the screen?

 

See requestAnimationFrame. That's what GSAP and every other animation library uses. It will paint after the animation frame callback has executed. 

https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

 

 

 

 

  • Like 3
Link to comment
Share on other sites

Thanks a lot for clearing that up for me. That definitely makes sense.

 

What do you suggest that I do in my case then?

 

From what I gather, I either need to find a way to get a callback when the animation is fully rendered and not just finished? Is there a way to do that?

 

Otherwise, I need to optimize my "updateCardElements" somehow? Is there a way to insert an "available" space in the call stack, so things can be rendered on screen in between?

Link to comment
Share on other sites

If you use requestAnimationFrame inside your onComplete, it should give you the space you need.

 

requestAnimationFrame(() => {
  
  this.updateCardElements();
  
  ...
});

 

 

If you need more time, you can use TweenLite.delayedCall().

 

TweenLite.delayedCall(0.1, () => {
  
  this.updateCardElements();
  
  ...
});

 

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