Skip to content

Instantly share code, notes, and snippets.

@johndigital
Created October 17, 2019 12:47
Show Gist options
  • Save johndigital/32b4c1b80ce12180c10f6d487f53cc8f to your computer and use it in GitHub Desktop.
Save johndigital/32b4c1b80ce12180c10f6d487f53cc8f to your computer and use it in GitHub Desktop.
<template>
<div class="scrolling-layout">
<!-- This is just an 800vh spacer,
it ensures the page is tall
enough to scroll -->
<div class="spacer" />
<!-- This is the actual content that gets scrolled.
It is position fixed and the "scrolling" is done
with transforms -->
<transition name="fade">
<div v-show="ready" class="slider" :style="sliderStyle">
<flex-layout :layout="layout" />
<flex-layout :layout="layout" />
<flex-layout :layout="layout" />
</div>
</transition>
</div>
</template>
<script>
import { everyFrame, transform, value, listen } from 'popmotion'
const { wrap, clamp, smooth } = transform
const wrapper = wrap(0, 1)
const clamper = clamp(-1, 1)
let scrollPos
export default {
data() {
return {
ready: false,
scrollListener: null,
tickAction: null,
direction: 1,
looped: 0,
yPos: 0,
sTop: 0
}
},
async mounted() {
// Set up scroll listener to keep track of window scrollTop
this.scrollListener = listen(window, 'scroll').start(() => {
this.sTop = window.pageYOffset || document.documentElement.scrollTop
})
// Kick the endless scroll function
// (looping back to middle using window.scrollTo)
this.startEndlessScroll()
// allow the page to render
await this.$nextTick()
// Start tracking the velocity of the scrollDist,
// which is just a measurement of how many pixels the
// user has scrolled in total including the loops
scrollPos = value(this.scrollDist)
// Start an animation tick every frame
this.tickAction = everyFrame()
.pipe(
() => {
// Feed the overall scrolling distance
// into a smoothing fucntion every tick
return this.scrollDist
},
// This number controls how smoothed
// out the scroll motion is.
smooth(150)
)
.start(scrollDist => {
// Update the scroll distance tracker with the smoothed
// value so we can track velocity
scrollPos.update(scrollDist)
// Convert velocity into a useful multiplier value
// that we can use to actually move the page
const vel = scrollPos.getVelocity() * 0.06
// Ensure it is clamped between -1 and 1
const mult = clamper(vel / 1000)
// Keep track of direction change.
// At this point if mult is negative the user
// is scrolling up and vice versa
if (this.direction === 1 && mult < -0.01) {
this.direction = -1
} else if (this.direction === -1 && mult > 0.01) {
this.direction = 1
}
// Apply movement.
// yPos is a fraction between 0 and 1 that represents how
// scrolled the page is with 1 being 100%
const movement = 0.0004 * this.direction + 0.03 * mult
this.yPos = wrapper(this.yPos + movement)
})
this.ready = true
},
beforeDestroy() {
// Tear down all listeners when we move off the page
if (this.scrollListener) this.scrollListener.stop()
if (this.tickAction) this.tickAction.stop()
window.removeEventListener('scroll', this.trackScrollJump)
},
computed: {
page() {
return _get(this.$store.state, 'pageData.frontpage')
},
layout() {
return _get(this.page, 'acf.layout') || []
},
midPoint() {
// This keeps track of where the middle of the document is
const { docHeight, winHeight } = this.$store.state.browser
return docHeight / 2 - winHeight / 2
},
sliderStyle() {
return {
transform: `translateY(-${33.33 + 33.33 * this.yPos}%)`
}
},
scrollDist() {
return this.sTop - this.midPoint + this.looped * this.midPoint
}
},
methods: {
startEndlessScroll() {
// Set scroll to middle of page
this.scrollToMiddle()
window.addEventListener('scroll', this.trackScrollJump)
},
trackScrollJump() {
const { docHeight, winHeight } = this.$store.state.browser
if (this.sTop <= 100) {
this.scrollToMiddle()
this.looped--
} else if (this.sTop >= docHeight - winHeight - 100) {
this.scrollToMiddle()
this.looped++
}
},
scrollToMiddle() {
window.scrollTo(0, this.midPoint)
this.sTop = this.midPoint
}
}
}
</script>
<style lang="scss">
.scrolling-layout {
.slider {
position: fixed;
right: 0;
left: 0;
top: 0;
}
.spacer {
height: 800vh;
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment