Jump to content
Search Community

ScrollSmoother + ScrollTrigger Snapping Causing Jankiness in a Pinned Element

Fifteen4 test
Moderator Tag

Recommended Posts

Greetings, friends!

 

So let me start by saying I've been happily using GSAP for about 7 years now and have been absolutely thrilled with it! Seriously, you all are a bunch of geniuses — this thing just works.

I've been churning through a bit of a head scratcher recently, though, so I've finally decided to come out of my lurker mode and ask a question!

 

I was trying to nail down some weird jumpiness in pinning full-height ScrollTrigger (and ScrollSmoother) elements on mobile devices. I'd worked through all the usual issues with this (mobile viewport resizing, browser address bar nonsense, etc.) with almost complete success, but just couldn't get the element to sit still upon first touching to scroll. I noticed that the issue was most prominent when scrolling shortly after letting a snap finish its work, so I removed snapping  (literally just commented out the config line) and sure enough — it was perfect again.

 

So it begs the question — am I missing something with the snap functionality? Is this a known issue on some devices? It's gravy on desktop devices, but gets finicky on my android (the only mobile device I've tested so far). Have I lost my mind? Any ideas would be appreciated forever ❤️

 

Here's some info that could help:


Device: Google Pixel 6 Pro

OS: Android 13

Browser: Chrome for Android v 107.0.5304.105

 

It's best to look at the pen in full-screen mode.

 

I've also taken a screen recording that demonstrates it. There's a slight jank visible on the very top and bottom most of the time, but the most striking occurrence of the issue happens around 7 seconds in.

 

Thank you all!

See the Pen LYrOMex by nick-patterson (@nick-patterson) on CodePen

Link to comment
Share on other sites

  • Fifteen4 changed the title to ScrollSmoother + ScrollTrigger Snapping Causing Jankiness in a Pinned Element
19 hours ago, Fifteen4 said:

but just couldn't get the element to sit still upon first touching to scroll. I noticed that the issue was most prominent when scrolling shortly after letting a snap finish its work, so I removed snapping  (literally just commented out the config line) and sure enough — it was perfect again.

Hm, so are you saying that the pinning itself seems to be vibrating a bit as you touch-scroll, but only on Android and only if you have snapping enabled? Is it only happening after you release (which is when snapping would take effect) or the whole time you're touch-scrolling? I'm having a tough time seeing it in the video, but I'm probably just missing something obvious. 

 

I'm curious if you notice any difference by adding will-change: transform on your pinned element and/or any child that you see "vibrating". Some browsers seem to do odd sub-pixel rendering unless you enable will-change (strange, I know). 

 

19 hours ago, Fifteen4 said:

So let me start by saying I've been happily using GSAP for about 7 years now and have been absolutely thrilled with it! Seriously, you all are a bunch of geniuses — this thing just works.

I've been churning through a bit of a head scratcher recently, though, so I've finally decided to come out of my lurker mode and ask a question!

This is so nice to hear! Glad you're coming out of "lurker mode". And thanks for being a Club GreenSock member! 💚🥳

Link to comment
Share on other sites

20 hours ago, GreenSock said:

Hm, so are you saying that the pinning itself seems to be vibrating a bit as you touch-scroll, but only on Android and only if you have snapping enabled? Is it only happening after you release (which is when snapping would take effect) or the whole time you're touch-scrolling? I'm having a tough time seeing it in the video, but I'm probably just missing something obvious. 

That's most of it! It's definitely restricted only to android (for now) and only when snapping is enabled. The main issue (more so than the top/bottom "vibrating") is that the pinned element seems to come entirely "unpinned" for a brief moment when attempting to scroll very shortly *after* a snap finishes. In my video, the most notable example of this is when the big white block appears at the bottom (or rather when the pinned element is no longer pinned, thus revealing the white body background underneath) just as my scroll begins at around 7 seconds. At this time, you can even see the little circles pop up towards the middle — these are what my fingers are doing when the problem arises. Does that help?

 

20 hours ago, GreenSock said:

I'm curious if you notice any difference by adding will-change: transform on your pinned element and/or any child that you see "vibrating". Some browsers seem to do odd sub-pixel rendering unless you enable will-change (strange, I know). 

Having just tried this, it seems to have had very minimal (if any) effect, unfortunately :(. I added the will-change prop to varying combinations of the pinned element, the scrollSmoother containers, etc. just to cover all of my bases.

Thanks so much for looking at this!

Link to comment
Share on other sites

Hm, that actually sounds like an Android bug most likely. Let me explain the dynamics...

 

Most browsers handle scrolling on a completely different thread from JS (and everything else). So think of it as if that thread took a screenshot of your entire page, and then when you press to start scrolling, it just renders it as if that screen capture moved with your finger. But all JavaScript (including ScrollTrigger) can only run on the main thread, so then when that updates, it's like "oh, that element should be pinned...render it that way please", so it jumps back into the correct position. It's absolutely impossible (as far as I know) for ScrollTrigger to force those threads to remain in sync. That's why we came up with normalizeScroll which is the best we can do - it essentially says "whenever you have a touch event, don't do what you'd normally do (like scroll) - instead, we'll use JavaScript to handle the scrolling." 

 

You've got normalizeScroll, so it should be cancelling the "normal" scroll but the behavior you're describing sure sounds like it's not in that case. I've seen Android devices handle the first touch event in a very odd way (not according to spec). I wonder if it's inappropriately ignoring our request to preventDefault() on that event. 

 

What's most confusing for me is that you're saying it's resolved if you disable snapping. Can you tell me if it's only happening if you try to scroll WHILE it's still snapping? Maybe make sure you wait long enough inbetween scrolls. Let the snapping COMPLETELY finish, then scroll again - do you ever see an issue when you do that? And remember, the snapping may take slightly longer to finish than you visually see due to easing. I'm trying to discern what exactly might be happening when you see the issue. If snapping isn't even happening, it wouldn't make much sense that disabling snapping would solve the issue. Snapping has zero effect on the normalizeScroll functionality - it's not as if snapping enables native scroll again. The more details you can provide about EXACTLY how to reproduce the issue, the more likely we'll be able to track it down. 

Link to comment
Share on other sites

@GreenSock hmm that makes sense in context here. The issue seems so intermittent and bizarre that a browser (or OS) rendering/event propagation bug wouldn't surprise me one bit.

Let me try and test both of these cases for you:

 

Scrolling WHILE Snap is in progress:

 

- Upon timing my scroll to "interrupt" the snap while I can definitely see the scrollbar still moving (so before the ease would really start to slow things down), I'm able to recreate the issue somewhat reliably.

 

- In doing this, the issue seems to vary between some slight "vibrations" (20px or so of white background flashing at the top/botttom) all the way to the severe phenomena like the large white space shown in the video. Both of these would flash right at the moment of touch, then subside after a split second.

 

- One *very* interesting thing I noticed is that the severe phenomena seems to only crop up when I "interrupt" a snap by scrolling in the SAME direction the snap is working towards. In fact, I was able to (twice) seemingly break things entirely, meaning the element remained "unpinned" even AFTER the snap completed (I waited probably 10 seconds) — only re-pinning if I were to scroll again. In digging further, it seemed like interrupting a snap by scrolling in the OPPOSITE direction of where a snap was working towards would only yield the vibration, not the severe phenomena.

 

- Regarding the above, It might be worth noting that my android is using the "natural" scroll interaction (inverted?) — meaning that the page scrolls UP when my finger moves DOWN. Since the severe phenomena seems to pop up only when interrupting a snap by scrolling in the same direction as the snap, could there be something with the delta in touch start/move/end in that case? I don't know if this is even at play here, but this certainly threw up a red flag for me.

 

Scrolling AFTER the snap finishes

 

- After taking your recommendation and waiting for the snap to completely finish, I wasn't able to replicate the severe phenomena at all — regardless of direction.

 

- I was, however, able to still replicate the vibration phenomena. It's worth noting that this doesn't occur at all when snap is disabled — in either direction.

 

Does this help?

Link to comment
Share on other sites

Thanks for the data, @Fifteen4. I cannot replicate the issue at all on my Android device. Would you mind experimenting with this?: 

// old
snap: "labels"

// new
snap: {
  snapTo: "labels",
  onStart: self => {
    self.getTween(true).eventCallback("onUpdate", () => {
      self.scroll.cacheID++;
      ScrollTrigger.update();
    });
  }
}

I'm just curious if that helps at all on your device.

 

As for the direction of scroll, I don't think there's any code in ScrollTrigger that'd act differently like that, so that certainly sounds like a browser-level glitch.  🤷‍♂️

Link to comment
Share on other sites

@GreenSock so interestingly, this addition prevents the severe issue from popping up entirely. I wasn't able to reproduce the large boxes or complete "unpinning" at all after adding it.

What does that mean in the context of the snapping? There is still the slight vibration happening (absent with no snapping enabled), but there's not anything nearly as drastic as before.

I'm still entirely confused, but thank you so much for the improvement!

Link to comment
Share on other sites

@GreenSock Haha a beta test!? For Greensock!? Oh — the pleasure is all mine.

 

After playing around with it for a while — trying all of our tests we've discussed and covered — I have to say it's a similar story as with the cache-busting onStart callback you gave me earlier today — the severe issue is entirely absent.

 

The vibration, however, remains, but to a seemingly more subdued extent and limited only to the top of the viewport. Adding back the will-change props (to the pinned element and the scroller content) doesn't seem to have any effect — but given that I'm trying all this in the same CodePen example I posted above, I wonder if CodePen's iFrame is responsible for this effect on Android. The apparent difference in behavior relative to scrolling direction is also now irrelevant — the vibration just remains at the top (right under CodePen's minimal UI) when attempting to scroll during or after a snap. Perhaps a truly isolated full page may be treated differently?

I can try this again in an isolated sandbox (no CodePen iFrames) tomorrow, but regardless, this is a significant (huge) improvement.

Thanks so much for the improvements already — and I'll update you tomorrow.

Link to comment
Share on other sites

16 hours ago, Fifteen4 said:

the severe issue is entirely absent.

🥳

 

As for the "vibration", that likely has to do with how the browser is rounding things. I can't replicate that anywhere, sorry. Would you please try this beta version of ScrollSmoother (on CodePen/CodeSandbox/Stackblitz only) and set wholePixels: true in your config object?: 

https://assets.codepen.io/16327/ScrollSmoother.min.js

 

ScrollSmoother.create({
  wholePixels: true, // <- only for testing, not official API
  smooth: 2
});

Does that help anything with the vibration? I kinda doubt it will, but I'm curious about your device.

Link to comment
Share on other sites

Hey @GreenSock Sorry but I totally lied about getting back to you when I said I would! Haha evidently I forgot about Thanksgiving. If you celebrate, I hope yours was a wonderful one.

In trying these betas out, the results are the same. The severe issue is gone entirely, but the vibrations and slight unpinning during/after snap still remains. I think it's definitely safe to say this is a rendering bug specific to something about my device. Perhaps there's some underlying difference in how the browser renders things as a response to a touch vs. when the scroll position is programmatically manipulated (as with a snap).

 

Interestingly, though, on said (problematic) device — the scrollbar seems to sometimes not show at all with the new betas (noticed with the new scrollTrigger by itself and with both scrollTrigger + scrollSmoother). I'm able to scroll normally in any event, but occasionally the page loads entirely without a scroll bar. It seems intermittent as far as I can tell, but is most easily replicable after clearing my cache and returning to the demo after some time.

In any event, thank you so much for tracking down a remedy for this! I'm eager to see the next version address it.

In my situation, though, I may re-work things to use native sticky positioning and just use the scroll trigger to scrub the timeline and not worry about pinning. Since, in practice, there's only one full-screen element like this on the page, it may be worth carving out the exception to handle it.

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