Jump to content
GreenSock

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

Detect reverse start event

Recommended Posts

Tweens offers many events to detect actions: onStart, onComplete, onReverseComplete... But there's no onReverseStart

 

How would you detect the animation started in reverse?

Link to comment
Share on other sites

Hi,

 

The thing is that reverse is usually called inside an event handler, so you can add your code in that.

 

If you're using a repeat value with the yoyo:true in your config object, you can use onRepeat, like that when the tween goes backwards it'll trigger that callback:

TweenMax.to(element, time, {vars, repeat:1, yoyo:true, onRepeat:yourCallback});

function yourCallback()
{
  // your code here
}

As always it'll help to see a very simple example of the issue you're having:

 

http://forums.greensock.com/topic/9002-read-this-first-how-to-create-a-codepen-demo/

 

Rodrigo

  • Like 2
Link to comment
Share on other sites

Yep, Rodrigo is exactly right - we didn't add an onReverseStart callback because it seemed quite unnecessary since you'd have to call reverse() anyway, so you can just call whatever method you want at that point. We didn't see the need to bloat the API or codebase for that (we really try to avoid adding things to the core when they're relatively easy to accomplish outside the core). Maybe you have a particular use case we haven't thought about though - feel free to enlighten us :)

Link to comment
Share on other sites

I sure can explain. I'm programming an interactive websites with a lot of animations.

 

To do so, I have a global TimelineMax instance that is binded to the user progression through the website. This progression can be the scroll, button clicks, etc - the idea is that it is reusable and can be applied to differents kind of user interactions.

 

If the progression is binded to the scroll, then the user can go forward and backward. So I never actually explicitly call play or reverse. I'm usually calling `timeline.tweenTo( x )` from a navigation controller.

 

This navigation controller have no knowledge of the timeline position or progression, it only knows that when it is called, it need to advance (or reverse) by X frame.

 

Then, each module is registered on the global timeline and is managing its inner state by itself. As so, this module doesn't know when reverse is called because it is completly decoupled.

 

Does it make sense?

Link to comment
Share on other sites

I see what you mean. The behavior you're describing wouldn't technically be an "onReverseStart" because in GSAP, "reverse" means something very particular - it's the orientation of the tween/timeline in the context of its parent timeline. So, for example, imagine the parent timeline's playhead moving forward across where this particular tween resides - if the tween is reversed, it will play in the opposite direction as the parent playhead moves forward. And remember that you can also reverse() that parent timeline, so a reversed tween inside a reversed timeline will appear to play forward even though technically its "reversed" value is true.

 

The behavior you're describing is attempting to discern when the local direction of the playhead changes, and you can accomplish that with an onUpdate like this:

var tl = new TimelineLite({onUpdate:checkDirection});
var lastTime = 0;
var forward = true;
tl.to(...); //add your tweens or whatever

function checkDirection() {
    var newTime = tl.time();
    if ((forward && newTime < lastTime) || (!forward && newTime > lastTime)) {
        forward = !forward;
        console.log("changed direction");
        if (!forward) {
            onReverseStart();
        }
    }
    lastTime = newTime;
}

function onReverseStart() {
    //do stuff
}

The concept is just to check the time and compare it to the last recorded time - that tells you the local direction of the playhead. When you sense a change, you can call your function or run whatever logic you want. 

 

Does that help?

  • Like 3
Link to comment
Share on other sites

What you explained about parent timeline make sense. That is the behavior I'd expect.

 

I just believe an event "onReverseStart" - so when the last frame is played going backward - would simplify such boilerplate.

 

In the current case I made a workaround too. The solution you show would work, but it is a lot of boilerplate code for something that could (IMO) be abstracted away in the core system.

Link to comment
Share on other sites

Yes indeed, but there are a few challenges related to shoving this logic into the core:

  1. We are EXTREMELY concerned with performance. That's one of the main reasons GSAP has gained such popularity. Adding the feature you're requesting would require new logic to run on EVERY render of EVERY tween/timeline ("what's the direction? Did it change? Is there a callback?"). I loathe the idea of adding extra logic to the render loops that'd slow every tween and every timeline down (albeit slightly) across the board just to empower a feature that a very, very small subset of users ever tap into. In other words, would we want to impose even a 2% performance tax on everyone so that 0.001% of the users can avoid the boilerplate code I shared above?
  2. What would you name it (since onReverseStart is not accurate, as explained above)? onChangePlayheadDirection? If so, folks would probably ask for a Boolean property that indicates direction too (so we're adding at least 2 things to the API)
  3. There are also some minor costs in terms of bloating the API and the file size. Again, I'm hesitant to impose that small tax on everyone so that such a small portion of users can avoid the boilerplate code above (which really isn't very much in my opinion)

See what I mean? I completely understand why you'd want this baked into the core. Makes perfect sense in your particular scenario, but when you take a step back and look at it from the perspective of the platform as a whole and what costs it would impose (performance, API, and kb-wise), hopefully you see why it's not such an easy thing. 

  • Like 5
Link to comment
Share on other sites

  • 8 months later...

Ok, so what you're saying is true: adding new (cool) features brings a minus in performance. But what about the TweenLite and TweenMax separation? Couldn't this be applied in this case too?  Maybe this feature can be added only to TweenMax, so that users that do not want this can use TweenLite.

I'm having a simillar issue where I needed the onReverseStart event and to be honest, I find it very weird (from a programmer's point of view) to expose a complete event without exposing it's matching start event. If the feature isn't fully implemented or reliable, why exposing it in the first place?

  • Like 2
Link to comment
Share on other sites

Hi Gion and welcome to the GreenSock forums.

 

You raise an excellent point, but a follow up question could be, how you're planning in reversing your timeline?, mouse/touch-event, key-event, scroll-event?, are you going to use play() and reverse() methods, tweenTo() method or any other?.

 

As it was mentioned earlier in the thread, once you invoke an event handler you can execute any method you like. You can also create a private function, call that one and pass whatever parameter you'll need in order to get the behaviour you want.

 

Keep in mind also, that onStart triggers once the instance's playhead is at 0 seconds (progress = 0), you could check the progress and if it is 1 and the reverse() method is called you can trigger a specific code. You can also create a boolean and change that using the onComplete callback in order to change it, and whenever the reverse() method is called you can check that boolean to see if the instance has been completed or not and depending on that execute some specific code. If you could share a little bit about your app, it would help to better understand what you need.

 

Finally the engine comes with a plugin template, so you could give it a try to implement your own solution for your particular app.

 

Rodrigo.

  • Like 1
Link to comment
Share on other sites

  • 6 months later...
  • 1 year later...

reverse() isn't called in yoyo:true case. so, if we have to stop the tween with yoyo, we need onReverse event. the code bloat happens at user side. not good.

Link to comment
Share on other sites

Hi,
 
In the case of TweenMax/TimelineMax instances, yoyo does not acts as reverse(), the yoyo feature basically alters the direction of the repeat feature.
 
From the docs:

 

yoyo : Boolean - If true, every other repeat cycle will run in the opposite direction so that the tween appears to go back and forth (forward then backward). This has no affect on the "reversed" property though. So if repeat is 2 and yoyo isfalse, it will look like: start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end.

 

What I can suggest is to detect the direction of the instance or the progress value inside a function and use that on the onRepeat callback:

// at startup the direction of an instance is forward
// THIS IS NOT RELATED TO THE REVERSED STATUS
var forward = true;

function cBack(){
  forward = !forward;
  if( !forward) {
    // the instance is repeating from the end to the start
  } else {
    // the instance is repeating from the start to the end
  }
}

TweenMax.to(el, 1, {vars, onRepeat:cBack});

Codepen sample:

 

See the Pen WRvVqN?editors=0010 by rhernando (@rhernando) on CodePen

 

Hopefully this makes things a bit more clear.

 

Happy Tweening!!

  • Like 3
Link to comment
Share on other sites

Hello Rodrigo,

 

thanks for your workaround, but i really like non-imperative-declarative style..

 

i have a button with hover-gsap-TimelineLite tween-effect witch is invoked inside mouseover -> play(), and reversed inside mouseout -> reverse().

when this effect completes, onComplete creates a yoyo-tween which applies more animation to this button changing from the same value {scale} as in first effect.

now, the need is to reverse yoyo-tween to the complete state of first effect and then, to continue reversing first TimelineLite.

 

maybe i'm going the wrong way doing this [e1->e2, e1<-e2] animation.

anyway, thanks for reading.

Link to comment
Share on other sites

HI Determin1st,

 

Welcome to the forums.

Its always best to provide a demo for questions like these as explained here: http://greensock.com/forums/topic/9002-read-this-first-how-to-create-a-codepen-demo/

 

From what I gather, it sounds like you may be want to explore tweening the progress() of your tween or timeline to 0 like

TweenLite.to(myRepeatingAnimation, 2, {progress:0});

Please see the https://medium.com/net-magazine/7-hidden-gems-of-the-greensock-animation-platform-4fb71389f6ca#.if4n6lvs1 section Tween a Tween for some more info.

  • Like 4
Link to comment
Share on other sites

  • 1 year later...
On 12/03/2014 at 3:32 PM, GreenSock said:

I see what you mean. The behavior you're describing wouldn't technically be an "onReverseStart" because in GSAP, "reverse" means something very particular - it's the orientation of the tween/timeline in the context of its parent timeline. So, for example, imagine the parent timeline's playhead moving forward across where this particular tween resides - if the tween is reversed, it will play in the opposite direction as the parent playhead moves forward. And remember that you can also reverse() that parent timeline, so a reversed tween inside a reversed timeline will appear to play forward even though technically its "reversed" value is true.

 

The behavior you're describing is attempting to discern when the local direction of the playhead changes, and you can accomplish that with an onUpdate like this:


var tl = new TimelineLite({onUpdate:checkDirection});
var lastTime = 0;
var forward = true;
tl.to(...); //add your tweens or whatever

function checkDirection() {
    var newTime = tl.time();
    if ((forward && newTime < lastTime) || (!forward && newTime > lastTime)) {
        forward = !forward;
        console.log("changed direction");
        if (!forward) {
            onReverseStart();
        }
    }
    lastTime = newTime;
}

function onReverseStart() {
    //do stuff
}

The concept is just to check the time and compare it to the last recorded time - that tells you the local direction of the playhead. When you sense a change, you can call your function or run whatever logic you want. 

 

Does that help?

 

 

 

 

Using this example, how do I run the onReverseStart function every time I reverse, and not just when I change the timeline's direction?

 

See the Pen YaVZOK by Noturnoo (@Noturnoo) on CodePen

 

Link to comment
Share on other sites

If I understand your question correctly, you'd just call your function right before (or after) you call reverse(). 

Link to comment
Share on other sites

When reverse each Tween on TimelineMax

Link to comment
Share on other sites

I didn't quite understand your question.

 

Are you asking how to call a function whenever the playhead starts playing each and every tween, but only in the reverse direction? If so, why would that be helpful? Sometimes it's good to take a step back and ask the broader question about what the overall goal is because often there's more of an engineering issue at play that perhaps we could address. 

 

(PS: I think it'd probably be best to create a new thread in situations like this rather than resurrecting a 4-year old one :))

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