Jump to content
Search Community

Timeline with delayed calls not playing smoothly

zanntux test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hello,

I have a problem where I use the gsap.timeline for show/hide logic but it's not really smooth for the first 1-5 seconds.
 

I expect this code to show/hide red and green box without flickering. But there is 1 frame where none of the boxes are shown.
There is a small blue box under them for easier detection when the timeline is misbehaving.
(I know I can achieve this with normal tweens, but the logic on my side is more complex and I'm using the timeline like a queue)

Is this normal and why if it is or am I doing something wrong?

I have isolated the problem with a simple codepen.

See the Pen WNWMMaq by martin-nikolov (@martin-nikolov) on CodePen

Link to comment
Share on other sites

  • zanntux changed the title to Timeline with delayed calls not playing smoothly

Hi @zanntux and welcome to the GSAP Forums!

 

I'm not 100% sure, but this could be related to the fact that you call the init method, where you add the display none style to the elements. Then the GSAP instances are created and then a single tick runs before the GSAP timeline starts which could lead to this behaviour. Although a single GSAP tick is just 16 milliseconds, so IDK if that should be enough to show the blue element behind and to be noticeable.

 

We'll look into this and let you know.

Happy Tweening!

  • Thanks 1
Link to comment
Share on other sites

  • Solution

It's not really a bug. It's just a fundamental logic problem in the way you're setting things up. Let me explain...

 

In order for a callback to fire, the playhead must cross that spot on its parent timeline, or land directly on top of it. So it's based on the playhead moving (its new position). The timeline doesn't render for the first time until the next tick (it'd be silly to render right away by default because the playhead hasn't moved anywhere yet, so it'd be a waste of CPU cycles). That's why the very first one didn't fire right away. 

 

The timeline's playhead updates on each "tick" which is typically about every 16.67ms but that really depends on the browser and how busy the CPU is, etc. 

 

Your timeline is 2 seconds long and has repeat: -1. So let's say it renders almost at the end, at like 1.9857 seconds, and then on the next tick, the totalTime renders at 2.013 which means that it went past the end and wrapped around to the beginning, and 0.013 seconds into the timeline (from the start). In that ONE tick, it'd fire that callback that's at the very end of the timeline AND since it looped back to the beginning and went a little bit past, it ALSO triggers the callback that's sitting at the very start. Great. 

BUT

 

What if the playhead happens to land EXACTLY at the end of the timeline (2 seconds precisely)? What do you think should happen? Obviously the callback at the end should fire, but should the callback that's sitting at the very START of the timeline also fire? I mean the end of the timeline and the start of the timeline are not the same technically, so it'd be weird if both fired. The playhead can't be at 2 seconds AND at 0 seconds. It wouldn't make a lot of sense to fire the callbacks from BOTH places on that ONE tick. 

 

See the problem? 

 

There are many ways to accomplish what I think you're trying to do there (alter visibility of things in a synchronized way), but I'd need to see what other requirements you have in order to offer the best recommendation. 

 

Thanks for the excellent minimal demo, by the way. 👍

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Thank you for the detailed explanation! I didn't know the timeline will render on the next tick. I've made a new pen where show/hide is called in single delayedCall to make sure everything is synchronized. I will try to adapt this solution.


I guess immediateRender is only for tweens?

 

See the Pen gOyeezJ by martin-nikolov (@martin-nikolov) on CodePen

Link to comment
Share on other sites

Sure, that's one way you could do it. A few suggestions:

  1. Don't use "new": 
    // BAD
    let childTl = new gsap.timeline({});
    
    // GOOD
    let childTl = gsap.timeline();

     

  2. This can be simplified: 
    // OLD
    elements.forEach((element, index, array) => {
      element.style.display = "none";
    });
    
    // NEW
    gsap.set(elements, {display: "none"});

     

  3. Since you're not using params anyway, just use .add() instead of .call():
    // OLD
    childTl.call(() => {
      //...
    }, [], 1);
    
    // NEW
    childTl.add(() => {
      //...
    }, 1);

     

I think you could greatly simplify the logic too: 

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

 

Like I said, there are many, many ways to tackle this. Hopefully this helps get you on your way to something that works well for you. 

  • Thanks 1
Link to comment
Share on other sites

Also, you can easily force a render of a tween or timeline, and even improve runtime performance slightly by forcing all the tweens inside a timeline to initialize and grab their start/end values like this: 

// jump to the end and immediately back to the start
animation.progress(1).progress(0);

 

  • Thanks 1
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.
×
×
  • Create New...