Skip to content

Instantly share code, notes, and snippets.

@baptistebriel
Created February 17, 2018 01:23
Show Gist options
  • Save baptistebriel/e5646f7e6a68ef9b281dabc367349b76 to your computer and use it in GitHub Desktop.
Save baptistebriel/e5646f7e6a68ef9b281dabc367349b76 to your computer and use it in GitHub Desktop.
import _ from 'underscore'
import App from 'lib/app'
import size from 'size'
import Mn from 'backbone.marionette'
import TweenMax from 'TweenMax'
import VirtualScroll from 'virtual-scroll'
import prefix from 'vendor-prefix'
import template from './template.html'
import { props } from 'lib/decorators'
const transform = prefix('transform')
@props({
template,
className: 'VirtualScroll'
})
export default class ScrollView extends Mn.View {
initialize() {
_.bindAll(this, 'event', 'rAF')
this.cache = null
this.data = {
scrollable: true,
locked: false,
current: 0,
target: 0,
fixed: 0
}
}
onRender() {
this.addScroll()
}
addScroll () {
this.vs = new VirtualScroll()
this.vs.on(this.event, this)
TweenMax.ticker.addEventListener('tick', this.rAF, this)
}
event (e) {
if (this.data.locked || this.dead) return
this.data.target += Math.round(e.deltaY * -0.5)
this.clamp()
}
clamp () {
this.data.target = Math.round(Math.min(Math.max(this.data.target, 0), this.data.height))
}
scrollTo (y, lock = false) {
if (lock) {
this.data.locked = true
}
this.data.target = y
this.clamp()
}
rAF () {
const el = this.ui.section[0]
if (!el.style) return
const scrollY = this.data.current
const scrolling = this.data.target.toFixed() !== this.data.current.toFixed()
const fixed = this.data.fixed = scrolling ? 3 : 0
const translateY = this.data.current.toFixed(fixed)
this.data.current += (this.data.target - this.data.current) * .15
el.style[transform] = `translate3d(0,-${translateY}px,0)`
if (!this.cache) return
const threshold = this.options.threshold || 150
this.cache.forEach((data, index) => {
const el = this.ui.els[index]
const top = data.top - scrollY
const bottom = data.bottom - scrollY
const inview = top < (size.height + threshold) && bottom > -threshold
const direction = top < 0 ? 'top' : 'bottom'
if (!data.state && (top > -threshold) && (bottom < (size.height + threshold))) return
inview ? this.inView(el, data, direction) : this.outView(el, data, direction)
})
}
inView (el, data, direction) {
if (data.state) return
data.state = true
const unit = size.height / 2
const duration = Math.random() * (1.3 - 0.5) + 0.5
const delay = Math.random() * (0.1 - 0) + 0
TweenMax.fromTo(el, .2, {
opacity: 0,
}, {
opacity: 1,
ease: Linear.easeNone
})
TweenMax.fromTo(el, duration, {
scale: 1.1,
y: unit * (direction === 'top' ? -1 : 1),
rotationX: direction === 'top' ? -8 : 8
}, {
scale: 1,
y: 0,
rotationX: 0,
ease: Expo.easeOut,
delay: delay
})
}
outView (el, data, direction) {
if (!data.state) return
data.state = false
}
onResize(width, height) {
const scrollY = Math.round(this.data.target)
const bounding = this.ui.bounding[0].getBoundingClientRect()
if (this.ui.els && !App.isIE && !App.isFirefox) {
this.cache = []
this.ui.els.each((index, el) => {
const bounds = el.getBoundingClientRect()
const top = bounds.top + scrollY
const bottom = bounds.bottom + scrollY
this.cache.push({
state: top < size.height && bottom > 0,
top: top,
bottom: bottom
})
})
}
this.data.height = (scrollY + bounding.top + bounding.height) - height
}
onBeforeDestroy() {
this.vs.off(this.event, this)
this.vs = null
TweenMax.ticker.removeEventListener('tick', this.rAF, this)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment