Jump to content
Search Community

Nested pins don't work

thei test
Moderator Tag

Recommended Posts

I built a site using ScrollSmoother, only to find out that ScrollSmoother doesn't really work as soon as anything complex is required. I have now run out of time to troubleshoot this any further as the site is already overdue, so I am trying to remove ScrollSmoother and revert back to a ScrollTrigger-only implementation.

 

Unfortunately, removing ScrollSmoother causes other weird problems, and it's doing my head in. I've used GSAP for years and never really had any issues. Am I losing my mind, or is something stupid going on here?

 

Note: This is a copy of the below with ScrollSmoother and its wrapper elements removed. Apologies for the inconsistent JavaScript, I started writing the codepen specifically and then just copied bits in from the site I'm working on with the quickest modifications to make it work in the codepen and haven't bothered making it neat, so it's all over the place.

 

*Edit: Apparently all Codepens just go to the bottom of the post no matter where I put their links*

Here is an example (copied from the above) without pinType: 'transform'and another example (also copied from the above) using pinType: 'transform' pins, along with the original ScrollSmoother example (which can't scroll to the bottom):

 

See the Pen KKRQYLq by nedmartin (@nedmartin) on CodePen

 

 

See the Pen zYjWdvZ by nedmartin (@nedmartin) on CodePen

See the Pen MWGVvga by nedmartin (@nedmartin) on CodePen

Link to comment
Share on other sites

Hi @thei. Very sorry to hear about your frustrations! 

 

Pinning is actually a very complex operation. Nested pinning (pinning an element that's inside another element that's also pinned in another ScrollTrigger) is not supported. You can still have normal (non-pinning) ScrollTriggers in an element that gets pinned with a separate ScrollTrigger (that's what the pinnedContainer property is for). 

 

As for pinType: "fixed" | "transform", it comes down to understanding how the browser handles position: fixed stuff. Ideally, pinned elements use position: fixed because browsers typically scroll on a whole separate thread that's NOT synchronized with screen repaints from the main JS thread (annoying). But even position: fixed pinning requires applying a transform at the very end of the pin. For example, if it's pinned for 500px, when it becomes unpinned, we must offset that element by 500px (down), otherwise it'll jump up as soon as it's unpinned. But when you have ANY transform applied to an element, the browser treats that as an entirely new stacking context (even if the transform is translate(0, 0)), meaning any descendant element that gets set to position: fixed will use that ancestor element as the reference point for positioning instead of the viewport! That's why it becomes necessary to use pinType: "transform". 

 

That's why you see that nested "pinned" element animating downwards - it's because you've got it set up with pinType: "transform", so it's moving it perfectly in sync with the scroll position to make it appear pinned but you've got the parent element pinned which messes it all up.  

 

I've been swamped with other stuff, so I haven't had a chance to do a deep-dive into your stuff but it sure looks to me like the root of all your problems is that you're trying to do nested pinning (which, again, isn't supported). 

 

Just out of curiosity, you said you built a whole site on ScrollSmoother but it doesn't look like you've got a Club GreenSock membership. Were you just using the trial to test it out locally or on CodePen? That's totally fine, of course. 

 

I wish I had a silver bullet for you. I really do feel badly that you sound so frustrated/disappointed. I've burned thousands of hours trying to build tools that make life easier for folks like you, so it's a bummer to hear when you have a negative experience. 

 

With enough elbow grease, I bet it's possible to accomplish the effect you're after. Perhaps the best next step would be to just focus on one single CodePen that's as simple as possible and shows what you're attempting (one piece at a time). 

Link to comment
Share on other sites

15 minutes ago, GreenSock said:

Hi @thei. Very sorry to hear about your frustrations! 

 

Pinning is actually a very complex operation. Nested pinning (pinning an element that's inside another element that's also pinned in another ScrollTrigger) is not supported. You can still have normal (non-pinning) ScrollTriggers in an element that gets pinned with a separate ScrollTrigger (that's what the pinnedContainer property is for). 

 

As for pinType: "fixed" | "transform", it comes down to understanding how the browser handles position: fixed stuff. Ideally, pinned elements use position: fixed because browsers typically scroll on a whole separate thread that's NOT synchronized with screen repaints from the main JS thread (annoying). But even position: fixed pinning requires applying a transform at the very end of the pin. For example, if it's pinned for 500px, when it becomes unpinned, we must offset that element by 500px (down), otherwise it'll jump up as soon as it's unpinned. But when you have ANY transform applied to an element, the browser treats that as an entirely new stacking context (even if the transform is translate(0, 0)), meaning any descendant element that gets set to position: fixed will use that ancestor element as the reference point for positioning instead of the viewport! That's why it becomes necessary to use pinType: "transform". 

 

That's why you see that nested "pinned" element animating downwards - it's because you've got it set up with pinType: "transform", so it's moving it perfectly in sync with the scroll position to make it appear pinned but you've got the parent element pinned which messes it all up.  

 

I've been swamped with other stuff, so I haven't had a chance to do a deep-dive into your stuff but it sure looks to me like the root of all your problems is that you're trying to do nested pinning (which, again, isn't supported). 

 

Just out of curiosity, you said you built a whole site on ScrollSmoother but it doesn't look like you've got a Club GreenSock membership. Were you just using the trial to test it out locally or on CodePen? That's totally fine, of course. 

 

I wish I had a silver bullet for you. I really do feel badly that you sound so frustrated/disappointed. I've burned thousands of hours trying to build tools that make life easier for folks like you, so it's a bummer to hear when you have a negative experience. 

 

With enough elbow grease, I bet it's possible to accomplish the effect you're after. Perhaps the best next step would be to just focus on one single CodePen that's as simple as possible and shows what you're attempting (one piece at a time). 

Ok that's a fairly serious issue then, if it's simply not supported 😢 - probably explains why everything is broken, even the seemingly simple stuff, when I've had very few problems in the past and GSAP has traditionally been really good and solved the silly issues for me so I don't need to worry about them.

 

I know about the differences between pinning via transforms or fixed - I'm using ScrollSmoother (ideally, some of my scrubbed animations look a bit rubbish without it) so can't use fixed - I mention it here only because it seemed to have different bugs depending which method was used (though I guess if it's not supported, they're not bugs)

 

The above codepens illustrate the simple effect I need - I don't think I can really make it much simpler. The first section of the site (the green "Top 100vh section" in my codepens) needs to slide up, revealing the rest of the site fixed ("pinned") underneath. This "rest of the site" area will then contain multiple animations, many of which contain pinned areas of their own (the usual stuff you'd expect on a site - ranging from simple pinned headings through to complex slide reveals, etc)

 

Because of ScrollSmoother I can't use any form of actual "position:fixed" to achieve this, hence pinning it with ScrollTrigger. However, you've now just told me that means I can't pin anything else inside this pinned section (which is effectively the "rest of the page", so is going to contain all kinds of animated and pinned things)

 

Any ideas? Or do I go back to the client and tell them what they want is not achievable and get them to come up with some alternative design that doesn't involve the first section sliding up over a pinned rest-of-the-site?

 

And yes I am using a paid version (and have been using GSAP for years), but I don't want to post from my work account.

Link to comment
Share on other sites

Quote

The first section of the site (the green "Top 100vh section" in my codepens) needs to slide up, revealing the rest of the site fixed ("pinned") underneath. This "rest of the site" area will then contain multiple animations, many of which contain pinned areas of their own (the usual stuff you'd expect on a site - ranging from simple pinned headings through to complex slide reveals, etc)


I reckon most things are achievable, it just takes a little bit of thinking outside of the box sometimes. ScrollTrigger/ScrollSmoother/FLIP (a lot of the new GSAP3 tools) are different beasts to old GSAP - we certainly feel this from a forum perspective too, the complexity of the animations people want help with nowadays is far beyond what it used to be. It's not that the tools are broken, it's that they're trying to make a huge number of complex problems possible and trying to work around a lot more edge cases, there are so many more intertwining things to be aware of. We do our best to add in solutions where we can but as with anything code-wise there will always be limitations to work within. It's mega frustrating when you hit those limitations though. I've been there. 

 

That being said. Solutions! Ok...
 

So here's one idea, pinning the entire content is going to cause issues, but you need the initial content visible and static underneath the 'slide'. So maybe pop that first section of content out into it's own container, make sure it's 100vh and then allow the rest of the content to catch up after it?

 

See the Pen rNvdvXp?editors=0110 by GreenSock (@GreenSock) on CodePen



Anther option is to create a big animation timeline that does what you want - then hook that up to the scroll. It doesn't work for everyone's use cases but it can sometimes make otherwise impossible things nice and simple.

  • Like 1
Link to comment
Share on other sites

17 hours ago, Cassie said:


I reckon most things are achievable, it just takes a little bit of thinking outside of the box sometimes. ScrollTrigger/ScrollSmoother/FLIP (a lot of the new GSAP3 tools) are different beasts to old GSAP - we certainly feel this from a forum perspective too, the complexity of the animations people want help with nowadays is far beyond what it used to be. It's not that the tools are broken, it's that they're trying to make a huge number of complex problems possible and trying to work around a lot more edge cases, there are so many more intertwining things to be aware of. We do our best to add in solutions where we can but as with anything code-wise there will always be limitations to work within. It's mega frustrating when you hit those limitations though. I've been there. 

 

That being said. Solutions! Ok...
 

So here's one idea, pinning the entire content is going to cause issues, but you need the initial content visible and static underneath the 'slide'. So maybe pop that first section of content out into it's own container, make sure it's 100vh and then allow the rest of the content to catch up after it?

 

 

 


Anther option is to create a big animation timeline that does what you want - then hook that up to the scroll. It doesn't work for everyone's use cases but it can sometimes make otherwise impossible things nice and simple.

 

Thanks for this. Unfortunately, I don't think this will work in this instance.

Why? A few reasons:

  1. The pinned part itself contains pinned parts - something I now know that GSAP can't do (they should really update the documentation to make this clearer - the way it mentions using pinnedContainermakes it sound like it does support this, so I wasted a huge amount of time trying to get it to work and blaming Vue.js dom manipulation and all kinds of complicated things rather than questioning whether it was something GSAP could actually do - random trivia: I got 99% of it to work by refreshing the next animation upon completion of the previous one - basically, once the previous pin has completed, rerunning all the calculations for the next one seems to make it work, but it was becoming an fragile house of cards and was not reliable)
  2. The pinned part is not full viewport height - content from other parts overlaps it, so even if I remove the pinned child parts from the pinned parent and get the client to accept that simply can't be done, I still can't do any full-height workarounds like you've done. Unfortunately, some of the content that overlaps the pinned part is itself a complex pinned animation, so I'd have to get the client to accept that none of the stuff at the top of the site can be pinned, which is unfortunately where a lot of the "cool stuff" is supposed to happen.
  3. What you've done is currently my emergency fallback though - but it requires that I modify the design such that the first part has no pinning in it and is always a minimum of 100vh high with no subsequent parts overlapping it, which breaks the client's design and I'm pretty sure the client is going to say that aspect of the design is important, being basically the first thing any visitor to the site sees. But I'm way over time and over budget so at some point a working site that doesn't do what it was supposed to is better than no site at all I suppose 😢

 

I've already wasted lots of time trying things like the above, but I'll try using a timeline to manually animate the fixed content to move negatively inline with the scroll and/or manually create pin spacers to see if that is smooth enough to work, but I'm guessing it'll break the subsequent pinning in the same way that using pinning does as it's effectively what pinning does. Unfortunately ScrollSmoother makes it impossible to actually fix the position of anything without using pinning (or manually doing something similar)

 

My only other idea is to get rid of ScrollSmoother, and then that opens up a whole pile precisely-timed toggles of position:fixed vs position:static changes and CSS solutions, but getting rid of ScrollSmoother makes all the animations across the rest of the site noticeably jerky, so I'll then need to play around with them, see if I can find smoother-looking easings.

 

Something that seemed simple (and which I quoted a simple amount of time to do) has turned into a nightmare.

 

If anyone does come up with a magical solution that I haven't thought about I'd love to hear it so please do keep the ideas coming. In the meantime I need to go back to the designer and ask that they change the design to something that can work, but unfortunately this is the very top of each page on the site so it's a fundamental aspect of how the site has been envisioned, and so far they have been incredulous that something as common as a slide-up reveal on top of fixed content, which itself also contains animations, which they have seen all over the internet, is impossible for me to do, and have asked that I look into alternative animation frameworks - and I tend to agree with them, this doesn't seem like it should be complex and it seems like it's something that would be commonly done?

Link to comment
Share on other sites

Alright, @thei, I spent a bunch of time trying to improve nested pinning functionality and here's a fork of your original demo, but loading the latest beta files: 

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

 

Can you hammer on that and see if you can break anything? I'm not prepared to promise full nested pinning functionality (certainly not beyond one level) - it's not a simple task to accommodate, I'm hoping this will allow you to deliver your project to your customer without completely stressing out. :)

 

You made a very fair point about the docs needing to do a better job of making it clear that nested pinning isn't supported, so I've added some notes in there. Thanks for the suggestion, and I apologize for any confusion there. I genuinely feel bad that you've been a long-time GSAP user and were frustrated with this experience. We really do try to bend over backwards to solve common problems so that stuff "just works". Like Cassie said, it's not always doable but we try our best. 

Link to comment
Share on other sites

2 hours ago, GreenSock said:

Alright, @thei, I spent a bunch of time trying to improve nested pinning functionality and here's a fork of your original demo, but loading the latest beta files: 

 

 

 

Can you hammer on that and see if you can break anything? I'm not prepared to promise full nested pinning functionality (certainly not beyond one level) - it's not a simple task to accommodate, I'm hoping this will allow you to deliver your project to your customer without completely stressing out. :)

 

You made a very fair point about the docs needing to do a better job of making it clear that nested pinning isn't supported, so I've added some notes in there. Thanks for the suggestion, and I apologize for any confusion there. I genuinely feel bad that you've been a long-time GSAP user and were frustrated with this experience. We really do try to bend over backwards to solve common problems so that stuff "just works". Like Cassie said, it's not always doable but we try our best. 

Thanks for looking into this for me. The latest version I can download seems to be 3.11.2, which is what I'm already using. I see that the codepen assets is 3.11.3f. Is there a way I can download the beta you mentioned in my actual project (I'm using the yarn add /.gsap-bonus.tgz method of install, it's a Nuxt-based site that's already had a bit of time spent on changing gsap stuff to allow cross-page animations without ScrollSmoother breaking things, so I'd rather not have to mess with the build system at this stage to change how scripts are loaded if I can help it?)
It is significantly more complex than the sandbox example (I have numerous pinned elements that also run simultaneous scrubbed scrolltrigger animations for example) - if it works that'd be great and I feel like nested pinning would be a useful feature for you to have. If not, I unfortunately don't have the time to mess around testing much anymore - project was due last week and I'm well over dev budget and working for free now.

Link to comment
Share on other sites

38 minutes ago, GreenSock said:

I just sent you a PM with instructions about how to get that. 👍

I have updated to this version and the pins (at least the ones I can scroll to) do seem to all work correctly, however, I can't scroll all the way to the bottom of the page. I'll see if I can make a codepen but it's getting complex.

Link to comment
Share on other sites

Yeah, a CodePen would be SUPER helpful. It's just crazy difficult to troubleshoot blind. As you can see in the CodePen I forked of yours, the "can't scroll to the bottom" issue was resolved, at least in that one.  I assume there's some other factor at play in your local one. 🤷‍♂️

Link to comment
Share on other sites

4 hours ago, GreenSock said:

Yeah, a CodePen would be SUPER helpful. It's just crazy difficult to troubleshoot blind. As you can see in the CodePen I forked of yours, the "can't scroll to the bottom" issue was resolved, at least in that one.  I assume there's some other factor at play in your local one. 🤷‍♂️

Unfortunately after a lot of effort I've been unable to make a codepen that illustrates the problem.

The best I can do is:


Here is a codepen that unfortunately seems to work just fine, and here is the actual code deployed to netlify, where it does not work. The netlify code is built using Nuxt so I'm guessing that's the cause for the problem, however I've tried to normalise them as much as possible - even adding the extra pointless wrapper divs that exist in the Nuxt generated code to the codepen code. I've also tried running the animation code after a 5 second delay and things like that, to rule out any Vue virtual dom delay weirdness - it all makes no difference.

 

If I get rid of the nested pinning, 100% everything works perfectly as per how GSAP has always worked, with no bugs, all great. But as soon as I have that nested pinning... problems. But annoyingly... the code in the codepen works.

 

Interestingly, resizing the page causes it to work - or at least, sort of work. However, manually calling Scrolltrigger.refresh() does *not* cause it to work, *unless* it's already scrolled as far as it can, in which case calling Scrolltrigger.refresh() *twice* causes it to allow it to scroll further (but usually too far)

 

note that I've got the code window.st = ScrollTrigger; in there, so you can call st.refresh() to demonstrate the above.

 

https://reliable-heliotrope-700433.netlify.app/demo/

 

See the Pen eYrreBX by nedmartin (@nedmartin) on CodePen

Link to comment
Share on other sites

9 minutes ago, GreenSock said:

Well we're making progress! Thanks for putting the effort in. I have a theory about what might be going on in your setup. I updated the files to work around it. Would you please try them again and let me know if it works well for you? 

Thanks for all your effort, but sadly no noticeable difference - still can't scroll to the end if I have nested pins.

I've updated https://reliable-heliotrope-700433.netlify.app/demo/ in case that's of use, though it looks basically the same. Note that it has a 5 second delay before it runs GSAP (debugging I forgot about and left in when I deployed it, to ensure Vue had fully finished and everything had loaded etc)
 

Link to comment
Share on other sites

I wonder if you got a cached file or something, so I just uploaded a fresh set of files that has a unique version ending in "-beta" on ScrollTrigger as well as a bunch of console.log() stuff so that I can more effectively see what's going on in your setup. Could you please update and let me know when I can hit that demo site again and verify things? 

Link to comment
Share on other sites

27 minutes ago, GreenSock said:

I wonder if you got a cached file or something, so I just uploaded a fresh set of files that has a unique version ending in "-beta" on ScrollTrigger as well as a bunch of console.log() stuff so that I can more effectively see what's going on in your setup. Could you please update and let me know when I can hit that demo site again and verify things? 

I believe you're right, I probably had a cached file - it was actually difficult to update, had to clear yarn's cache and delete node_modules, or it just kept reinstalling the same one.

Now that it is actually using your new beta, it appears to work properly 😀 - I can scroll all the way to the bottom! I haven't done any testing on mobile or whatever, but it works on my most complex page in both Edge and Firefox, where it didn't before, so I'm guessing it's good. I'll do some proper testing tomorrow.

I've updated the demo page in case you still wanted to look through your debugging logging, but given that it seems to be working, I'm assuming you don't need to.

 

Thanks for spending the time on this. I'm impressed in the turnaround time for what I assume was a complex issue/not actually supported feature. In future I'll try not to assume things I've done many times before will work and leave testing major functionality until the last moment and then it'll be a whole lot less stressful 😂

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, thei said:

It would appear that ScrollSmoother.scrollTop() is returning incorrect values

My bad - ignore that.

I still had offsets in from previous attempts to correct it when it was wrong. Now that I've removed those, it seems to be working as expected.

  • Like 1
Link to comment
Share on other sites

22 hours ago, thei said:

My bad - ignore that.

I still had offsets in from previous attempts to correct it when it was wrong. Now that I've removed those, it seems to be working as expected.

Actually, it is wrong in my opinion, though it's perhaps arguable how it should work? However it's consistently wrong by +100vh (I assume from my absolutely positioned 100vh element prior to my first pin?) so I'm just subtracting innerHeight and it all works.

See these two. Note how the yellow box is offset due to the scroll distance factoring in the pin. I'm not sure if this makes sense or not, but it's confusing and if it is the correct behaviour, then I'd ask the question - how would one calculate the actual scroll offset in real pixels, if the reported scroll offset itself is different?

 

See the Pen dyeeLNV by nedmartin (@nedmartin) on CodePen

 

See the Pen ZEooZYE by nedmartin (@nedmartin) on CodePen

Link to comment
Share on other sites

Heya,

 

Glad this is all being resolved nicely.

 

Quote

See these two. Note how the yellow box is offset due to the scroll distance factoring in the pin. I'm not sure if this makes sense or not,

I'd say this is correct, the distance the page has scrolled is different from the distance that container has moved as it's been pinned initially.

With this challenge I'd definitely lean on FLIP - Flip's great for complicated layout stuff like this. I'm guessing that if you're wanting the element to go 'fullscreen' you want it to be fixed? You can animate between two elements with FLIP, or if you wanted to avoid duplicate elements you could reparent the element into the body so it can be fixed outside of the smoother container.

 

Something like this maybe?


See the Pen GRdGPrQ?editors=0011 by GreenSock (@GreenSock) on CodePen


 

  • Like 2
Link to comment
Share on other sites

3 hours ago, Cassie said:

I'd say this is correct, the distance the page has scrolled is different from the distance that container has moved as it's been pinned initially.

Exactly right. ScrollSmoother's scrollTop() should definitely reflect the distance the page has scrolled (it'd be weird if that value didn't change at all while you scrolled and you've got an element pinned).

 

And yes, Flip is super helpful in cases like this. 👍

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