Jump to content
Search Community

Timeline trigger all callbacks with specific timing when play

ngocducdim test
Moderator Tag

Recommended Posts

Hi, in the codepen I have a timeline with 10s and seek at 3s at initialize, then I click 1.Register call back button to add callback at 1s, 3s & 5s, and then I click 2. Start timeline to play the timeline, in the console I expected the output:
- callback at 3s
- callback at 5s


But the actual output:
- callback at 1s <- this one unexpected
- callback at 3s
- callback at 5s


My question is I seek the timeline at 3s at initialize, how come when play timeline the callback at 1s get called? is there any fix or do I misunderstanding how timeline works? Thanks

See the Pen wvQBwra by Duc-Dim (@Duc-Dim) on CodePen

Link to comment
Share on other sites

For me the fix is seek the timing forward and backware before play the timeline:
tl.seek(3 + 0.001);
tl.seek(3);

This will remove the callback at 1s, not sure why, maybe gsap timeline need recording the state again after add callback by using seek? if so do we have any method to do this instead using this 'hack', like timeline.recordingState()

See the Pen abQzoRa?editors=1111 by Duc-Dim (@Duc-Dim) on CodePen

Link to comment
Share on other sites

Hi,

 

By default some methods of Tweens/Timelines instances, when pass a position parameter (time/label) suppress events and callbacks by default when moving the instance's playhead around. So when you call play() after the seek event is expected to have those callbacks fired because you create the timeline, then you move the playhead with the seek() method to 3 seconds, then you add the callbacks, after moving the timeline's playhead, and finally you just change the paused state with play(), so at that point the instance will call all the callbacks as you're seeing right now. That's why adding those extra seek() methods before calling play() is working, because you're moving the timeline's playhead around before toggling it's paused state and after adding those callbacks. Those callbacks will be ignored when you toggle the paused state.

 

This also will work the way you intend:

tl.addPause(10);
tl.seek(3);

document.getElementById("#btn-register-cb").addEventListener("click", () => {
  tl.add(() => {
    console.log("callback at 1s");
  }, 1);

  tl.add(() => {
    console.log("callback at 3s");
  }, 3);

  tl.add(() => {
    console.log("callback at 5s");
  }, 5);
  
  tl.seek(2.9999);
});

document.getElementById("#btn-start-timeline").addEventListener("click", () => {
  tl.play(3);
});

This also will work in the way you're expecting:

tl.addPause(10);

document.getElementById("#btn-register-cb").addEventListener("click", () => {
  tl.add(() => {
    console.log("callback at 1s");
  }, 1);

  tl.add(() => {
    console.log("callback at 3s");
  }, 3);

  tl.add(() => {
    console.log("callback at 5s");
  }, 5);
  
  tl.seek(3);
});

document.getElementById("#btn-start-timeline").addEventListener("click", () => {
  tl.play();
});

Because you're moving the playhead after adding those callbacks, not before, so those callbacks will be ignored.

 

Hopefully this makes sense and helps. Let us know if you have more questions.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Hi thanks for the reply, I was though gsap timeline is like timeline in some video editor like Adobe Premier or Sony Vegas, I'm building the project that require timeline like video editor  and I use the gsap timeline for this, my project add/edit/delete tween & callback in the timeline on-the-fly(backward or forward of the playhead or at the playhead) and I think after every action like this need move around the playhead(using seek) before play the timeline right? If so, why when I seek timeline again at the current timing it won't reflect, let said current playhead timing is 3s, this one works:

document.getElementById("#btn-start-timeline").addEventListener("click", () => {
  console.log("Play with seek");
  tl.seek(3 + 0.001);
  tl.seek(3);
  tl.play();
});


but this don't:

document.getElementById("#btn-start-timeline").addEventListener("click", () => {
  console.log("Play with seek");
  tl.seek(3);
  tl.play();
});

Link to comment
Share on other sites

  • 2 weeks later...

Short answer

I've made an improvement in the next release that should help in this edge case: 

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

 

Explanation

When you add() an animation to a timeline (and a callback is just a zero-duration tween with an onComplete/onReverseComplete), it would only force a silent render (suppressEvents: true) if the animation had already rendered at least once, like if it was previously in another timeline. In this case it didn't force that render. So the next time the timeline renders, it says "hey, the playhead is past that animation, so we better render that to make sure it's in the proper state!" It's a tricky scenario, as an argument could be made that it should render immediately when inserted and a valid argument could be made against that as well. It's another one of those logic challenges that plague zero-duration children on timelines that are inherently time/duration-oriented. 

 

So the next release just does a silent render in this scenario, thus it doesn't trigger the actual callback when it updates the playhead initially. 

 

If you didn't follow any of that, it's fine. But hopefully it helps understand the mechanics a bit. 

 

If you seek(3) when the playhead is ALREADY at 3, it won't render anything because the playhead didn't move at all. GSAP has a lot of performance optimizations - it skips rendering things needlessly. So that unrendered callback was still unrendered at that point. So when you actually move the playhead, it'd trigger the render. But if you seek(3.001) first, that does move the playhead and since seek() suppresses events by default, it won't fire that callback but it is rendered, thus the callback tween's playhead is updated to be at its "end". Then you seek(3) which, again, suppresses events. So after that, the playhead starts moving normally and it won't fire that callback since it's behind the playhead. 

 

I hope that clears things up. Let us know if there's anything else you need.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

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