Jump to content
Search Community

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

coffee_breath test
Moderator Tag

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

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

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

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

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

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