Jump to content
Search Community

Unexpected behaviour when adding Tweens to a Timeline

Héctor test
Moderator Tag

Recommended Posts

Hello,

 

It's has been some time since I don't post here so I'll seize this oportunity to thanks again the greensock comunity for this great free framework.

 

I've been trying to do something easy: An animation of an sprite being pulled down from the bottom of the stage, and then pushed down again. Some simple transition. I have 2 functions for that (they use speed instead of seconds)

 

public static function pushDown(target:DisplayObject, onCompleteFunction:Function = null, onCompleteParams:Array = null, speed:Number = 1200):TweenLite {
  var distance:Number = stageHeight - target.y;
  var duration:Number = distance / speed;
  trace("PUSH: target.y = " + target.y + "  distance = " + distance);
  return TweenLite.to(target, duration, { y: target.y + distance, onComplete: onCompleteFunction, onCompleteParams:onCompleteParams, ease:Linear.easeNone } );  
 }

 public static function pullDown(target:DisplayObject, onCompleteFunction:Function = null, onCompleteParams:Array = null, speed:Number = 1200):TweenLite {  
  var distance:Number = stageHeight - target.y;
  var duration:Number = distance / speed;
  trace("PULL: target.y = " + target.y + "  distance = " + distance);
  return TweenLite.from(target, duration, { y: target.y + distance, onComplete: onCompleteFunction, onCompleteParams:onCompleteParams, ease:Linear.easeNone } );  
 }

 

The problem comes when I use them in a simple timeline:

 

var timeline:TimelineLite = new Timeline({paused:true});
timeline.append(SlideAnimations.pullDown(object));
timeline.append(SlideAnimations.pushDown(object));

// play will takes place within a listener handler function (ON_ADDED_TO_STAGE), but ill simplify it
timeline.play();

 

What I have realized (tracing the y property of the object being tweened) is that before the timeline is created, the object has y = 0. But then after appending the first tween, the y property of the object is changed. I didnt expect this to happen. Shouldn't the property being tweened change during/after the tween is taking place?

 

In other words, the first tween (TweenLite.from) should not change the Y property of the object being tweened until the timeline starts to play, but what I have seen is that it gets changed just after adding it to the timeline.

 

I hope what im trying to explain is understandable, if not, ill add more details. Thanks for your time.

Héctor

Link to comment
Share on other sites

Are both pullDown() and pushDown() supposed to move the object down?

 

An animation of an sprite being pulled down from the bottom of the stage, and then pushed down again.

 

I'm confused by the description of something being pulled down from the bottom, are we not supposed to see that part of the animation?

 

That aside, from the description of your traces I think what you need is to use immediateRender:false on the from tween:

 

 return TweenLite.from(target, duration, { y: target.y + distance, onComplete: onCompleteFunction, onCompleteParams:onCompleteParams, ease:Linear.easeNone, immediateRender:false } );  

 

that will give the the traces of:

 

 

PULL: target.y = 0  distance = 400
PUSH: target.y = 0  distance = 400

 

perhaps this description from the TweenLite.from() docs will help:

 

 

NOTE: By default, immediateRender is true in from() tweens, meaning that they immediately render their starting state regardless of any delay that is specified. You can override this behavior by passing immediateRender:false in the vars object so that it will wait to render until the tween actually begins (often the desired behavior when inserting into timelines). To illustrate the default behavior, the following code will immediately set the alpha of mc to 0 and then wait 2 seconds before tweening the alpha back to 1 over the course of 1.5 seconds:

 

TweenLite.from(mc, 1.5, {alpha:0, delay:2});

Link to comment
Share on other sites

I think the names I gave to the functions aren't the best and they may lead to confusion. The animation does the following (I will explain in movement terms).

  • An object is being moved up from the bottom of the screen. Initially, the object isnt visible because it is out of the stage, and then it starts to be visible as it moves up.
  • Then the movement is just the oposite. The object is moved down from its position on the stage to the bottom of the stage until it is out of the stage.

Thanks for mentioning that property. I think it may be useful but then I'll have to deal with pre-positioning the object outside the stage so the animation starts from then. If not, the object seems to start in its tweening end position, then jump to its tweening start position (because is a from tween) and then end again in the starting position.

 

I'll look it again more carefully.

Thanks again.

Link to comment
Share on other sites

I think what I need in my case, is not the first tween [the from() call] being not inmediately rendered, but the second [the to() call].

 

Lets suppose I have this simple tween:

 

// simple tween
TweenLite.to(object, 5, {y: object.y + 100});

 

¿Is there any way the tween renders and read the object.y property just before the tween starts instead of the moment the tween is being created? I think I could use the onInit property, but maybe is there any easier way of doing it. Similar to the inmediateRender property for the from() method, that I can't find in the to() method.

Link to comment
Share on other sites

yes I think the onInit property or timeline.invalidate() method may help but I must admit that I am having difficulty understanding what the problem is.

 

in most cases I would prefer to place the assets into their starting positions and then call a method that builds or rebuilds the timeline immediately prior to it playing.

Link to comment
Share on other sites

What I wanted to avoid is just that, having to place assets into positions before tweening, so I could build the timeline on the objects or assets construction and then just play that timeline when I just add the object to the stage after having set its coordinates (using an ON_ADDED_TO_STAGE event handler)

 

So in order to create an animation what i would do is the following:

  • Create the display object. Create the timeline within its constructor call
  • Setting the display object coordinates in the stage, and adding the ON_ADDED_TO_STAGE listener
  • Playing the timeline in the listener handler function. At this time is where the different tweens in the timeline, should try to read the object property values, but just when they are about to start in the timeline.

In this way I could plan all the animations without having to care about starting positions (setting x and y outside of the stage for example) and just setting the final coordinates where the object is doing all the rest of the stuff.

 

I will try another aproach, where the tweens can be built before the animation or timeline takes place even if i had to pass property values through parameters and setting starting positions prior to the animations.

Link to comment
Share on other sites

I'm kinda scratching my head too trying to understand your goals and why the existing behavior isn't what you desire. Maybe this will help:

 

When you create a from() tween, you either want it to IMMEDIATELY set the starting position as you define it in the tween or you don't. If you don't, it will simply jump to that starting position when the tween actually begins running. There's really no other conceivable option (or am I missing something?).

 

For example, if I have a from() tween that has a delay of 5 seconds, the default behavior is to IMMEDIATELY set the starting values as you define them, then wait 5 seconds, and then animate them to the end position which was recorded when you created the tween. However, if you set immediateRender to false, it won't do ANYTHING until that 5 seconds expire and then it will perform the tween (jumping to the from() values and proceeding from there).

 

I feel like there must be something that we're miscommunicating.

 

Maybe you don't mean to do from() tweens? Maybe you just meant to do to() tweens?

Link to comment
Share on other sites

Ok, I have just realized where the fail was, and of course, it wasn't in the Tweening engine.

 

The problem is the duration calculation when the tween is being created. I'll copy the code I was using to replicate the problem above:

 

package {
import com.greensock.easing.Linear;
import com.greensock.TimelineLite;
import com.greensock.TweenLite;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;

public class Main extends Sprite {

 private var timeline:TimelineLite;
 private var sprite:Sprite;

 public function Main():void {  
  sprite = new Sprite();
  sprite.graphics.beginFill(0xFF0000);
  sprite.graphics.drawRect(0, 0, 20, 20);	
  sprite.x = 400;
  sprite.y = 300;
  sprite.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);  
  addChild(sprite);
 }

 private function moveToBottom(target:DisplayObject, speed:Number = 1200):TweenLite {
  var distance:Number = 600 - target.y;
  var duration:Number = distance / speed; // 0 duration in execution means tween wont run
  trace("[onCreation]moveToBottom: target.y = " + target.y + "  distance = " + distance);
  return TweenLite.to(target, 1, { y: 600, ease:Linear.easeNone, onInit:onInitMoveToBottom} );
 }

 private function moveFromBottom(target:DisplayObject, speed:Number = 1200, inmediateRender:Boolean = true):TweenLite {
  var distance:Number = 600 - target.y;
  var duration:Number = distance / speed;
  trace("[onCreation]moveFromBottom: target.y = " + target.y + "  distance = " + distance);
  return TweenLite.from(target, 1, { y: 600, ease:Linear.easeNone, immediateRender:inmediateRender, onInit:onInitMoveFromBottom } );
 }
 private function onInitMoveToBottom():void {
  trace("[onInit]moveToBottom: target.y = " + sprite.y);
 }

 private function onInitMoveFromBottom():void {
  trace("[onInit]moveFromBottom: target.y = " + sprite.y);
 }

 private function onAddedToStage(evt:Event):void {
  timeline = new TimelineLite( { paused:true } );
  timeline.append(moveFromBottom(sprite,1200),1);
  timeline.append(moveToBottom(sprite, 1200), 1);
  timeline.play();
 }
}
}

 

The problem is on the creation of the to() tween. When it's created, target's y property is 600 so the distance is 0 (600 - 600) and as a result, tween duration is 0. So I think the solution should be simple if I can calculate the duration during the onInit "event" instead of the tween creation time.

 

The only doubt I have is how could I change the tween duration (better said, how could I pass the tween Im am creating during its creation as a onInitParam?) Is this possible?

 

This doesn't seem to work:

var tween:TweenLite = TweenLite.to(target, 0, { y: 600, ease:Linear.easeNone, onInit:calculateDuration, onInitParams:[tween, speed]} );

 

Using a class level variable also doesn't work. Any ideas?

Link to comment
Share on other sites

I'm not sure but after having looked through the code I think the way of changing tween properties in the onInit "event" won't work. Because it seems the onInit function is called just before setting tween properties, and of course, you won't be able to change the duration or whatever other property of a tween that is not created.

 

Relying in the onStart method doesn't seem a good solution because if I've understanded it well, it would get called with the tween already started, and that could cause unexpected jerks and so.

Link to comment
Share on other sites

Disclaimer: it looks to me like you're probably doing logic back flips that are getting you into pinches that would be eradicated if you backed up a bit and took a different, simpler approach.

 

For example, why are you creating tweens before you even know how long you want to make them? Why not just create and insert them when you have the info you need rather than doing half the job in one spot and operating on those tweens later on right before they start? I'm half brain-dead right now so I may very well be missing something obvious, but your approach seems highly unconventional.

 

To answer your question about how to pass the tween as the parameter, that's actually quite simple in v12 (although it looks like you're using v11). If you update to v12, you can simply use "{self}" in any of the parameters and it will automatically get replaced with a reference to the tween itself. See the notes at http://www.greensock.com/v12/ for details. Also note that onInit was dropped in v12 because quite frankly it seemed unnecessary and you can accomplish pretty much the same thing in other ways. onStart is still there though.

Link to comment
Share on other sites

Finally I took your advise, and took another approach. Using onComplete callbacks for loading following tweens rather than appending all of them to a timeline in advance. More code, but less problems :)

 

I was using v11 still because of the v12 beta status, but seeing there are no bugs and people seem to give good feedback im upgrading now.

 

Thanks for your time.

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