Jump to content
Search Community

Scrolltrigger Timeline progress incorrect on page load. Timeline onUpdate callback not firing after Scrolltrigger.refresh()

cbravo test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Good morning!

 

I am having two issues that I am hoping I can get some eyes on. In my codepen I have an animated section hooked up to scrollTrigger. I have 2 timelines one of them is nested in the other. In my actual use case the nested timeline has an important onUpdate function which draws my html5 canvas.  If you scroll below the animated section and refresh the page (scroll position should be restored below the animation area)  you will see that the progress on the timeline is reported as 0 before ScrollTrigger.refresh() and 1 afterwards AND my onUpdate function does not fire which means my canvas animation is not in the right state.


1. Why does ScrollTrigger.refresh() need to be called for progress to change? The animation looks as if progress is 1 but calling the progress getter returns 0. 

2. If I call ScrollTrigger.refresh() and my timeline progress changes  from 0 to 1 why doesn't my onUpdate fire? is there an alternative? 

3. if attaching a ScrollTrigger moves the playhead should onUpdate fire? 

 

Note: You will need to fork this codepen and view directly and in debug mode so you can view without iframe and have the page load in a scrolled down position with scroll restoration to see the issues I am talking about.

 

My actual use case is a bit more complicated than the pen because I am using react and each of the timelines (timeline1 and nestedTimeline) are located in different components like this:

const parentTimeline = gsap.timeline();
const scrollTrigger = scrollTrigger.create({...})
// Here is where I would need to call the canvasDraw function or the onUpdate from the timeline inside <NestedComponent /> 
//so I cannot simply call those functions after setting up scrollTrigger as those are defined elsewhere.
//Currently my workaround is to do something like this on the parent component after I create and refresh the ScrollTrigger:
const nestedTimelime = parentTimeline.getChildren(false, false, true)[0]
nestedTimelime.vars.onUpdate(...nestedTimelime.vars.onUpdateParams)
                                            
<ParentComponent>
  // passing the parent timeline down so I can add more animations  
  // NestedComponent contains the canvas and canvasDraw function I call on timeline update
  <NestedComponent parentTimeline={parentTimeline} /> 
</ParentComponent>


                                            

 

 

 

See the Pen mdrdoKd by bravoclicks (@bravoclicks) on CodePen

Link to comment
Share on other sites

  • Solution

Good questions. Let me explain...

  1. Timelines are a unique beast when it comes to ScrollTrigger because unlike tweens, you could keep adding things to timelines, so ScrollTrigger can't know when you're "done", thus it waits for 1 tick to do its initial refresh on a ScrollTrigger attached to a timeline. In other words, your code could look like this: 
    let tl = gsap.timeline({ scrollTrigger:{...} });
    // if it refreshes NOW, it's totally empty!
    tl.to(...)
      .to(...); // now we've populated it, but ScrollTrigger has no way of knowing you're done, thus it waits for 1 tick. 

    Of course you can signal to ScrollTrigger that you're done and ready to refresh by calling ScrollTrigger.refresh(), as you discovered. 

  2. When ScrollTrigger does its refresh work internally, it sometimes needs to move the playhead on the attached timeline/tween and we suppressEvents for those playhead moves so that it doesn't cause other issues for users. For example, there could be timeline call()'s, eventCallbacks, etc. that occur at specific times on those timelines, and temporarily moving the playhead to a new position to ensure things are read correctly could cause those to fire even though it's often a temporary thing. One example is that ScrollTrigger has to sense whether or not your animation is affecting the translation of the pinned element (if there is one) so that it can offset that properly, so it must test that by "rewinding" the tween and seeing if the translation changes, etc. So if you need to run some canvas rendering code you could either add a "refresh" event listener to trigger that or an onUpdate on the ScrollTrigger itself. 

Does that clear things up? 

  • Like 2
Link to comment
Share on other sites

Aw, you made my day @cbravo. That's great to hear. 

 

Sometimes it's tough to stay on top of things around here and these forums eat up a huge chunk of our resources, so it's very nice to hear when someone recognizes the efforts. Special thanks to @ZachSaucier, @akapowl, @mikel, @PointC @Rodrigo and all the other moderators for their efforts in the neighborhood. 

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