It appears as though the scrollerProxy is initiated multiple times after routing which causes the matrix 3d animation to jump from -232213 to 0 and back.
I think this is because the nuxt instance is kept while routing between pages that also use the _.vue page template but I cant figure out how to force it to forget the past scrollerProxy 😢
Any advice as to how to initiate smoothscrolling in Nuxt?
// _.vue template
<template>
<div>
</div>
</template>
<script>
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export default {
mounted() {
this.smooth()
this.$forceUpdate()
},
destroyed() {
// used to kill the ScrollTrigger instance for this component
this.expand = false
},
methods: {
smooth() {
if (process.client) {
window.scrollTo(0,0)
const locoScroll = new this.LocomotiveScroll({
el: document.querySelector('.smooth-scroll'),
smooth: true
})
// each time Locomotive Scroll updates, tell ScrollTrigger to update too (sync positioning)
locoScroll.on('scroll', ScrollTrigger.update)
ScrollTrigger.scrollerProxy('.smooth-scroll', {
scrollTop(value) {
return arguments.length ? locoScroll.scrollTo(value, 0, 0) : locoScroll.scroll.instance.scroll.y
}, // we don't have to define a scrollLeft because we're only scrolling vertically.
getBoundingClientRect() {
return {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight}
},
// LocomotiveScroll handles things completely differently on mobile devices - it doesn't even transform the container at all! So to get the correct behavior and avoid jitters, we should pin things with position: fixed on mobile. We sense it by checking to see if there's a transform applied to the container (the LocomotiveScroll-controlled element).
pinType: document.querySelector('.smooth-scroll').style.transform ? 'transform' : 'fixed'
})
// each time the window updates, we should refresh ScrollTrigger and then update LocomotiveScroll.
ScrollTrigger.addEventListener('refresh', () => locoScroll.update())
// after everything is set up, refresh() ScrollTrigger and update LocomotiveScroll because padding may have been added for pinning, etc.
ScrollTrigger.refresh()
const headerItems = ['.header__logo', '.nav', '.header__trigger']
if (!this.isTouchDevice()) {
headerItems.forEach((item) => {
gsap.to(item, {
scrollTrigger: {
trigger: '.header',
scroller: '.smooth-scroll',
scrub: true,
pin: true,
start: 'top',
end: document.querySelector('body').offsetHeight,
pinSpacing: false,
},
y: document.querySelector('body').offsetHeight,
transformOrigin: 'center top',
ease: 'none'
})
})
const largeMedia = document.querySelectorAll('.large-media.no-controls')
if (largeMedia) {
largeMedia.forEach((media) => {
let mediaItem = media.querySelector('video')
if (media.querySelector('img')) {
mediaItem = media.querySelector('img')
}
gsap.to(mediaItem, {
scrollTrigger: {
trigger: media,
scroller: '.smooth-scroll',
scrub: true,
start: 'top',
end: 'bottom',
},
y: '100%',
transformOrigin: 'center top',
ease: 'none'
})
})
}
const nextCase = document.querySelector('.next-case')
if (nextCase) {
gsap.to('.next-case .large-media', {
scale: 1,
opacity: 0,
scrollTrigger: {
trigger: nextCase,
scroller: '.smooth-scroll',
start: `top-=${window.innerHeight / 2}`,
end: `bottom-=${window.innerHeight}`,
scrub: 1,
}
})
gsap.to('.next-case__background', {
opacity: 1,
scrollTrigger: {
trigger: nextCase,
scroller: '.smooth-scroll',
start: `top-=${window.innerHeight / 2}`,
end: `bottom-=${window.innerHeight}`,
scrub: 1,
}
})
gsap.to('.large-text-header', {
opacity: 1,
scrollTrigger: {
trigger: nextCase,
scroller: '.smooth-scroll',
start: `top-=${window.innerHeight / 2}`,
end: `bottom-=${window.innerHeight}`,
scrub: 1,
}
})
const observerTrigger = document.querySelector('.next-case__observer-trigger')
const onIntersection = (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
this.loaded = entry.intersectionRatio
if (entry.intersectionRatio > 0.95) {
this.background = true
if (!this.expand) {
// window.location.href = nextCase.querySelector('a').getAttribute('href') // goor, doch effectief
this.$router.push(nextCase.querySelector('a').getAttribute('href'))
this.expand = true
}
}
}
}
}
let threshold = [] // create array with numbers between 0 and 1
for (var i = 0; i <= 100; i++) {
threshold.push(i / 100)
}
const observer = new IntersectionObserver(onIntersection, { threshold })
observer.observe(observerTrigger)
}
}
}
},
isTouchDevice() {
try {
document.createEvent('TouchEvent')
return true
} catch (e) {
return false
}
}
},
}
</script>