fix: sync use case click navigation with ScrollTrigger

Clicking a use case button previously set activeIndex directly, which
was immediately overridden by the next scroll event. Now scrollToIndex
animates the page to the correct ScrollTrigger position so the scrub
tween updates activeIndex naturally.

Registers ScrollToPlugin for programmatic scroll animation.

Amp-Thread-ID: https://ampcode.com/threads/T-019d9321-f99c-759b-9313-6f126651291d
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
DrJKL
2026-04-15 15:20:12 -07:00
parent 64f7ed4993
commit 15fec60f49
3 changed files with 26 additions and 5 deletions

View File

@@ -44,7 +44,7 @@ const navRef = ref<HTMLElement>()
const leftImgRef = ref<HTMLElement>()
const rightImgRef = ref<HTMLElement>()
const { activeIndex: activeCategory } = usePinScrub(
const { activeIndex: activeCategory, scrollToIndex } = usePinScrub(
{ section: sectionRef, content: contentRef, nav: navRef },
{ itemCount: categories.length }
)
@@ -138,7 +138,7 @@ useParallax([leftImgRef], { trigger: sectionRef, y: -60 })
? 'text-primary-comfy-canvas'
: 'text-primary-comfy-canvas/30 hover:text-primary-comfy-canvas/50'
"
@click="activeCategory = index"
@click="scrollToIndex(index)"
>
{{ category.label }}
</button>

View File

@@ -38,9 +38,26 @@ function interpolateY(
export function usePinScrub(refs: PinScrubRefs, options: PinScrubOptions) {
const activeIndex = ref(0)
let ctx: gsap.Context | undefined
let scrollTriggerInstance: ScrollTrigger | undefined
const vhPerItem = options.vhPerItem ?? 100
function scrollToIndex(index: number) {
if (!scrollTriggerInstance) {
activeIndex.value = index
return
}
const progress = index / (options.itemCount - 1)
const scrollPos =
scrollTriggerInstance.start +
progress * (scrollTriggerInstance.end - scrollTriggerInstance.start)
gsap.to(window, {
scrollTo: { y: scrollPos, autoKill: false },
duration: 0.6,
ease: 'power2.inOut'
})
}
onMounted(() => {
if (
!refs.section.value ||
@@ -84,7 +101,10 @@ export function usePinScrub(refs: PinScrubRefs, options: PinScrubOptions) {
pin: true,
scrub: true,
refreshPriority: 1,
onRefresh: cacheLayout
onRefresh: cacheLayout,
onUpdate(self) {
scrollTriggerInstance = self
}
},
onUpdate() {
activeIndex.value = Math.round(proxy.index)
@@ -106,5 +126,5 @@ export function usePinScrub(refs: PinScrubRefs, options: PinScrubOptions) {
ctx?.revert()
})
return { activeIndex }
return { activeIndex, scrollToIndex }
}

View File

@@ -1,6 +1,7 @@
import gsap from 'gsap'
import { ScrollToPlugin } from 'gsap/ScrollToPlugin'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin)
export { gsap, ScrollTrigger }