Jump to content
GreenSock

coffee_breath

ScrollTrigger + Snapping + Native smooth scroll demo - issue with native scroll position after snapping

Recommended Posts

 

Hey there,

 

I'm trying to get the Smooth Scroll with ScrollTrigger Natively  demo to work with snapping.

In the codepen supplied snapping works - it transitions to the closest value from point a to point b as expected.

 

However once you start scrolling again ( after the snap has completed ), the native scroll bar reverts back to point a, where it was before the snap transition. It appears that the native scroll position isn't updated duration the snap transition.

 

I tried updating the setProp function to set window.scrollTop rather than the original "content" elements position, in a hope that the native scroll position would update ScrollTrigger and in turn update the the "content" elements position. But his didn't not work.

 

Thanks in advance

 

 

See the Pen MWJdEMR by the-cam (@the-cam) on CodePen

Link to comment
Share on other sites

Welcome to the forums, @coffee_breath. And thanks for being a Club member! 

 

I'll need some time to dig into this and see exactly what's going on. The fundamental problem is your scroll position isn't synchronized when you're snapping. It may take a while for me to get back to you, but it's on my list. 

  • Like 2
Link to comment
Share on other sites

Hey Jack,

 

Thanks for the incredible library btw, i've been an advocate for some time.

 

It appears setting the window position in setProp kills the snapping transition tweenTo in ScrollTrigger.

This makes sense, given a user scroll should kill the snapping transition right?

 

The only problem with this approach is that it creates a loop.

  1. Scroll within dist of snap
  2. transition to point
  3. call setProp - set window position
  4. scrollTrigger detects a scroll
  5. kill snap transition
  6. Scroll within dist of snap (back to start).

 

A possible work around inside ScrollTrigger might be to check the current scroll position against the expected scroll position, if they don't match then it suggests a user has taken control of the scrolling, in which case kill the snap transition.

 

pseudo code;

// Somewhere in ScrollTrigger, where the scroll handler is killing the snap tweenTo transition

window.addEventListener('scroll', val => {
  let y = getScrolltop()
  killSnap(y)
})

killSnap(y) {
  if (Math.floor(snapTweenObj.y) !== y && snapping) {
    gsap.killTweensOf([ snapTo, snapTweenObj])
    snapping = false
  }
}

getScrolltop(){
  return ( document.body.scrollTop || document.documentElement.scrollTop )
}

// snapTo func called when within dist of closest snapPoint
snapTo() {
  snapTweenObj.y = getScrollTop()
  gsap.killTweensOf(snapTweenObj)
  gsap.to(snapTweenObj, {
    duration:  0.5,
    ease: 'sine.inOut',
    onUpdate: onSnapUpdate,
    onComplete: onSnapComplete,
    y: snapPoint,
  })
}

onSnapUpdate() {
  gsap.set(window, { scrollTo: snapTweenObj.y })
}

onSnapComplete() {
  snapping = false
}

    

 

Link to comment
Share on other sites

Phew! After many hours of digging into this, I think I've got it all worked out. Here's a fork: 

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

 

Notice I pulled your custom code out of the smoothScroll() function. I think that's a lot cleaner now. 

 

The fundamental problem was that the scrollerProxy's setter needed to ALSO set the actual scroll position of the page (not just the "fake" content scroll) so that they're synchronized, but doing so directly would create a funky loop because the main ScrollTrigger is set up to basically smooth out the "real" scroll position, thus when we set it in the scrollerProxy(), it was being softened in a sense. I updated the smoothScroll() function to sense that condition and work around it. Seems to work well, right? 

 

And yes, the snapping logic already employs the technique you mentioned internally where it checks to see if the scroll position matches (or is at least very close to...because browsers are weird) the last one set by the snap tween. If not, it kills it, assuming the user (or something else) influenced the scroll. 

 

Anyway, it feels good to have that one figured out. Hopefully the new function solves any issues you were running into. Please let me know if it works well for you. 

  • Like 4
Link to comment
Share on other sites

👏👏👏  Works perfectly!

 

I would suggest elevating Demo (native ScrollTrigger)  to the top of the scrollerProxy examples - it's the cleanest solution imo - using other lib's with ScrollTrigger for smooth scrolling muddies the water.  

 

Or even consider rolling this into a companion plugin for ScrollTrigger... ScrollJack 😃.  JK

 

Thanks again! gsap is the best.

 

  

  • Like 2
Link to comment
Share on other sites

On 5/8/2021 at 4:16 AM, coffee_breath said:

I would suggest elevating Demo (native ScrollTrigger)  to the top of the scrollerProxy examples - it's the cleanest solution imo - using other lib's with ScrollTrigger for smooth scrolling muddies the water.  

Fair point. Done. 

 

Glad it's working well for you!

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

hey @GreenSock, this solution looks fantastic as I really would like to not have to use another scroll library and stick with just GSAP, but in all honesty it's a bit over my head. I am trying to update this snapping function so that it would snap the '.box' to the centre of the "viewport" but I can't get it to work right, I've read the documentation on utils.snap() and still not much help there for me either. Can you please point me in the right direction?

Link to comment
Share on other sites

Sure, to center it you'd just have to offset the value by half the viewport height and half of the element's height:

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

 

Is that what you wanted?

  • Like 1
Link to comment
Share on other sites

Hey @GreenSock sorry for the delay, I;ve been a bit unwell lately. This is great thankyou. I've been trying to get the smoothScroll to work horiziontally by switching all the height's to widths and y's to x's but no dice. Is there something I'm missing here?

Link to comment
Share on other sites

Hey @ilike.idontlike


I guess it depends on how you're doing horizontal scrolling, if you're 'faking' horizontal scroll you can add a scrub value and that'll do the job without smoothScroll.

If this doesn't help would you be able to post a minimal demo of your use case and what you've tried?

See the Pen 7435db37f1a04d9aa921ff0f17a8030d?editors=1011 by cassie-codes (@cassie-codes) on CodePen

  • Like 1
Link to comment
Share on other sites

hey @Cassie. I'm not planning on having set width containers. Its so I can do horizontal scrolling on an entire page of images, which will all have variable widths (height defined though). 

See the Pen dyWQzaM by jo02 (@jo02) on CodePen



Here's my pen where I restyled for horizontal and switched out height for width and y for x. 

Link to comment
Share on other sites

So I'm not super clued up on how the smoothScroller works yet - from a glance there's a scrollTop value in there so I think it'll be a bit trickier to refactor than just swopping x and y values. @GreenSockwill be able to advise more!

Either way - you can still use my demo without a fixed width container

See the Pen d278004cb5c44e32cf6e5da8e156d468?editors=1100 by cassie-codes (@cassie-codes) on CodePen

Link to comment
Share on other sites

So Ive used this solution already but It doesn't work with the snapping that I'm after where the image would snap to the center of the viewport. I was hoping to find a solution in  native smooth scroll sine Locomotive scroll library doesnt work with snapping for some reason.

  • Like 1
Link to comment
Share on other sites

There were a bunch of issues in your code that I've resolved in this demo (as far as I can tell at least): 

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

 

Is that more like what you're looking for? 

  • Like 1
Link to comment
Share on other sites

@GreenSock. Thanks for taking the time on this. I love that it scrolls smooth from left to right but I'm trying to get it to do that when the user scrolls up and down. Sort of like a combination of yours and Cassie's code if that makes sense?

Link to comment
Share on other sites

Hmm. Ok, well you can do snapping with fake scrolling.

These aren't quite the right calculations though. 👀

I'm going to go and have some breakfast and ponder it over. I think my brain needs some toast. Here's the vague idea in the interim

See the Pen 49e47131423a38053917443d19de96d2?editors=0011 by cassie-codes (@cassie-codes) on CodePen

Link to comment
Share on other sites

Toast and tea is always a good plan. It just needed a little offsetting. 🥳

 

You'll need to add an event listener and recalculate the snapPositions array on resize, but I think this should get you most of the way there?

 

(updated previous pen)
 

Link to comment
Share on other sites

You english too? I find toast and teas to be *the* thing to get me through figuring out anything JS hahaha. Thankyou so much @Cassie I'll look into this!

  • Haha 2
Link to comment
Share on other sites

Tea is always the solution

  • Haha 1
Link to comment
Share on other sites

Here's a slightly tweaked version of Cassie's which recalculates things on refresh (which is called by default on screen resizes): 

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

  • Like 1
Link to comment
Share on other sites

Oh cool! I wasn't aware of that. That makes life easier.

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