Jump to content
Search Community

Timeline ScrollTrigger invalidateOnRefresh

jfudman test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

I am programmatically moving an element inside of a pinned trigger to the center of the viewport via scrolltrigger with timeline animations.


It works, but values do not properly update on resize. I'm setting values with anonymous functions, i even read through this post... but I'm still stuck. What's worse is `invalidateOnRefresh` is breaking some values (see codepen).

Finally, I have to refresh the tl.scrollTrigger in order to use the tl.scrollTrigger.start value. Does anyone know why?


See the Pen poQEWKZ by jfudman (@jfudman) on CodePen

Link to comment
Share on other sites



I think the issue could be in the calculations you're doing for the Y value. Which raises the question of why you're doing that and if it's completely necessary. If the element is an ancestor of the element being pinned: Why animate it's Y value anyways? and Why you need the starting point of the ScrollTrigger instance which is based on the position of the top ancestor that is getting pinned? Unless I'm missing something it doesn't really make much sense to me.


Also keep in mind that the values of getBoundingClientRect returns are based on the position of the element in the viewport, so if the element is not below or above the viewport this will return values based on that:

const elRect = el.getBoundingClientRect();



Maybe using offsetTop and offsetLeft of the parent element plus margins/paddings.


IMHO you're overcomplicating this a bit, I forked your codepen and removed the Y value in your timelines and commented out a few things and I'm getting the same results with invalidateOnRefresh: true:

See the Pen OJaRzGy by GreenSock (@GreenSock) on CodePen


Finally another option would be to use the Flip Plugin:



Maybe this example that combines ScrollTrigger and Flip with scrub can help:

See the Pen bGxOjeP by GreenSock (@GreenSock) on CodePen


Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Thanks for the reply @Rodrigo. Here is a fork of your fork with some explanations as to why i need to determine the y value.
Long story short: there will sometimes be content above or below the box.


I use the Scrolltigger.start value in the calculation because the pinned element's start value will sometimes be different

See the Pen ZEmpmRY by jfudman (@jfudman) on CodePen

Link to comment
Share on other sites

Yeah the plugin looks awesome and I'll likely end up using it. Thank y'all for for the suggestion @Rodrigo and @Cassie!

Although I still would like to know why my code doesn't work on resize because it theoretically should lol

  • Like 1
Link to comment
Share on other sites

  • Solution
On 6/21/2023 at 4:13 PM, jfudman said:

Finally, I have to refresh the tl.scrollTrigger in order to use the tl.scrollTrigger.start value. Does anyone know why?

That's because timelines with a ScrollTrigger attached must WAIT to refresh until the timeline actually gets populated, thus it waits one tick (it can't know when you're done adding stuff - that's why it waits 1 tick). Otherwise, the timeline would have zero duration which makes scrubbing pretty weird, plus snapping to "labelsDirectional" couldn't work (no labels), etc. Individual tweens can refresh right away upon creation, but timelines have to wait. Of course you can manually call the .refresh() method when you're done populating it if you don't want it to wait a tick. 


The reason that invalidateOnRefresh: true was breaking things for you was due to the fact that you weren't calculating them in a way that includes the scroll position. When you DON'T have invalidateOnRefresh, it was just doing the calculations initially, when the page wasn't scrolled at all. But if you do invalidateOnRefresh, it invalidates on refresh and then the next time that animation renders, THAT Is when it calls the function-based values. So in your demo, that would be when the user scrolled enough to hit the ScrollTrigger start position, thus your getBoundingClientRect() wouldn't reflect y values inclusive of the scroll position. 


// bad
y: elRect.height / 2 + elRect.y

// good
y: elRect.height / 2 + elRect.y + window.pageYOffset

Is this what you were looking for?: 

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

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