Jump to content
Search Community

ScrollTrigger pinnedContainer - issue when trigger starts before pin ends

ethanchiu10 test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

@Cassie Thank you for the quick response! But I feel that solution may be too specific to this one use case.

 

For example, the current project Im working on is CMS driven using Vue. Pinned components or Parallax components (or other scroll trigger components) could be in any order. The parallax component (or other scroll trigger component) could come before and/or after the Pinned component, and have different start/end requirements unrelated to the Pinned component. 

 

Also, that solution loses the parallax effect when the image first enters the page.

 

Would be nice to be able to solve it for scroll-smoother speed too.

Link to comment
Share on other sites

Mmm. I'm not really sure of the solution here (or sure there is one, it sounds like a bit of a logical conundrum)

Maybe @GreenSock has some ideas.

(Also I thought pinnedContainer could be of use here to leverage for the parallax element to offset the start/end positions instead of self.previous().end but I couldn't get it to work so I'm wondering about that too)

Link to comment
Share on other sites

  • ethanchiu10 changed the title to ScrollTrigger pinnedContainer - issue when trigger starts before pin ends

I'm a little fuzzy on your goal here - the problem is that your parallax starts BEFORE the pin, thus its start should not get pushed down on the page. Are you saying that you want it to push only the END position down according to how long the pin is? So it would essentially push it 1000px further down, stretching out the entire parallax effect much longer? 

 

In this case, it'd be as simple as adding 1000px to the end: 

end: "bottom+=1000 top",
Link to comment
Share on other sites

Yes, the end positioning should be pushed down 1000px. But, also:

 

1. The parallax should not be animating while the page is pinned

 

2. Shouldn't this be done internally by ScrollTrigger?

 

3. How would all my subsequent parallax image components know whether it started before a pin (which requires adding extra px to the end), or after the pin (which does not require adding extra px to the end)?

 

4. How can we determine how much needs to be added? Do I need to add enough px for all previous pins? Or only the pin that overlaps? And is there a way to get the amount that needs to be added? Or do I need to calculate and store this myself?

Link to comment
Share on other sites

The big issues is, if you only pin a section of the page, the sections before and after seems to float away and float in, and looks strange in a portrait sized window. 
 

To solve this, we need to pin the entire page instead of just a section.

 

But doing so causes the other parallax images to have issue.

 

This is a really common use case in my view. Just personally, I'm already on the 3rd project that could have used this feature. But I've just gotten around the issue by either A. pinning only the section and letting the previous/next sections float in/out, or more often B. stretching the section to full viewport height. In both cases, it looks visually wrong for tall windows (very common use case when a window is placed on one side of the screen).

Link to comment
Share on other sites

Oh! Yeah, that's definitely not something I'd expect to support. Think of it this way: a ScrollTrigger's progress is mapped linearly between its start and end scroll position. So let's say it starts at 0 and ends at 1000 to make it easy. Progress would be 0.5 at exactly 500. What you're proposing is an entirely new and likely confusing behavior where if the pinnedContainer starts at 200 and lasts 500px, the original ScrollTrigger would technically keep its start of 0, get a new end of 1500 but the progress would become very non-linear. Between 0 and 200 it would go non-proportionally fast, then suddenly stop for 500px (so the scrollbar is moving between the start/end of the ScrollTrigger, but the progress isn't, and onUpdate isn't getting fired, etc.) and then at 700 it'd start moving quickly again through 1500. If I wanted that ScrollTrigger to be at exactly 0.5 progress...where would that be? It's no longer where folks might expect (halfway between the start and end scroll positions). 

 

Not only could that get very confusing for people, it likely has BIG ramifications on many other things that trickle-down like ScrollSmoother effects because they're mapped linearly with some pretty complicated logic already to accommodate pinning and other things. It would require quite an overhaul.

 

If this is something you'd really like to pursue, feel free to reach out privately and we can discuss paid consulting services. This is definitely an edge case that very few other users would likely want or care about. 

 

There are ways you could get this behavior in the current version with enough elbow grease, building out custom logic in an onUpdate or a CustomEase for example. Another option is a timeline that splits the scaling up into two chunks according to the start/end positions, leaving a gap while the pin is happening. 

Link to comment
Share on other sites

I really don't think non-full viewport height pinning is as rare and edge case as you think. I'll show you in a few weeks after my current project launches.
 

I think it is a huge limitation of ScrollTrigger's pinning system. It essentially only supports pinning full height sections, and creates visual or logical bugs when they are not full height.

 

I do appreciate and understand the difficulties in implementing it in to the current system though. Perhaps something to think about once you hear enough demand. Maybe it will warrant a rewrite of ScrollTrigger and/or its pinning system then.

Link to comment
Share on other sites

50 minutes ago, ethanchiu10 said:

I really don't think non-full viewport height pinning is as rare and edge case as you think....It essentially only supports pinning full height sections, and creates visual or logical bugs when they are not full height.

I think you may be misunderstanding. ScrollTrigger definitely supports pinning elements of any size. It doesn't care if it's the full viewport height or not. There are plenty of examples in the CodePen collections, demos, etc. The unique problem in your particular scenario is that you're trying to have one ScrollTrigger interfere with another and have a pinned container start in the middle of another ScrollTrigger that should be affected. This tool has been out for years now and I don't think I've ever seen anyone else even try this, much less report it as a problem/limitation. That doesn't mean it's a bad idea or it couldn't be quite useful at times. I do appreciate your suggestion and don't fault you at all for recommending it as a possible feature addition. 

 

56 minutes ago, ethanchiu10 said:

I do appreciate and understand the difficulties in implementing it in to the current system though. Perhaps something to think about once you hear enough demand. Maybe it will warrant a rewrite of ScrollTrigger and/or its pinning system then.

We're always trying to understand what our users want, looking for ways to improve, etc. So yes, if a lot of people run into a need for this and request it, we may consider a rewrite. We've got some other exciting things in the hopper that an exponentially larger portion of our users would benefit from, so spending hundreds of hours rewriting logic to accommodate this one extremely uncommon scenario just doesn't seem prudent at this point but like I said we appreciate the suggestion and we'll be listening for similar feedback from others. If you want to pursue private consulting about this outside the forums, feel free to reach out and I'd be happy to discuss.

 

Thanks for taking the time to put together the minimal demo too - that was very helpful. And thanks for being a Club GreenSock member! 🙏

Link to comment
Share on other sites

Apologies, I don't mean to say it doesnt support it at all. It certainly can pin elements of any size, but with a huge caveat. 

 

1. The previous/next element floats away in a wierd way, or;

 

2. Subsequent scrolltriggers will not run correctly unless you can guarantee they start after the pin. That means you are not allowed to use any scrolltrigger that triggers based on entering the viewport. In real world usage with a CMS, that basically makes it unusable. 

 

I personally have seen this issue since the very first site I used ScrollTrigger on https://goodmeat.co/ (solved by making everything full height) and soon after that on https://wisr.com.au/ (solved by letting prev/next sections float away wierdly) , but I never bothered to report it, as I'm sure many others have too. I think once my new project launches in 3 weeks, I'll be able to show you a really really clear example of why this is such a necessary feature. 

 

I think once you fully understand the issue, you will realize it is a very very common use case.

1. It is very common to view webpages in portrait mode (such as when your browser is using half the screen of a laptop)

2. Many sections of long scrolling sites should not be full height in portrait mode.

3. One of the most common use case for scroll triggers is to trigger an animation when the item has entered the viewport.

Given these points, I hope you can appreciate how common and important this feature request truly is.

 

I do want to add, I've loved using gsap for many years, it is an amazing product, thank you!

Link to comment
Share on other sites

Actually maybe this example is good enough to show you the issue already. 

 

https://wisr.com.au/credit-scores

 

This page can be significantly improved in portrait mode if I could pin the entire page instead of only 1 section. But if I did that, and the immediate next section had any need for ScrollTrigger, it would break. 

 

Hope that clears things. If not, I'll show you more examples with videos in a few weeks.

 

 

 

design.jpg

 

 

 

Link to comment
Share on other sites

 

Maybe I'm just misunderstanding the actual issue here, but couldn't the subsequent ScrollTrigger playing whilst the one before pins be circumvented, by adding some relatively simple logic that disables/enables the subsequent ScrollTrigger in the callbacks of the pinning one?

 

Looks to me like that would do what you descriebed, you wanted to happen @ethanchiu10

 

Edit: Of course on the other hand that could get a bit tricky with resizes, where the progress might just jump in cases when the ScrollTrigger will be disabled.

 

See the Pen NWymRgr by akapowl (@akapowl) on CodePen

 

  • Like 1
Link to comment
Share on other sites

9 hours ago, ethanchiu10 said:

1. The previous/next element floats away in a wierd way

Got a minimal demo that shows a common use case where something floats away in a weird way? 

 

9 hours ago, ethanchiu10 said:

Subsequent scrolltriggers will not run correctly unless you can guarantee they start after the pin. That means you are not allowed to use any scrolltrigger that triggers based on entering the viewport. In real world usage with a CMS, that basically makes it unusable.

Again, I wonder if there's a misunderstanding here - a minimal demo would be super helpful. Doesn't "subsequent" imply "after"? And if it's before, the pinning shouldn't affect it unless you're doing this very particular setup where you're pinning the container inbetween another dependent ScrollTrigger's start and end. Like I said, literally not one other person (to my memory) has ever mentioned an issue like this. I've seen quite a few CMS's use it as well, so calling it "ususable" seems like a bit of a stretch but maybe I'm missing something obvious. 🤷‍♂️

 

11 hours ago, ethanchiu10 said:

One of the most common use case for scroll triggers is to trigger an animation when the item has entered the viewport.

Given these points, I hope you can appreciate how common and important this feature request truly is.

I guess I'm still fuzzy on why you seem to think ScrollTrigger can't work well when an element enters the viewport or in portrait mode. Probably thousands of sites are doing these things with ScrollTrigger with great success. I'm sure I'm just misunderstanding your point. 

 

Sometimes I'm not sure people realize just how complicated it can get to accommodate all these features. 

 

Almost anything is possible with the tools - for an edge case like this that creates logic problems, it just requires some extra effort with some custom code most likely. @akapowl's suggestion of multiple ScrollTriggers is valid and there are certainly ways you can ensure that's responsive too. Unfortunately it's not generally feasible to build a perfect toolset that handles every possible edge case, but I'm definitely interested in adding features and solving problems as much as is realistic. This just doesn't seem like one of those cases where it justifies all the extra kb, processing, and development time to tear everything apart and rebuild it to hopefully accommodate this edge case that has come up once so far (though I'm sure you're right that some people may have had a similar need but never reported it). 

 

11 hours ago, ethanchiu10 said:

I do want to add, I've loved using gsap for many years, it is an amazing product, thank you!

Very much appreciated! 👍

Link to comment
Share on other sites

1. Previous / next section scrolls away wierdly. (lets call it "scroll" instead of "float" from now on, to avoid confusion)

Minimal Demo 

See the Pen VwQgVgR by ethanchiu10 (@ethanchiu10) on CodePen

 

Real Demo https://wisr.com.au/credit-scores

 

 

Forewords

First of all, I want to make sure we don't get off on the wrong foot here. I want to say most importantly that gsap is an amazing piece of software that can do a TON of things, and 'm a HUGE fan. I 100% appreciate how incredibly complicated and amazing this tool is. Personally I've been using your product throughout my entire career. https://ethanchiu.dev/ Every single one of those sites in my portfolio has used gsap, so THANK YOU THANK YOU THANK YOU for creating an amazing product, and to keep adding new cool libraries such as ScrollTrigger and ScrollSmoother!

 

The fact that I haven't needed to post a single message on this forum throughout all these years is truly a testament to how many possible use cases gsap supports and how incredibly flexible this library is. You absolutely should be proud of it!

 

With that said, I hope you are not too proud to think ScrollTrigger is already perfect after just 2 years of existence, and that there is no room for improvement. I hope you don't dismiss this issue because you perceive it is as niche and edge case and haven't heard a lot of complaints. I'm trying really hard to show you examples and prove to you why it is a VERY common use case. And I hope that you can recognise I'm a power user that wouldn't be posting if there wasn't a real issue.

 

Obviously scroll trigger supports elements entering the viewport in portrait mode. But what I'm trying to tell you is, it does NOT support that IF you need to have the page pinned when a non-full height horizontal scrolling section comes in

 

The Problem

1. when a section needs horizontal scrolling, the entire page should pin instead of just that section. I don't want the previous/next sections to scroll away / in. [see scrolling away example at https://wisr.com.au/credit-scores]

2. the sections immediately below this pinned section should be allowed to have scrubbable scroll triggers that depends on entering the viewport. [e.g. parallax images]

 

Work Arounds and Caveats

Yes, work arounds exists, but each work around has non-ideal caveats. Either you have to

1. make all of your horizontal scrolling sections full height, in which case it looks wierd in portrait mode for a lot of designs

2. pin only the horizontal scrolling section instead of the whole page, in which case the prev/next sections scrolls out/in wierdly

3. pin the entire page, but then the scrubbable scroll triggers that depend on entering the viewport after the pin does not work correcly (as shown in the very first post)

4. pin the entire page, manually keep track of all the scroll triggers that are not part of the horizontal scrolling section, and toggle their playback (as shown by @akapowl)

 

Link to comment
Share on other sites

  • Solution
36 minutes ago, ethanchiu10 said:

1. Previous / next section scrolls away wierdly. (lets call it "scroll" instead of "float" from now on, to avoid confusion)

Oh, sure, that's totally expected behavior since you're pinning the one element but the others continue to scroll. That's very much by design and not a bug at all. In fact, if it didn't behave like that, I bet we'd get a ton of complaints about that being a bug :)

 

As you've discovered, there are ways to get the behavior you wanted (pinning the container).

 

Thanks for the minimal demo, by the way - that made it very easy to see what you meant. 

 

47 minutes ago, ethanchiu10 said:

I hope you are not too proud to think ScrollTrigger is already perfect after just 2 years of existence, and that there is no room for improvement.

Oh goodness, no! I hope you've seen that we keep adding features, improving things, etc. Virtually every GSAP release since ScrollTrigger was born has had new ScrollTrigger goodies. I've been spending hundreds of hours on ScrollTrigger-related improvements the past few weeks alone. I have never gotten to the place with any tool where I thought "yep, that's perfect...there's nothing that could possibly be improved."

 

When I call it an "edge case", I don't mean to dismiss you or imply your suggestion has no merit.

 

Every feature has a cost (file size, performance, development time, API surface area, code complexity, etc.). We just have to be careful about where we focus our limited resources and which features are truly worth the costs. What if the feature you're asking for made ScrollTrigger 30% bigger and 25% slower across the board, and only 0.02% of our user base would even use the feature - would that be worth it? What if it took 6 months of development due to the complexities? What if there were 10 other super-cool features that could be developed in that same amount of time that would significantly benefit 85% of our user base in their daily work and cost a fraction of the kb with no performance penalty? Can you see why it wouldn't be prudent to make that tradeoff?

 

I know that when you are the one experiencing a frustration and desire a particular feature, it probably feels like an extremely common problem that tons of people must run into. You may be right, but it's definitely not what the data show (at least according to the data I have access to). 

 

Sometimes I think it's difficult for users to understand all the complexities involved with creating and maintaining a tool like ScrollTrigger. It may sound easy to "just make it do ____ because my project needs it and I think a lot of other people would want that too," but making it a reality is quite a different thing. It may be a fun experiment for you to try to either build something like ScrollTrigger with a similar feature set (plus the one you're requesting), or try editing ScrollTrigger's source code to add the feature you're requesting. My guess is that it'll become apparent pretty quickly that it's no easy task :) My only point in bringing that up is that if it was as simple as "tweak 20 lines of code," I'd totally do that because the cost wouldn't be terribly high. But what you're asking for is far more complex and would likely open a can of worms that could lead to non-intuitive behavior like I described earlier where the "progress" doesn't report as expected, etc. 

 

1 hour ago, ethanchiu10 said:

Work Arounds and Caveats

Yes, work arounds exists, but each work around has non-ideal caveats. Either you have to

1. make all of your horizontal scrolling sections full height, in which case it looks wierd in portrait mode for a lot of designs

2. pin only the horizontal scrolling section instead of the whole page, in which case the prev/next sections scrolls out/in wierdly

3. pin the entire page, but then the scrubbable scroll triggers that depend on entering the viewport after the pin does not work correcly (as shown in the very first post)

4. pin the entire page, manually keep track of all the scroll triggers that are not part of the horizontal scrolling section, and toggle their playback (as shown by @akapowl)

Here's another one - I whipped up a helper function that should make it easier for you: 

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

 

Here's the helper function: 

function pinSafe(animation) {
  let originalVars = animation.vars.scrollTrigger,
      markers = originalVars.markers,
      // we'll just use this ScrollTrigger for positioning purposes. It's identical to the original (but no markers)
      st = ScrollTrigger.create(Object.assign(originalVars, {markers: false})),
      container = st.pinnedContainer,
      split = [], // store the new tweens we create here
      onRefresh = () => {
        let distance = st.end - st.start;
        // now find the ScrollTrigger(s) with the pinnedContainer as the pin 
        ScrollTrigger.getAll().forEach(curST => {
          if (curST.pin === container) {
            let curDistance = curST.end - curST.start;
            if (curST.start > st.start && st.end > curST.start) {
              // split it into two tweens (one for the first part of the animation before the pin, and the second for the part after the tween)
              split.push(
                gsap.fromTo(animation, {progress: 0}, {progress: (curST.start - st.start) / distance, ease: "none", scrollTrigger: {start: st.start, end: curST.start, scrub: originalVars.scrub, markers: markers}}),
                gsap.fromTo(animation, {progress: (curST.start - st.start) / distance}, {progress: 1, ease: "none", scrollTrigger: {start: curST.end, end: st.end + curDistance, scrub: originalVars.scrub, markers: markers} })
              );
            } else if (st.start === st.start) { // special case (if the start positions are identical, push it down)
              split.push(gsap.fromTo(animation, {progress: 0}, {progress: 1, ease: "none", scrollTrigger: {start: curST.end, end: st.end + curDistance, scrub: originalVars.scrub, markers: markers}}));
            }
          }
        });
        if (!split.length) { // if none were created, just revert back to the "normal" one.
         split.push(gsap.fromTo(animation, {progress: 0}, {progress: 1, ease: "none", scrollTrigger: originalVars}));
        }
      };
  animation.scrollTrigger.kill(true, true);
  animation.pause();
  ScrollTrigger.addEventListener("refresh", onRefresh);
  ScrollTrigger.addEventListener("refreshInit", () => {
    split.forEach(s => s.kill(false));
    split.length = 0;
  });
}

Just pass the tween/timeline into the pinSafe() method and it looks for other ScrollTriggers that pin the pinnedContainer and then if/when it finds one, it splits it apart into two tweens of the animation's progress (before and after the pinning chunk) 

 

I hope that helps. 

 

3 hours ago, ethanchiu10 said:

I want to say most importantly that gsap is an amazing piece of software that can do a TON of things, and 'm a HUGE fan. I 100% appreciate how incredibly complicated and amazing this tool is. You absolutely should be proud of it!

Very kind of you to say. 💚

  • Like 2
Link to comment
Share on other sites

1 hour ago, GreenSock said:

Oh, sure, that's totally expected behavior since you're pinning the one element but the others continue to scroll. That's very much by design and not a bug at all. In fact, if it didn't behave like that, I bet we'd get a ton of complaints about that being a bug :)

I absolutely agree, and I'm not suggesting that it is a bug at all. It is very much a legitimate way of pinning, and has many uses that I've personally used also. But this method of pinning does not cover all use cases, and that's fine, that's why ScrollTrigger supports the pin option to let you pin something else. That's all awesome and great.

 

I totally appreciate that a library such as this needs to be balanced against not only your effort, but also the file size and adding complexity to all projects that may use such a library. You are absolutely right to be extremely careful in adding only the necessary features that will help the most people. 

 

Also, thank you very much for your detailed example and helper function, I'll look in to implementing that to my current project!

 

Finally, I do understand that maybe not many other users are asking for such a feature. Alas, I'm only one data point, and I know I don't represent everyone. But as far as I can tell, most scrolling sites could really benefit from this. I'll leave you with this:

 

1. Is pinning a section to do horizontal scrolling very common?

2. Are portrait window sizes very common?

3. Would many horizontal scrolling sections look better and/or more correct if they were not forced to be full height in portrait mode?

 

Link to comment
Share on other sites

9 hours ago, ethanchiu10 said:

1. Is pinning a section to do horizontal scrolling very common?

2. Are portrait window sizes very common?

3. Would many horizontal scrolling sections look better and/or more correct if they were not forced to be full height in portrait mode?

I think we may still be missing each other here - your unique challenge is caused by using a pinnedContainer that gets pinned AFTER the start of another ScrollTrigger whose end is after the pin - you're trying to start/stop it based on the pinning (the pin's start is further down on the page than the dependent ScrollTrigger whose progress you want to impede). It has absolutely nothing to do with portrait/landscape mode, horizontal scrolling, full-height, etc. 

 

You seem to be under the impression that ScrollTrigger doesn't support horizontal scrolling in portrait mode and/or that it forces people to make the horizontal scrolling be full-height. That isn't true. You're just creating another ScrollTrigger that's interrupted by the pinning (it's positioned in the middle of it) and you're expecting it to start/stop. I think that's why you're the first person to request support for that kind of thing - it's just not very common. Again, I'm not saying it's uncommon to pin while doing the horizontal-fake-scroll stuff, even in portrait mode or non-full-height - it's the fact that you're creating another ScrollTrigger that spans across the pinning. 

 

Does that clear things up? 

 

Either way, I just spent a bunch of time creating that helper function for you to address your particular request so hopefully that does away with any need for further back and forth complaining that it isn't possible :) Like I said earlier, almost anything is possible with the current toolset and a little elbow grease. 🎉

Link to comment
Share on other sites

10 hours ago, GreenSock said:

You seem to be under the impression that ScrollTrigger doesn't support horizontal scrolling in portrait mode and/or that it forces people to make the horizontal scrolling be full-height.

 

It can support this IF you don't use any scrubbable ScrollTriggers that starts by entering the viewport in the rest of the page. I think this is a huge limitation.

 

Anyway, thanks again for making the helper function, it is helpful indeed!

 

In a few weeks time after my current project launches, let me share it with you here, I think a real example will clear everything up : )

Link to comment
Share on other sites

39 minutes ago, ethanchiu10 said:

It can support this IF you don't use any scrubbable ScrollTriggers that starts by entering the viewport in the rest of the page. I think this is a huge limitation.

No. But I give up. I've tried a few times to describe the very specific case you're stumbling on, but I must not be doing a good job. That's okay, the helper function should do the job for you, so it's a moot point. :) 

 

Good luck with the project! 👍

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