Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
richardCANE

Multiple TimelineLite Against Each Other/Concurrent Problem

Recommended Posts

My current project (JavaScript/GSAP) needs lots of tweening on different elements at the same time (it will be a heavily tweened/animated site).

In "lots of" I mean 50-75 "TweenLite.to"s per click event, in total about 150-300 css properties to tween.

 

It would be used all over the site on elements (buttons, menus, forms). Most of the elements will have 2 states, an "active" and an "inactive" state, basically when the user clicks on a menu, the menu will be activate, click again/elsewhere and goes to inactive.

 

To efficiently manage the tweens, I would like to use TimelineLite, create all the tweens after the site loads and store them in 2 TimelineLite instances avoiding the "TweenLite.to"s again and again on each event/state change.

 

- the "activateTimeline" and "inactiveTimeline" look like like this:

var activeTimeline = new TimelineLite({ paused: true,
                tweens: [ ...an then lots of tweens with "new TweenLite(...)"s... ]
});
var inactiveTimeline = new TimelineLite({ paused: true,
                tweens: [ ...an then lots of tweens again with "new TweenLite(...)"... ]
});

This works really well.

But there is a serious problem.

When the user clicks rapidly or programatically change states BEFORE the finish ("onComplete") of the ongoing timeline, jumps and slowdowns will occur.

 

This is not the case, when using "TweenLite.to"s on every event over and over again as the TweenLite system will override values and nicely finishes off tweens as naturally would be expected.

 

I have tried to use some conditions to somehow kill/invalidate the ongoing "old" timeline the let the "new" timeline on state change finish nicely as I would expect, also when starting over the timeline again:

function activate() {
            if( !active ) {
                var activeProgress = headerTimelineActive.progress();
                var inactiveProgress = headerTimelineInactive.progress();

                // sometimes, on very rapid clicking/programatical state changes, mostly the headerTimelineActive.progress() will give a NaN
                if( isNaN(activeProgress) || isNaN(inactiveProgress) ) {
                    reinitializeTweens();
                    activeProgress = headerTimelineActive.progress();
                    inactiveProgress = headerTimelineInactive.progress();
                }
                if( inactiveProgress > 0 && inactiveProgress < 1 ) {
                    headerTimelineInactive.kill();
                }
                if( activeProgress > 0 ) {
                    headerTimelineActive.pause(0, true);
                }
                headerTimelineActive.play();

                active = !active;
            }
        };
        function deactivate() {
            if( active ) {
                var activeProgress = headerTimelineActive.progress();
                var inactiveProgress = headerTimelineInactive.progress();

                if( isNaN(activeProgress) || isNaN(inactiveProgress) ) {
                    reinitializeTweens();
                    activeProgress = headerTimelineActive.progress();
                    inactiveProgress = headerTimelineInactive.progress();
                }
                if( activeProgress > 0 && activeProgress < 1 ) {
                    headerTimelineActive.kill();
                }
                if( inactiveProgress > 0 ) {
                    headerTimelineInactive.pause(0, true);
                }
                headerTimelineInactive.play();

                active = !active;
            }
        };

...but these methods did not help.

Again, on rapid clicking/programatically state changes/click before the actual timeline "onComplete"s jumping, jittering and some sort of race/concurrent conditions occur.

 

See the Pen Chuiz?editors=001 by anon (@anon) on CodePen

 

Please, give me some advice on what should I do/how should I do to use 2 "TimelineLite"s as I described and on state changes, somehow stop the "old" timeline and let the "new" timeline go.

 

I wolud like to get the same effects/tweening experiences, when using just "TweenLite.to"s again and again (as the TweenLite system will override values and nicely finishes off tweens as naturally would be expected), but with 2 "TimelineLite"s.

 

If I can get this to work with 2 timelines, there will be menus, when a 3rd state will be introduced, a "disabled" state with a 3rd TimelineLite. But currently, I am not able to do, what I wolud like to do with 2 timelines as I described.

 

Thank you in advance, thank you all!

Link to post
Share on other sites

Hello richardCANE, and Welcome to the GreenSock Forums!

 

Ok you can try these 2 ways:

 

First way is just invalidate() your tweens before you restart() them, so it can clear out the tweens previous initialization values. 

 

Using Invalidate() - Example 1:

See the Pen xcFHu by jonathan (@jonathan) on CodePen

activeTimeline.invalidate();
activeTimeline.restart();
...
inactiveTimeline.invalidate();
inactiveTimeline.restart();

Second way is to place your new tween instances inside the function.

 

Tween Instances inside function - Example 2:

See the Pen lehKE by jonathan (@jonathan) on CodePen

 

Notice in Example 2, how I just have the new tween inside your mousedown event handler function so it can create a new tween on each run of the function. In this case you wouldn't need to invalidate or kill the tween since the new tween would be created on each run of the function.

 

Either would work.. but it looks like invalidate() would be the easiest with your existing code

 

Does that help? :)

  • Like 3
Link to post
Share on other sites

Hey Jonathan!

 

Thank you for your response!

 

The "Example 1" -

See the Pen xcFHu by jonathan (@jonathan) on CodePen

you forked does not work, when you click rapidly (please try out yourself - click rapidly for 10-20 times, the tweens "die").

 

As for the "Example 2" - if you read what I wrote, what is my situation with the lots of tweens, I deliberately do not want to instantiate 40-50-60 TweenLite instances on every state changes (so fundamentally on every "onClick" events) again and again in functions.

 

The TweenLite and TimelineLite (and the "Max"-es) are insanely great tools, when tweens are need to be done.

 

Some, one or more proper solutions should exist for this kind of problem as I am probably not the first one, who needs tons of tweens and tries to basically group them and play back-and-forth the grouped tweens to reach visual pleasentness yet with nice performance.

 

I showed you what I tried within the "activate" and "deactivate" functions, my CodePen link is just the barebone minimal, when someone had this problem before.

 

Thanks again!

  • Like 1
Link to post
Share on other sites

Hi,

 

As you say this can be a tricky situation.

 

Jonathan's samples work fine (specially the second one) for toggling from one tween to another.

 

Have you consider using just one tween for each element and play/reverse that one based on the events the user triggers?. You mentioned a lot of UI elements in your previous posts and clicking elsewhere to toggle between states. Take a look at the following codepen and let us know if it helps:

 

See the Pen enlAD by rhernando (@rhernando) on CodePen

 

Rodrigo.

  • Like 2
Link to post
Share on other sites

Hey Rodrigo!

 

Thanks for the CodePen!

One of my first thoughts on this issue during my previous days whas just one TimelineLite and play back-and-forth, basically something like:

//... in the onEvent function
// .....
if( !active ) {
     timeline.play(0, true); // play from 0th and surpress events
} else {
     timeline.reverse(); // play reversely
}
// .....

...and this would really work most *.easeInOut and for linear/none easing functions.

However, for the sake of swiftness, I try to tween UI, but also provide fast and usable interface.

 

Therefore, I use Expo.easeOut easing function for both the "active" and the "inactive" timelines.

 

Even with using .easeOut for both timelines, it would be pretty straightforward to implement this in a back-and-forth manner (basically create the "active", then mirror it to get the .easeOut easing effect for the "inactive" section of the timeline). If I will not be able to solve this issue, I would have to do this way.

 

But when I want to introduce the 3rd state as I wrote, it would be quite long to implement and for larger scales, it is unfeasible.

 

If this is not possible with TimelineLite, do you know guys some method to group the TweenLite instances together with only one instantiations and after that juts use them?

 

Something like this:

// instantiate them once
var tweens = new TweenLite(
   [object1, duration, {...props for 1...}],
   [object2, duration, {...different props for 2...}],
   [object3, duration, {...diff props for 3...}],
   [object4, duration, {...diff again props for 4...}],
   [object5, duration, {...different props for 5...}],
);

// ...then later...
tweens.to(); // somehow call them at once efficiently

// ...then later again
tweens.to(); // call them AGAIN at once efficiently

// ...and then later again
tweens.to(); // call them AGAIN MORE at once efficiently

Are there any methods/ways to doing this?

 

...I was thinking also to somehow "dynamicProp" the tweening props (maybe using TweenLite/TimelineLite "onEvents"), but from performance perspective, it would be similar to reinstantiation of the tweens, so this is not a feasible way either.

 

Any additional suggestions/advices would be quite helpful, even just mere thoughts/notices.

Thank you guys again, thank you in advance!

Link to post
Share on other sites

Hi,

 

Well dynamicProps is an AS plugin, not ported to GSAP's JS flavor.

 

Fortunately the engine is so flexible that give us another option for this cases. Basically GSAP can tween any numeric value of any object. In this case the tween assigned to every element is an object and has, among others, a progress property that goes between 0 and 1.

 

So basically you can tween the progress value to 1 and 0 using Expo.easeOut on both, like this:

// create the tween with a linear easing function. constant speed so to speak
var t = TweenLite.to(e,0.5,{backgroundColor:"green", color:"cyan", paused:true, ease:Linear.easeNone});

// this is equivalent to play the tween forward
TweenLite.to(t, t.duration(),{progress:1, ease:Expo.easeOut});

// this is equivalent to play the tween backwards
TweenLite.to(t, t.duration(),{progress:0, ease:Expo.easeOut});

I've updated the codepen:

 

See the Pen enlAD by rhernando (@rhernando) on CodePen

 

Rodrigo.

  • Like 4
Link to post
Share on other sites

Hi,

 

Since you are tweening from salmon to red (on close) and then red to blue (on open), there is the potential for a harsh color jump, but you should be able to reverse the direction of the height tween very smoothly and also always have an Expo.easeOut ease.

 

respecting the color changes shown in your example, see if this works for you: http://codepen.io/GreenSock/pen/qyseh/

 

If you only need to tween between 2 colors (red and blue) you can just use a single timeline:

http://codepen.io/GreenSock/pen/IrlhF?editors=011

 

You'll note that in both my examples the timelines have tweens with eases of Linear.easeNone. Using the tweenTo() and tweenFromTo() methods we can apply the Expo.easeOut ease each time we change direction.

 

--

 

FWIW Like Rodrigo, I had no problem with Jonathan's second demo. I clicked very rapidly and there were no hiccups or flaws. strange.

 

Also, although I am having a tough time understanding your exact scenario, I would be very inclined to go with Rodrigo's approach. I have to admit that I'm having a little trouble imagining how you are implementing this on 50 to 60 elements and need to it to work for 3 states.

  • Like 1
Link to post
Share on other sites

Actually, I just had another idea - how about using the CSSPlugin and tweening the actual CSS rules? This way you can tween ONE thing, and have it apply to everything that uses that class:

 

See the Pen Katbq by jamiejefferson (@jamiejefferson) on CodePen

(edited to include disabled state)

 

It would also work well with you having that 'third' state.

  • Like 4
Link to post
Share on other sites

Hey Carl!

 

Thanks for the CodePen examples!

 

I did not know, that I can use "{timelineMaxInstance}.tweenTo(...)", nor  that "method" to switch the "progress" between 0 and 1 along with the "ease" too correspondingly.

 

THAT IS A GREAT FEATURE, AMAZING CAPABILITY!

My jaw dropped on this feature of TimelineMax.

 

I will definitely try it and get back with the results.

  • Like 1
Link to post
Share on other sites

Hi Jamie!

 

Thank you for the CodePen efforts!

 

I am seriously considering on using the CSSRulePlugin as you showed me.

I will try out Carl's solutions and yours too.

 

I will get back here with my experiences soon.

Link to post
Share on other sites

Dear Supporters!

 

After a couple of days of development, I have finally arrived to 2 main conclusions regarding which techniques should I use.

 

I am still learning the Tween/Timeline/Lite/Max frameworks, but my current project will be done with:

 

1.: As these frameworks are highly optimized, even 50-125 tweens at once did not chew significant resources from the test machines, so most of the time I will stick with TweenLite.to(...).

2.: In other, less often cases I will mix TweenLite.to(...) with the suggested CSSRulePlugin and for "fixed tweens" (basically motions without prop changing during tweens) TimelineLite will be used.

 

Thank all of you for those quick, helpful and desvriptive answers!

I will donate to GSAP after my current project.

  • Like 4
Link to post
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.

×