Jump to content
Search Community

Possible bug with call()

gum 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 had this strange bug in a game I was making that caused the player to be able to click twice on a button, even though the event listener removed itself which should have prevented the second click. After spending many hours on this I've managed to boil it down to what I think is a bug in how TimelineLite handles call()

 

Here's an example:

var obj = {value: 0};
var timeline_1 = new TimelineLite({delay: 1, onStart: function() { console.log("timeline_1 start") }, onComplete: function() { console.log("timeline_1 complete"); }})
.to(obj, 0.5, {value: 1})
.add(function() {
  console.log("function 1");
  timeline_2.play(0);
}, 0.4)

var timeline_2 = new TimelineLite({paused: true, onStart: function() { console.log("timeline_2 start") }, onComplete: function() { console.log("timeline_2 complete"); }})
.to(obj, 0.5, {value: 0})		
.add(function() {
  console.log("function 2");
  timeline_1.play(0);
}, 3);	

The first timeline tweens an object's value to 1 during 0.5 seconds, and calls a function after 0.4 seconds. That function goes on to play the second timeline (previously paused). The second timeline starts tweening the same object's value back to 0 (0.1 seconds before the first tween is done, causing an overlap), and after 3 seconds calls a function that plays the first timeline from 0 again, effectively creating a loop.

 

The expected behaviour (for me at least!) is that the tween on the second timeline will overwrite the first, cutting it a little short.

 

What actually happens is that the first timeline's call() function runs twice right after each other (logging "function_1" twice to the console), even though the first timeline is only started once!

 

This only happens when the two tweens overlap. If I nudge the function calls and/or tweens so the second tween doesn't overwrite the first, the problem is gone. The same goes for when I have the second tween affect some other unrelated value instead: no problem.

 

Have I misunderstood something here, or is this in fact a bug?

See the Pen xZgyBV by anon (@anon) on CodePen

Link to comment
Share on other sites

Thanks for the demo, it was very helpful. 

 

There may or may not be a bug in the engine, but we will have to investigate this a little further to find out for sure.

 

However, the main issue here is that if an overwrite does occur, other logic in this setup is going to fail. Keep in mind that once an overwrite happens, the tween that is overwritten is killed which means it ceases to exist and will never run again. If you intend to loop between these timelines and have them both fighting over the value of obj.value things are going to blow up as soon as timeline_2 starts playing.

 

Please note that I added an onUpdate callback to the tween in timeline_1 that logs obj.value

.to(obj, 0.5, {value: 1, onUpdate:function(){console.log(obj.value)}});

http://codepen.io/GreenSock/pen/jWydWz?editors=001

 

Notice how that log stops running as soon as timeline_2 starts playing and taking control of obj.value?

 

This is the correct behavior.

 

---

 

If you put overwrite:'none' on the tween in timeline_2

.to(obj, 0.5, {value: 0, overwrite:'none'}) 

The tween in timeline_1 will continue to update on each iteration of timeline_1 (after "timeline_1 start") and "function 1" will log only once. Take a look here:

http://codepen.io/GreenSock/pen/JGExRP?editors=001

 

Please note: on both demos I pause both timelines after 10 seconds to make it easier to read all the consoloe.log()s.

 

Summary:

 

If you want to have overlapping tweens and still have the tween in the first timeline exist so that it can be played in future iterations of your loop, you must set overwrite:'none' in the second timeline's tween. This is totally inline with the API design. 

 

I am not sure why the overwrite causes the function called via add() to fire twice when the tween is overwritten and I hope to find an answer for you.

  • Like 3
Link to comment
Share on other sites

  • 3 months later...

I'm pretty sure it's not really a "bug" per se, but I can see why you might think it was. To clarify, the tween that was getting killed by the overwrite was essentially "propping it open", meaning it was the determiner of the duration of the timeline (since it was last). Thus when it was killed, the timeline resized which forced the playhead to move (since the playhead cannot be positioned beyond the end of the timeline). Let's say the timeline rendered at 0.41 seconds which tripped the call() at 0.4 which triggered the overwrite which killed the tween that made the timeline 0.5 seconds long, so now it's 0.4 seconds long (that's where the call() is). So technically the playhead was forced to move back from 0.41 to 0.4 in this case, meaning it needed to re-render. 

 

Again, I wouldn't consider that a bug but how that you understand the mechanics hopefully it's easy to adjust for and get the behavior you want (probably by setting overwrite:false, although there are other ways). 

  • Like 1
Link to comment
Share on other sites

Just add my two bits ;)

 

Here is a list of the overwrite property values available for your tweens.

 

Taken from the GSAP to() Docs:

 

http://greensock.com/docs/#/HTML5/GSAP/TweenMax/to/

 

Choose from any of the following: "auto", "all", "none", "allOnStart", "concurrent", "preexisting".

 

The default value is "auto".

 

overwrite : String (or integer) - Controls how (and if) other tweens of the same target are overwritten. There are several modes to choose from, but "auto" is the default (although you can change the default mode using theTweenLite.defaultOverwrite property):
  • "none" (0) (or false) - no overwriting will occur.
     
  • "all" (1) (or true) - immediately overwrites all existing tweens of the same target even if they haven't started yet or don't have conflicting properties.
     
  • "auto" (2) - when the tween renders for the first time, it will analyze tweens of the same target that are currently active/running and only overwrite individual tweening properties that overlap/conflict. Tweens that haven't begun yet are ignored. For example, if another active tween is found that is tweening 3 properties, only 1 of which it shares in common with the new tween, the other 2 properties will be left alone. Only the conflicting property gets overwritten/killed. This is the default mode and typically the most intuitive for developers.
     
  • "concurrent" (3) - when the tween renders for the first time, it kills only the active (in-progress) tweens of the same target regardless of whether or not they contain conflicting properties. Like a mix of "all" and "auto". Good for situations where you only want one tween controling the target at a time.
     
  • "allOnStart" (4) - Identical to "all" but waits to run the overwrite logic until the tween begins (after any delay). Kills tweens of the same target even if they don't contain conflicting properties or haven't started yet.
     
  • "preexisting" (5) - when the tween renders for the first time, it kills only the tweens of the same target that existed BEFORE this tween was created regardless of their scheduled start times. So, for example, if you create a tween with a delay of 10 and then a tween with a delay of 1 and then a tween with a delay of 2 (all of the same target), the 2nd tween would overwrite the first but not the second even though scheduling might seem to dictate otherwise. "preexisting" only cares about the order in which the instances were actually created. This can be useful when the order in which your code runs plays a critical role.

Happy Tweening :)

 

 

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