Hi, so I've had this need as well and this is how I went about it:
First, I created the observer using ScrollTrigger.observe({...}). Then immediately after, I disabled the observer, with observer.disable()
Then I created a ScrollTrigger instance with start and end and pin set to true.
Then onEnter and onEnterBack I enable the observer.
The observer has preventDefault set to true, so, when it's enabled, normal scrolling is disabled.
Then I have a condition for disabling the observer when the animation is done.
I've pasted the necessary segments of the code here with comments in-between. I hope it helps.
const animateOnScroll = (direction: -1 | 1) => () => {
if (state.current.isAnimating) return
let nextIndex = state.current.productIndex + direction
if (nextIndex < Min || nextIndex > Max) {
state.current.observer?.disable() // comment: this is where I disable the observer so user can resume normal scroll of the page, because if this condition meets, animation is done.
return
}
state.current.isAnimating = true
replayAnimation(wrap(nextIndex))
}
const observer = ScrollTrigger.observe({
target: ref.current,
type: "wheel,touch,scroll",
wheelSpeed: -1,
tolerance: 10,
preventDefault: true, // set to true, to disable normal scroll,
onUp: animateOnScroll(1),
onDown: animateOnScroll(-1),
})
observer.disable()
state.current.observer = observer
ScrollTrigger.create({
trigger: ref.current,
start: () => "top " + getTop() + "px",
end: "+=100px",
scrub: 1,
pin: true, // set to true so that scroll trigger pins the section while animation is on
markers: false,
animation: gsap.timeline().to(ref.current, { opacity: 1 }),
toggleActions: "play none none reverse",
onEnter: () => observer.enable(),
onEnterBack: () => observer.enable(),
})