Last active
August 10, 2019 22:36
-
-
Save jesperlandberg/e636e4d2cf4412b7eadce9059f4650fe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import TweenMax from 'gsap' | |
| import store from '../store' | |
| import preload from './preload' | |
| import math from './math' | |
| import Pointer from './Pointer' | |
| import VS from './VS' | |
| import EventBus from './EventBus' | |
| import { Events as GlobalResizeEvents } from './GlobalResize' | |
| class GlobalRAF { | |
| constructor() { | |
| TweenMax.ticker.addEventListener('tick', this.tick) | |
| this.state = { | |
| target: 0, | |
| current: 0, | |
| ease: 0.1 | |
| } | |
| this.addListeners() | |
| } | |
| tick = () => { | |
| const state = this.state | |
| if (store.isSmooth) { | |
| state.current = math.lerp(state.current, state.target, state.ease) | |
| if (state.current < .1) { | |
| state.current = 0 | |
| } | |
| } else { | |
| state.current = state.target | |
| } | |
| EventBus.emit(GlobalRAF.events.TICK, { | |
| target: state.target, | |
| current: state.current, | |
| }) | |
| } | |
| clampTarget() { | |
| this.state.target = Math.min(Math.max(this.state.target, 0), store.scrollHeight) | |
| } | |
| onEvent = (e) => { | |
| Pointer.run() | |
| this.state.target += e.deltaY * -1 | |
| this.clampTarget() | |
| } | |
| onScroll = () => { | |
| this.state.target = window.scrollY | |
| } | |
| addListeners() { | |
| if (store.isSmooth) { | |
| this.vs = new VS() | |
| this.vs.on(this.onEvent) | |
| } else { | |
| window.addEventListener('scroll', this.onScroll, { passive: true }) | |
| } | |
| EventBus.on(GlobalResizeEvents.RESIZE, this.onResize) | |
| } | |
| } | |
| GlobalRAF.events = { | |
| TICK: 'TICK', | |
| } | |
| export default new GlobalRAF() | |
| export const Events = GlobalRAF.events |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import store from '../store' | |
| import preload from '../utils/preload' | |
| import EventBus from '../utils/EventBus' | |
| import { Events as GlobalRAFEvents } from '../utils/GlobalRAF' | |
| import { Events as GlobalResizeEvents } from '../utils/GlobalResize' | |
| class Smooth { | |
| constructor() { | |
| this.el = store.scrollEl | |
| this.ui = { | |
| sections: document.querySelectorAll('.js-smooth-section') | |
| } | |
| this.state = { | |
| total: this.ui.sections.length, | |
| current: 0, | |
| target: 0, | |
| threshold: 100, | |
| isResizing: false | |
| } | |
| this.init() | |
| } | |
| init() { | |
| this.on() | |
| } | |
| on() { | |
| this.setStyles() | |
| this.getCache() | |
| this.addListeners() | |
| preload(this.onResize) | |
| } | |
| setStyles() { | |
| store.body.classList.add('is-virtual-scroll') | |
| } | |
| run = ({ current }) => { | |
| this.state.current = current | |
| this.transformSections() | |
| } | |
| transformSections() { | |
| const { total, current, isResizing } = this.state | |
| for (let i = 0; i < total; i++) { | |
| const data = this.sections[i] | |
| const { el, bounds, speed } = data | |
| const { isVisible, transform } = this.isVisible(bounds, speed) | |
| if (isVisible || isResizing) { | |
| Object.assign(data, { out: false }) | |
| el.style.transform = `translate3d(0, ${-transform}px, 0)` | |
| } else if (!data.out) { | |
| Object.assign(data, { out: true }) | |
| el.style.transform = `translate3d(0, ${-transform}px, 0)` | |
| } | |
| } | |
| } | |
| isVisible({ top, bottom, offset, parallaxOffset }, speed) { | |
| const { current, threshold } = this.state | |
| const translate = current * speed | |
| const transform = translate - parallaxOffset | |
| const start = (top + offset) - translate | |
| const end = (bottom + offset) - translate | |
| const isVisible = start < (threshold + store.height) && end > -threshold | |
| return { | |
| isVisible, | |
| transform | |
| } | |
| } | |
| getCache() { | |
| this.getSections() | |
| } | |
| getSections() { | |
| if (!this.ui.sections) return | |
| this.sections = [] | |
| this.ui.sections.forEach((el) => { | |
| el.style.transform = 'translate3d(0, 0, 0)' | |
| const speed = el.dataset.speed || 1 | |
| const { top, bottom, height } = el.getBoundingClientRect() | |
| const centering = ((store.height / 2) - (height / 2)) | |
| const parallaxOffset = top < store.height ? 0 : ((top - centering) * speed) - (top - centering) | |
| const offset = (this.state.current * speed) + parallaxOffset | |
| const state = { | |
| el, | |
| bounds: { | |
| top, | |
| bottom, | |
| offset, | |
| parallaxOffset | |
| }, | |
| speed, | |
| out: true, | |
| } | |
| this.sections.push(state) | |
| }) | |
| } | |
| onResize = () => { | |
| this.state.isResizing = true | |
| if (this.sections) { | |
| this.sections.forEach(({ el, bounds, speed }) => { | |
| el.style.transform = 'translate3d(0, 0, 0)' | |
| const { top, bottom, height } = el.getBoundingClientRect() | |
| const centering = ((store.height / 2) - (height / 2)) | |
| const parallaxOffset = top < store.height ? 0 : ((top - centering) * speed) - (top - centering) | |
| const offset = (this.state.current * speed) + parallaxOffset | |
| Object.assign(bounds, { | |
| top, | |
| bottom, | |
| parallaxOffset, | |
| offset | |
| }) | |
| }) | |
| this.transformSections() | |
| } | |
| this.state.isResizing = false | |
| } | |
| addListeners() { | |
| EventBus.on(GlobalRAFEvents.TICK, this.run) | |
| EventBus.on(GlobalResizeEvents.RESIZE, this.onResize) | |
| } | |
| removeListeners() { | |
| EventBus.off(GlobalRAFEvents.TICK, this.run) | |
| EventBus.off(GlobalResizeEvents.RESIZE, this.onResize) | |
| } | |
| destroy() { | |
| this.removeListeners() | |
| this.ui = null | |
| this.state = null | |
| this.sections = null | |
| } | |
| } | |
| export default Smooth |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import store from '../store' | |
| import EventBus from './EventBus' | |
| const event_id = 'vs' | |
| class VS { | |
| constructor(opts = {}) { | |
| this.el = window | |
| this.opts = Object.assign({ | |
| mouseMultiplier: (["Win32", "Win64", "Windows", "WinCE"].indexOf(window.navigator.platform) !== -1) ? 0.7 : 0.45, | |
| touchMultiplier: 3, | |
| firefoxMultiplier: 90 | |
| }, opts) | |
| this.event = { | |
| y: 0, | |
| deltaY: 0 | |
| } | |
| this.touchY = null | |
| } | |
| emit(e) { | |
| const event = this.event | |
| event.y += event.deltaY | |
| EventBus.emit(event_id, { | |
| y: event.y, | |
| deltaY: event.deltaY, | |
| originalEvent: e | |
| }) | |
| } | |
| onWheel = (e) => { | |
| const { firefoxMultiplier, mouseMultiplier } = this.opts | |
| const event = this.event | |
| event.deltaY = e.wheelDeltaY || e.deltaY * -1 | |
| if (store.isFirefox && e.deltaMode == 1) { | |
| event.deltaY *= firefoxMultiplier | |
| } | |
| event.deltaY *= mouseMultiplier | |
| this.emit(e) | |
| } | |
| onTouchStart = (e) => { | |
| const touch = (e.targetTouches) ? e.targetTouches[0] : e | |
| this.touchY = touch.pageY | |
| } | |
| onTouchMove = (e) => { | |
| const { touchMultiplier } = this.options | |
| const event = this.event | |
| const touch = (e.targetTouches) ? e.targetTouches[0] : e | |
| event.deltaY = (touch.pageY - this.touchY) * touchMultiplier | |
| this.touchY = touch.pageY | |
| this.emit(e) | |
| } | |
| addListeners() { | |
| if (store.hasWheelEvent) { | |
| this.el.addEventListener('wheel', this.onWheel, { passive: true }) | |
| } | |
| } | |
| removeListeners() { | |
| if (store.hasWheelEvent) { | |
| this.el.removeEventListener('wheel', this.onWheel, { passive: true }) | |
| } | |
| } | |
| on(callback) { | |
| EventBus.on(event_id, callback) | |
| this.addListeners() | |
| } | |
| off(callback) { | |
| EventBus.off(event_id, callback) | |
| this.removeListeners() | |
| } | |
| } | |
| export default VS |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment