Jump to content
GreenSock

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

Grouping Transitory Tweens

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

First off, I'm a big fan of GSAP. Great work!

 

I have a question about "grouping" transitory tweens.

 

I'm developing a Google Maps-like experience using multiple canvas elements. I'm using TweenMax to periodically change the state of sprites on the screen.

 

My abstractions are similar to those of KineticJS: a "Map" class owns "Layer" classes (which have associated canvas elements) which own "Sprite" classes.

 

The top-level Map class uses the TweenMax ticker, triggering redraws of child Layers when necessary. One condition for triggering redraws is the presence of active tweens.
 

var thereAreActiveTweens = TweenMax.getAllTweens().length > 0;

if (thereAreActiveTweens) {
  // redraw all layers
} else if ( /** other conditions **/) ...

This works, but it's inefficient: I could end up redrawing multiple canvases for the sake of one active tween on one layer.

 

I'm looking for functionality like this:
 

Map("A new frame! Do my layers need to redraw?")
  Layer 1 ("I have no active tweens! Skip  me.") [return;]
  Layer 2 ("I have active tweens! Redrawing...") [do redraw; return;]
  etc.

In other words, I'd like to arbitrarily associate tweens with different "groups" under a single instance of TweenMax.  Something in the spirit of

TweenMax.getActiveTweensFor("backgroundLayer");

I think what I want is very close to a Timeline* instance for every layer, except that I'm only interested in active tweens. (And, in fact, I want old tweens garbage collected!)

 

Any guidance?

 

Cheers!

Link to post
Share on other sites

Hi and glad to hear you are having such success with GSAP.

 

I may not know exactly how to assist you in this matter but I'm confident there are enough hooks in the API to make your layer-updating process more efficient.

 

Using getAllTweens() gives you an array of all the tweens (as you know)

 

You could then loop through each tween and check its isActive() state

 

If you find an active tween you can then get its target (I'm thinking this is one of your Sprites).

 

As long as each Sprite knows which Layer it belongs to (perhaps each Sprite has a parentLayer property or something) you can then mark each layer eligible for updating.

 

---

 

Not sure if you are familiar with our KineticPlugin, but when you do a Kinetic tween it automatically finds the Layer of the target and makes sure that only one Layer.draw() call is called once per tick (even if multiple objects are being animated on the same layer).

 

For something as robust as this Map app you are building you might be interested in exploring creating your own custom plugin.

  • Like 2
Link to post
Share on other sites

Thanks, Carl -- that's helpful.

 

I expect that a quick loop through the active tweens (to build a "layer redraw list") is the easiest approach at this juncture. (Though I've bookmarked the KineticJS plug-in for future reference!)

Link to post
Share on other sites

Yep, Carl's custom plugin idea is a good one. Here are a couple other things to throw out there that might help:

 

--

 

Ideally, your framework would manage flagging things as "dirty" (in need of redrawing), so you could use functions for getters/setters and inside of those, you set your flag. Kinda like:

yourObject.prototype.x = function(value) {
    if (!arguments.length) {
        return this._x;
    } else if (this._x !== value) {
        this._x = value;
        this.layer.isDirty = true;
    }
}

Then, when you tween yourObject.x, every time it gets set to a new value, its layer gets flagged as dirty. Then you can loop through your layers once per tick and redraw only the ones that are dirty (don't forget to reset the isDirty flag to true after you redraw them). 

 

--

 

Or you could use an onUpdate for each tween that sets some flag that tells you that a particular layer is "dirty" (in need of redrawing). Kinda like:

TweenLite.to(obj, 1, {x:100, onUpdate:function() { obj.layer.isDirty = true }});
function onTick() {
    //loop through your layers and redraw only the ones that are dirty, and remember to set isDirty back to false
}

--

 

Here's a nifty little trick: you can create a TimelineLite for each layer, and then add your tweens accordingly so that they're all separated by layer. That way, you could add an onUpdate to the timeline instance that'll handle the redrawing (remember, the onUpdate for that layer only gets called once after all the tweens inside of it have rendered on each tick). In fact, we can add a couple of methods to make this even easier for you...

function createLayerTimeline(layer) {
    return new TimelineLite({smoothChildTiming:true, autoRemoveChildren:true, onUpdate:function() {
        layer.redraw();
    }});
}

function tween(target, duration, vars) {
    if (!target.layer.timeline) { //if a timeline hasn't been created yet for this layer, create one...
        target.layer.timeline = createLayerTimeline(target.layer);
    }
    var tl = target.layer.timeline;
    return tl.to(target, duration, vars, tl.time()); //add the tween at wherever the playhead currently is.
}

//now all you need to do is call tween(...) instead of TweenMax.to(...)
tween(obj, 1, {x:100});

So all your tweening code would get very concise and they'd automatically get routed to the appropriate layer timeline that has a redraw call wired to its onUpdate. Booyah! :) Of course if you're building other timelines of your own (unrelated to layering), this technique wouldn't be ideal. Hopefully you see the concepts, though, and can apply them to your situation appropriately. 

 

Anyway, like I said, there are a lot of options for this. I probably wouldn't go the route of getting all tweens on every tick and looping through them - that just seems a tad inefficient. 

  • Like 2
Link to post
Share on other sites

You guys rock. Lightbulbs going off over here!  :-P

 

I didn't appreciate that TweenMax would sense and use getter/setter functions. That solves a ton.

 

In light of that, I'll probably hold off on layer-specific timelines for the moment, but I'm inspired by the concision.

 

I'll make a point to swap out Scroller for the ThrowPropsPlugin soon, if only so I have an excuse to join the Club.

 

Thanks again.

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

×