Jump to content
Search Community

timeline - wrong order of function execution?

annam 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

Hello :)

 

we've come across a strange issue today, it seems that some of the functions in a timeline are being executed in the wrong order if they both have 0 duration and they are positioned at time 0 in the timeline:

new TimelineMax().add(function () {
  document.getElementById('box').style.display = 'block';
  document.getElementById('box').style.opacity = 0;
}, 0)
.to('#box', 0, {
  opacity: 1
})

it seems that in this case the to() will run before the add() function. if i set the add() delay to eg 0.01, then the add() function will run first, as expected.

 

you can find a working jsfiddle here: https://jsfiddle.net/annam/bbxnf2zL/ . try changing the add() delay to see that the rectangle appears as expected if the delay is not 0.

 

DOM breakpoints confirm what i mention above, the opacity is first changed from the to() function and then from the add() function

 

any ideas why this is happening?

 

thanks!

Anna

See the Pen by annam (@annam) on CodePen

Link to comment
Share on other sites

Hello annam and welcome to the GreenSock forum!

 

Since you are using the add() method it gets added to end of timeline. You were correct to add a 0 for the position parameter, ;) But you also have to add a position parameter for the zero based to() tween that comes after it.

 

https://jsfiddle.net/bbxnf2zL/2/

 

You can see in the output console that add() outputs first and then to() gets outputted next since i added a position parameter for the zero based to() tween.

 

But basically what you can do since you are using the to() method with a duration of zero.. can also be accomplished with using set() which is a zero based tween.

 

GSAP set() : http://greensock.com/docs/#/HTML5/GSAP/TimelineMax/set/

 

So this

// so this zero based tween
.to('#box', 0, {
  opacity: 1
})

// becomes this using set()
.set('#box', {
  opacity: 1
})

One thing to keep to also keep in mind that if you are animating CSS properties, in your case opacity. You want to change those properties with GSAP so it can keep track of what your changing. So in your case change

document.getElementById('box').style.display = 'block';
document.getElementById('box').style.opacity = 0;

to this using the GSAP set() method:

TweenLite.set('box',{display: 'block', opacity:0});

This way GSAP can keep track and record your changes, and apply cross browser prefix for opacity, since webkit based browsers use -webkit-opacity.

 

GSAP add() : http://greensock.com/docs/#/HTML5/GSAP/TimelineMax/add/

 

GSAP position parameter: http://greensock.com/position-parameter

 

 

And you can also look into using autoAlpha instead of opacity for a performance boost. autoAlpha is part of the GSAP CSSPlugin

 

http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/

  • autoAlpha
    Identical to opacity except that when the value hits 0 the visibility property will be set to "hidden" in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than 0, visibility will be set to "inherit". It is not set to "visible" in order to honor inheritance (imagine the parent element is hidden - setting the child to visible explicitly would cause it to appear when that's probably not what was intended). And for convenience, if the element's visibility is initially set to "hidden" and opacity is 1, it will assume opacity should also start at 0. This makes it simple to start things out on your page as invisible (set your css visibility:hidden) and then fade them in whenever you want.
//fade out and set visibility:hidden
TweenLite.to(element, 2, {autoAlpha:0});

//in 2 seconds, fade back in with visibility:visible
TweenLite.to(element, 2, {autoAlpha:1, delay:2});

Also keep in mind when creating your new TimelineMax() constructor to always create it in a var so the JS browser parser knows your intent and to prevent strict JS parsers like IE to not parse right with issues.

 

Does that help? :)

Link to comment
Share on other sites

Hi Jonathan,

 

thanks for all the feedback and suggestions, I appreciate it and we will look into adding these improvements. 

 

It seems strange that I would need to add a position parameter to to() if the add() delay is 0, but not if it is not 0.01, in which case even though the add is executed later, the to() function seems to understand this and is executed after the add. From what I understand from the docs, both the add() and the to() functions will be added to the end of the timeline if a position parameter is not explicitly passed to them.

 

I guess we could easily work around this issue by passing "+=0.01" as the position to the to() function. We'd like the two functions to run as close to each other as possible, which is why we haven't added any delay to the to() function, and both duration and delay are actually variable values which is why we are using to() and not set().

 

I just thought i should report this as a possible bug in gsap.

 

Thanks for looking into this!

 

Regards,

Anna

  • Like 1
Link to comment
Share on other sites

A zero-duration tween always executes IMMEDIATELY unless either of these conditions is true:

  1. You set immediateRender:false on the tween (I'd recommend that here)
  2. If it's created via a timeline convenience function (like timeline.to(), as you did here) **AND** it is not at the very beginning of the timeline. That's because if it's at the very start, there's not supposed to be any delay. 

From what I can tell, things are working exactly as they're supposed to (though I don't blame you from being a bit confused in this scenario). And again, there are reasons for the behavior. For example, imagine doing something like this:

var obj = {prop:0};
TweenLite.to(obj, 0, {prop:100}); //-OR- TweenLite.set(obj, {prop:100})
console.log(obj.prop); //what would you expect here?

It'd be really weird if obj.prop was still 0 at that point. Most developers would be like "what the heck? I just set it to 100...why is it not taking effect? Why do I have to wait 1 tick?" Zero duration means...zero duration, so it just happens instantly. But you were building a timeline and it's impossible to read the mind of the developer to understand their intent - if they're putting a zero-duration tween at the very start, do they want us to wait to render? Should we render immediately? Valid arguments could be made for either behavior. That's why we have the immediateRender special property - so that developers can specify their intent with a zero-duration tween. 

 

So the solution in your case is to just set immediateRender:false on your to() tween. 

 

Also, FYI, it's more concise to use set() instead of a to() with a zero duration (the results are identical)

 

Does that clear things up? 

  • Like 4
Link to comment
Share on other sites

Hi Jack, 

 

thanks for the explanation,

 

so if I just use immediateRender: false this issue will be fixed. Is there something else that this might affect? As I mentioned earlier, duration and delay are variable values, do I need to only set immediateRender to false if these values are 0 or can this always be false with no unexpected side effects? 

 

Thanks again!

Anna

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