Skip to content

Instantly share code, notes, and snippets.

@jesperlandberg
Last active February 15, 2020 20:35
Show Gist options
  • Select an option

  • Save jesperlandberg/f30fc32cb48425127c5df9804b457e2d to your computer and use it in GitHub Desktop.

Select an option

Save jesperlandberg/f30fc32cb48425127c5df9804b457e2d to your computer and use it in GitHub Desktop.
import gsap from 'gsap'
import VirtualScroll from 'virtual-scroll'
import Events from './Events'
// Raf
gsap.ticker.fps(-1)
gsap.ticker.add(tick)
function tick() {
Events.emit('tick')
}
// Scroll
const isWindows = (["Win32", "Win64", "Windows", "WinCE"].indexOf(window.navigator.platform) !== -1)
const vs = new VirtualScroll({
mouseMultiplier: isWindows ? 1.1 : 0.45,
touchMultiplier: 3,
firefoxMultiplier: isWindows ? 40 : 90,
passive: true
})
vs.on(scroll)
function scroll({ deltaY ) {
Events.on('scroll', { y: deltaY })
}
// Resize
window.addEventListener('resize', () => Events.emit('resize'))
import e from '@unseenco/e'
const Events = new e()
export default Events
import gsap from 'gsap'
import { Events } from '@/events'
export default class {
ui = {}
constructor(el = document.body) {
this.el = el
this.ui.elems = [...this.el.querySelectorAll('[data-smooth-item]')]
this.state = {
target: 0,
current: 0,
scroll: 0,
threshold: 100,
resize: false,
wh: window.innerHeight
}
this.init()
}
init() {
this.setBounds()
this.setElems()
this.addEvents()
}
addEvents() {
Events.on('scroll', this.scroll)
Events.on('tick', this.run)
Events.on('resize', this.resize)
}
setBounds() {
const { height } = this.el.getBoundingClientRect()
const state = this.state
state.max = height - state.wh
}
setElems() {
const { threshold, wh } = this.state
this.elems = this.ui.elems.map(el => {
el.style.transform = 'translate3d(0, 0, 0)'
const { top, bottom } = el.getBoundingClientRect()
return {
el,
start: top - wh - threshold,
end: bottom + threshold,
speed: parseFloat(el.dataset.smoothItem || 1)
}
})
}
resize = () => {
const state = this.state
state.resize = true
state.wh = window.innerHeight
this.setBounds()
this.setElems()
this.transformElems()
this.clamp()
state.scroll = state.current = state.target
state.resize = false
}
run = () => {
const state = this.state
state.current += (state.target - state.current) * 0.1
state.scroll = Math.round(state.current * 100) / 100
!state.resize && this.transformElems()
}
scroll = ({ y }) => {
this.state.target += y
this.clamp()
}
clamp() {
this.state.target = gsap.utils.clamp(0, this.state.max, this.state.target)
}
transformElems() {
this.elems.forEach(elem => {
const { visible, transform } = this.visible(elem)
if (visible || this.state.resize) {
elem.out && (elem.out = false)
elem.el.style.transform = `translate3d(${-transform}px, 0, 0)`
} else if (!elem.out) {
elem.out = true
elem.el.style.transform = `translate3d(${-transform}px, 0, 0)`
}
})
}
visible({ start, end, speed }) {
const transform = this.state.scroll * speed
const visible = (transform > start) && (transform < end)
return {
transform,
visible
}
}
removeEvents() {
Events.off('scroll', this.scroll)
Events.off('tick', this.run)
Events.off('resize', this.resize)
}
destroy() {
this.removeEvents()
this.ui = null
this.state = null
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment