Jump to content
GreenSock

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

onReverseComplete of sub-timeline not working

Recommended Posts

If you click on Toggle, it will play, and on second click, it will reverse the timeline, but the "onReverseComplete" callback of the sub-timeline is not triggered, which should add yellow background to the button

See the Pen qBdyeye by anteksiler (@anteksiler) on CodePen

  • Like 1
Link to post
Share on other sites

From what I can tell, this is expected behavior. onReverseComplete is only supposed to fire when that particular animation is REVERSED. Like...literally it must be in reversed state, like call reverse() or set reversed(true) on it. In your scenario, the animation is still in its forward state but the PARENT animation is reversed. 

 

If we altered the behavior in GSAP so that onReverseComplete was always called whenever the playhead hit the start from the opposite direction, that'd mean it'd be triggered if it played already and then you set the time() or progress() to 0 or if you moved the playhead of any ancestor animation back past the start of where this animation is. See what I mean? You could certainly argue that it's more intuitive for onReverseComplete to fire regardless of the animation's reversed state, but there are scenarios where that'd certainly seem less intuitive. 

 

Anyway, if your goal is to call a function whenever the playhead rewinds all the way to the start (regardless of "reversed" state), I have a few solutions for you...

 

Timeline solution

Here's a helper function that you can simply feed a timeline and the callback, and it'll handle it all for you by adding a 0.0001-second tween with an onUpdate that tracks the playhead direction:

function onRewindComplete(timeline, callback) {
  var time = 0;
  timeline.to(callback, {
    duration: 0.0001,
    onUpdate: function() {
      var t = this.time();
      (!t && time) && callback.call(timeline);
      time = t;
    }
  }, 0);
}

Usage: 

var tl = gsap.timeline();
tl.to(...); // add your stuff
  ...
onRewindComplete(tl, yourFunction);

 

Tween solution:

Here's a simple plugin that, once registered, allows you to add an onRewindComplete to any tween!

gsap.registerPlugin({
  name: "onRewindComplete",
  rawVars: true,
  init(target, callback, tween, index, targets) {
    this.c = (index === targets.length - 1) ? callback : 0; // only use the callback for the last target (so that we don't fire it for each and every target)
    this.s = tween.vars.callbackScope || tween; // scope
    this.t = tween;
    this.l = 0; // last time recorded.
  },
  render(ratio, data) {
    if (data.c) {
      let t = data.t.time();
      (!t && data.l) && data.c.call(data.s);
      data.l = t;
    }
  }
});

Usage:

gsap.to(".class", {onRewindComplete: yourFunction, x: 100});

 

Does that help? 

  • Like 1
Link to post
Share on other sites

Hi @GreenSock, thanks for the help. I did not quite understand the onRewindComplete function solution.

 

Do you mind editing the codepen? Do I add the onRewindComplete function to the parent timeline's onReverseComplete, and use the child timeline's callback? 

 

 

 

Link to post
Share on other sites
2 hours ago, GreenSock said:

function onRewindComplete(timeline, callback) {
  var time = 0;
  timeline.to(callback, {
    duration: 0.0001,
    onUpdate: function() {
      var t = this.time();
      (!t && time) && callback.call(timeline);
      time = t;
    }
  }, 0);
}

 

 

I am confused here as you using "callback" as a timeline selector, and the using "callback" as a timeline "callback.call(...)".

Link to post
Share on other sites
10 hours ago, anteksiler said:

Do you mind editing the codepen?

Absolutely. Here's a fork with the timeline-based solution in place: 

See the Pen 8e41e9d89bf1ec5bb621d296269681a5?editors=0010 by GreenSock (@GreenSock) on CodePen

 

You can apply an onRewindComplete() to any timeline (the nested one, the parent, whatever)

 

9 hours ago, anteksiler said:

I am confused here as you using "callback" as a timeline selector, and the using "callback" as a timeline "callback.call(...)".

Nope, I'm using the callback as the target of the "tween". Remember, you don't need to use selector text as the target - you can literally pass in any object as the target. A DOM element reference, a generic object, a function...almost anything. The reason I used the callback here is that it lets you kill any calls to that function later with gsap.killTweensOf(yourFunction). 

 

The reason I'm doing callback.call(timeline) is to set the scope of the function call. All functions have a ".call()" method that lets you define the scope. In other words, my goal was to make "this" refer to the timeline instance itself inside your function call (which is the default behavior for callbacks in GSAP). It just makes it easy to snag a reference to the timeline if you need it. If you don't care about that, callback.call(timeline) can be simplified to callback(). 

 

Does that clear things up? 

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

×