Jump to content
Search Community

Slowing Down a Timeline

djwaas test
Moderator Tag

Recommended Posts

Hello Everyone:

 

I have created 52 playing cards, and placed them at the same X position outside the stage.

The 52 playing cards mc are stored in the currentDeck[] table.

Using a TimelineMax, I make these cards scroll from right to left at a high speed.

When the app user hits a button, the overall scrolling speed will decrease, until everything stops.

The card in the center of screen will be the card the user gets from this "lottery wheel".

//Creating the "slower" variable, that will let me slow down later my animation/timeline.
public var slower:Number=1 ;

//Let's create now the Timeline that will rapidly scroll 52 cards horizontally on the screen from right to left in an infinite loop.
//Cards are spaced so that, at any time, there are three cards displayed on screen.
tl = new TimelineMax({repeat:-1, repeatDelay:0});
for (var j:int=0; j<52; j++)
{
tl.to(currentDeck[j], 0.5, {x:finalX, ease:Linear.easeNone} , "-=0.35");
}

//Now, when the user hits the button, the srolling should slow down then stop
//This will be achieved by tweening the "slower" value from 1 to 0
public function slowerThenStopBtn_handler(e:MouseEvent) 
{
	TweenMax.to(this, 2, {slower:0, onUpdate:changeDuration, onComplete: postRalenti});								
}

//Each new value of "slower" is then re-injected in my running timeline
public function changeDuration()
{
	tl.timeScale(slower);			
}
	
public function postRalenti()
{
	tl.stop();
}

Now this code works, but it's not perfect.

 

Question 1: EaseOut before the scrolling stops.
Is there a way to get a Bounce.easeOut easing on the scrolling animation before it stops?
This will enforce the "lottery wheel" effect.

I tried to apply the EaseOut easing on the tweening of "slower" variable, but it doesn't give the expected result.

Question 2: Continuous loop illusion.
When the last card #52 scrolls out of the screen, the screen is empty for a split second before the loops starts again and card #1 appears on screen.
How can I make cards #1 & #2 starts scrolling earlier so that 3 cards are always displayed, even at the end of the first loop.
Trying to set repeatDelay:-0.35 makes the last card #52 disappear suddenly from screen while scrolling, which ruins the continuous loop illusion.

Question 3: Outcome of the lottery wheel.

Now by slightly changing the duration of the tween of the "slower" variable, I can manage to make a specific card to end up at the center of screen when scrolling stops.

Is there a more precise and smarter way to do so?

 

Well, thanks a lot and kudos to Greensock for such fun and useful products!!!

 

Best regards.
 

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums.

Thanks for the clear explanation and questions. I think I have some very good news for you.

 

Question 1: EaseOut before the scrolling stops.

 

Instead of tweening a variable and then applying it to a timeline's timeScale, you can tween the timeScale directly!

var tl:TimelineLite = new TimelineLite();
tl.to(mc, 1, {x:200});
tl.to(mc, 1, {y:300});

// tween the timeScale() of tl

TweenLite.to(tl, 1, {timeScale:0, ease:Quad.easeOut})

In fact you can tween the time(), totalTime(), progress(), totalProgress() too!

 

Check out the demos and video on this page: http://www.snorkl.tv/2011/03/tween-a-timelinelite-for-ultimate-sequencing-control-and-flexibillity/

Definitely use the first demo and hit the bounce button. pretty coo, huh?

 

Keep in mind the article is a few years old, so some of the properties and methods are out-dated, like currentProgress() and currentTime().

 

Also, here is a JavaScript version which illustrates how smoothly you can accelerate and decelerate an animation: http://codepen.io/GreenSock/pen/LuIJj

AS3 uses the same syntax

 

Question 2: Continuous loop illusion.

 

It is not possible to play the beginning of the timeline while the end is still playing or have any overlap.

If you want to use a timeline, I'd suggest adding some dummy duplicate cards to the end

 

imagine this list of numbers was scrolling left

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3

 

as soon as the 10 gets offscreen you will see the black 1, 2, 3 fully reveled and you can then jump back to the beginning of the timeline where the red 1, 2, 3 are fully visible.

 

 

Question 3: Outcome of the lottery wheel.

Now by slightly changing the duration of the tween of the "slower" variable, I can manage to make a specific card to end up at the center of screen when scrolling stops.

Is there a more precise and smarter way to do so?

 

Going back to question 1, you might be able to determine at which time() each card is in the center. I would suggest experimenting with pausing the timeline and then tweening its time() to a specific value (even with an ease).

TweenLite.to(tl, 1, {time:whateverTimeCard3isInTheMiddle});

might take some experimenting.

 

Hopefully this helps put you in the right direction.

Link to comment
Share on other sites

  • 1 month later...

So I thought my code was done, but I realized it needs that little something to be perfect.

My 52 cards follow a circular path from one side of the screen to the other, at high speed to create an illusion of wheel of fortune.
When the user pushes a “stop” button, the wheel slows done abruptly, with an elastic easing.
He gets an illusion that the card that stops at the very center is a random card, but in reality, my code forces the “wheel” to stop at a very specific card.

My problem is the following: when debugging on pc, the wheel stops with the forced card at the very center of the screen, except when I do something that “bothers” the course of events, like resizing my flash window, in that case, my card to be forced ends up off center.
BUT: When testing on a mobile phone (so in an environment with less CPU power), the wheel stops 3 times on 4 off center, sometimes very close, sometimes quite far from center.

My code is structured in this manner:

  • TL1 tweens my cards from right to left. At the creation step of the timeline, it’s at a stopped state.
  • TL0 will control TL1 by activating the tl1 animations through “timescale:1 action”.
  • When “STOP” button is pushed, TL1 stops and all cards are replaced on the right, at their initial positions.
  • TL8 is created. Just like TL1, it tweens my cards from right to left. At the creation phase, it’s at a stopped state.
  • TL5 will control TL8 by simultaneously launching it AND stopping it through“timescale:0 action”.
  • TL8 will stop at the right card on the center by using tl8.seek() function with the right value inside.
  • gapFiller is a function that runs a tween of cards that fills the gap between my 52nd card and 1st card.
     
//When START button is hit, the wheel of playing cards start spinning endlessly

TweenPlugin.activate([CirclePath2DPlugin]);

circle = new CirclePath2D(518.85, 1600, 1050);

tl1 = new TimelineMax({repeat:-1, repeatDelay:-0});
tl1.timeScale(0); // tl1 Timeline is created in a stopped state, tl0 will launch it.

tl1.to(card[0], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
tl1.to(card [1], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
tl1.to(card [2], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
// (…) Rest of the cards
tl1.to(card [48], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
tl1.addLabel("tween3");
tl1.to(card[49], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
tl1.to(card [51], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
tl1.addCallback(gapFiller, "tween3"); //When the 48th card is moving, a different and shorter deck of cards is tweened to fill the gap between the 52nd and 01st card.

//Timeline 1 is the timeline that runs when “START” button is pushed
tl0 = new TimelineMax();
tl0.to(tl1, 3, {timeScale:1, ease:Linear.easeNone});


//Now, when the power button is hit to stop the wheel
public function btnPower_handler(e:MouseEvent)
{
     //The first tween of cards is stopped
     tl1.stop();

     //All cards are replaced in their original position, off screen, on the right
     for (var i:int=0; i<52; i++)
     {
          card[i].x = initialX;
          card[i].y = initialY;
     }

     //tl8 timeline will replace the tl1 on screen
     tl8 = new TimelineMax({repeat:-1, repeatDelay:-0});

     tl8.stop(); // tl8 Timeline is created in a stopped state, tl5 will launch it.

     tl8.to(card [0], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");

     tl8.to(card [1], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");

     // (…) Rest of the cards
     tl8.to(card [51], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");

     tl8.resume(3); //In order not to start from the very beginning and expose a gap in my wheel, I start from the third second

     //This is the real starting point of my tl8 Timeline, I tried many values of « targetToStop” until I found the value that stops my target card at the center
     tl8.seek(targetToStop);

     //tl5 is my timeline that “controls” tl8 and gives the Elastic easing to the stopping action
     tl5 = new TimelineMax();
     tl5.to(tl8, 3, {timeScale:0, ease:Elastic.easeOut});

}

public function gapFiller()
{
     tl3 = new TimelineMax();
     for (var v:int=25; v<29; v++)
     {
          tl3.to(card[v], 0.5, {circlePath2D:{path:circle, startAngle:-27, endAngle:190, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:0}, ease:Linear.easeNone}, "-=0.35");
     }
}

So the question is: Is there a way to readjust my cards position so that when TL8 stops at the "targetToStop" value, my target card ends up in the middle?

 

Thanks a lot!!!

 

 

 

Link to comment
Share on other sites

Sorry to hear you ran into some trouble. I read over the description and the code and I feel like it would take hours to understand what is going on and what needs to be changed. 

 

If you can provide a very simple fla with only enough code necessary to consistently replicate the issue (on desktop), then perhaps we can take a look. 

We just don't have hours to sift through full production code containing multiple timelines and such. Unfortunately it just isn't the type of support we can afford to offer. 

Link to comment
Share on other sites

  • 2 months later...

Dear Carl:

 

I uploaded a SWF file at the following address:

http://we.tl/lwoy8hzphO

 

After the START menu, just push the power button to spin the wheel, then push again the same button to stop it.

Repeat the process as you wish.

You will see that when you stop the spinning wheel for the "n" times, the "n" card is the one which stops in the center.

Everything runs pretty smooth, except on phones, where sometimes the "target" card finishes off center, and that's my problem.

 

I prepared a zip file (FLA + AS) for you if you want to have a look.

Please let me know how I can send it to you privately.

 

Best regards.

Link to comment
Share on other sites

Sorry, digging into all that code and testing on devices goes beyond the type of support we offer here.

 

Since the code works as expected on desktop I'm extremely doubtful there is an issue in the GSAP engine.

 

The first step in trouble-shooting this type of problem would be to visually log some values and events. 

Place a textField on the stage and try to track the value you are sending to tl8.seek(), and add an onComplete to tl5 that logs the time of tl8 when tl5 ends.

My concern is that you are tweening the timeScale over a certain amount of time, not tweening to a particular time.

 

Instead of tweening the timeScale with an Elastic ease, I think you should consider using TimelineMax's tweenTo() or tweenFromTo() methods.

 

http://greensock.com/docs/#/HTML5/GSAP/TimelineMax/tweenTo/ you can basically tween to any time or label and apply an ease. 

Link to comment
Share on other sites

Dear Carl:


I completely understand how long it would be to read a 2.000 lines of code.

Giving me some leads to try in your last post was all I was asking for.

Thanks a million times!!!!

Here’s what I found:


I.Reminder:

 

tl8 is my timeline that moves my cards from the right to the left of the screen.

tl5 is the timeline used to slowly stop tl8 at a specific place by tweening its timescale to 0.

My problem: Sometimes, the target card finishes off center.


II. Measuring time:


I followed your advice by measuring and logging elapsed times, here’s what I found:


Test on desktop Flash Player

On tl5's complete, tl8's elapsed time is: 0.666 (target card finishes on center)

Second atempt: On tl5's complete, tl8's elapsed time is: 0.665 (target card finishes on center)

Debugging on Android phone:

On tl5's complete, tl8's elapsed time is: 0.682 (target card finishes past the center)

Second atempt: On tl5's complete, tl8's elapsed time is: 0.663 (target card finishes before the center)


tl8 stops at different times, especially on the phone, where differences are significant for each try.

QUESTION #1: Can we conclude that due to weak CPU power, the tweening engine leads to different outcomes?

I will try to make lighter PNG files for cards, maybe this will bring relief to the CPU (although displayed cards will keep same heights and width on stage, which is likely to change nothing).

 

III. Using tweento():


So I got rid of tl5, got rid of timescale, and simply added a tweento() method to my tl8.

Yes, tl8.tweento stops the target card right where it should be.

The problem is: the stopping is too abrupt, and elastic easing hardly noticeable.

I can’t control HOW (or how long) the stopping action can take with the tweento() method.

QUESTION #2: Is there maybe a way to control speed and time of a tweento action?


IV. Combining timescale with tweento():

This is where I settled so far: tl5 timescales tl8 to zero to get a nice and slow stopping of my wheel, then on tl5’s complete, tl8.tweenTo readjusts cards position right where they should be.

It's no elegant code, but at least it's 95% close to what I seeking.

 

Thanks a lot Carl for your insights!!!

 

Best regards.

Link to comment
Share on other sites

QUESTION #1: Can we conclude that due to weak CPU power, the tweening engine leads to different outcomes?

 

We haven't seen a situation where the engine isn't accurate. The problem is most likely tied to the fact that you are tweening timeScale to 0, whereas you should be tweening the animation to a set time. All you are doing is saying that over the course of X seconds the timeScale of some animation should go to 0. If you start the timeScale animation early it will end earlier. If the CPU gets hung up for 4 seconds, the tween that is tweening the timeScale() is going to be forced to completion on the next tick, which would also result in the tweened animation not progressing as far as it should.

 

QUESTION #2: Is there maybe a way to control speed and time of a tweento action?

 

tweenTo() always respects the natural rate of playback of the animation.

You can tween the time() of a Timeline for however long you like

var tl = new TimelineLite();
tl.to(box, 25, {x:200});

TweenLite.to(tl, 2, {time:25, ease:Bounce.easeOut})
Link to comment
Share on other sites

  • 4 weeks later...

Just wanted to thank you Carl for your very helpful suggestions.

 

Tweening a timescale to 0 Vs using tweento function helped me better understand the mechanics behind your tween engine.

 

In fact, getting stuck with this problem lead me to dicover a wonderful profiling tool: Scout.

I found that adding 52 times a tl.to(...) instructions when the stop button is pushed is what really hurt the timing of my timescale tweening.

 

By the way, reading about optimizing my code, I discovered two tools that sounds promising:

- Starling API that makes better use of the GPU power

- Object tooling method

 

For someone who wants 52 cards that moves very fast from one side of the screen to the other, do you think I should try out the two suggestions above for better performance or frame rate?

 

Have a nice weekend.

 

BR

Link to comment
Share on other sites

Cool. Glad to hear you are making progress and gaining a better understanding of the engine.

I think if you want the fastest, smoothest performance, Starling is worth investigating. I've heard great things about it and seen some very impressive demos.

 

As for object tooling... I think you meant object pooling, and yes in some cases (mostly in particle effects) it can make a big difference to do things like repeat tweens on 100 objects than create 1000 objects.

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