Skip to content

Instantly share code, notes, and snippets.

@jesperlandberg
Created October 15, 2021 10:14
Show Gist options
  • Save jesperlandberg/5c4e85fe76a32bf4b67224b16c7ac481 to your computer and use it in GitHub Desktop.
Save jesperlandberg/5c4e85fe76a32bf4b67224b16c7ac481 to your computer and use it in GitHub Desktop.
import VirtualScroll from 'virtual-scroll'
import gsap from 'gsap'
const bounds = {
ww: window.innerWidth,
wh: window.innerHeight
}
export default class {
constructor() {
this.elems = [...document.querySelectorAll('.js-item')]
this.state = {
target: 0,
current: 0,
diff: 0,
a: {
min: 0, max: 0,
current: 0
},
b: {
min: 0, max: 0,
current: 0,
},
movement: 0,
resizing: false
}
this.setCache()
this.addEvents()
}
addEvents() {
this.vs = new VirtualScroll()
this.vs.on(this.scroll)
gsap.ticker.fps(-1)
gsap.ticker.add(this.tick)
}
setCache() {
const { wh } = bounds
const { a, b } = this.state
const last = this.elems.length - 1
this.cache = this.elems.map((el, i) => {
el.style.transform = 'translate3d(0, 0, 0)'
const { top, bottom } = el.getBoundingClientRect()
if (i === last) {
const max = bottom - wh
a.min = -wh
a.max = max
b.min = 0
b.max = max + wh
}
return {
el, start, end,
current: top < wh ? 'a' : 'b',
start: top - wh,
end: bottom,
out: true
}
})
}
scroll = ({ deltaY }) => {
this.state.target -= deltaY
}
tick = () => {
const state = this.state
const { a, b, resizing } = state
if (!a || !b) return
state.movement += 1
state.current += (state.target - state.current) * 0.1
state.current = Math.round(state.current * 100) / 100
state.diff = (state.target - state.current) * 0.01
a.current = gsap.utils.wrap(a.min, a.max, state.current + state.movement)
b.current = gsap.utils.wrap(b.min, b.max, state.current + state.movement)
!resizing && this.transformElements()
}
transformElements() {
const state = this.state
this.cache.length > 0 &&
this.cache.forEach(c => {
const current = state[c.current].current
const visible = this.visible(c, current)
if (visible || state.resizing) {
c.out && (c.out = false)
this.transform(c.el, current)
} else if (!c.out) {
c.out = true
this.transform(c.el, current)
}
})
}
visible({ start, end }, current) {
return current > start && current < end
}
transform(el, current) {
el.style.transform = `translate3d(0, ${-current}px, 0)`
}
resize = () => {
const state = this.state
state.resizing = true
this.setCache()
state.current = state.target
this.transformElements()
state.resizing = false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment