Created
May 10, 2019 08:25
-
-
Save jesperlandberg/22eb6b08837a19d5dc3042e793546fe4 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 sniffer from 'sniffer' | |
| import GlobalRAF from './utils/GlobalRAF' | |
| import Smooth from './scripts/Smooth' | |
| class App { | |
| constructor() { | |
| if (sniffer.isDesktop) { | |
| GlobalRAF.update() | |
| new Smooth() | |
| } | |
| } | |
| } | |
| new App() |
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
| export default { | |
| html: document.documentElement, | |
| body: document.body, | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| container: document.querySelector('.js-smooth'), | |
| docHeight: 0, | |
| } |
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 Emitter from 'tiny-emitter'; | |
| export default new Emitter(); |
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 sniffer from 'sniffer'; | |
| import preload from './preload'; | |
| import config from '../config'; | |
| import EventBus from './EventBus'; | |
| import { Events as ScrollControllerEvents } from './ScrollController'; | |
| import { Events as GlobalResizeEvents } from './GlobalResize'; | |
| class GlobalRAF { | |
| constructor() { | |
| TweenMax.ticker.addEventListener('tick', this.tick); | |
| this.data = { | |
| ease: 0.125, | |
| }; | |
| this.scroll = { | |
| target: 0, | |
| current: 0, | |
| }; | |
| this.addListeners(); | |
| } | |
| setMaxHeight = () => { | |
| Object.assign(config, { | |
| docHeight: config.container.getBoundingClientRect().height - window.innerHeight, | |
| }); | |
| } | |
| tick = () => { | |
| if (!sniffer.isDevice) { | |
| this.scroll.current += (this.scroll.target - this.scroll.current) * this.data.ease; | |
| } else { | |
| this.scroll.current = this.scroll.target; | |
| } | |
| EventBus.emit(GlobalRAF.events.TICK, { | |
| target: this.scroll.target, | |
| smooth: this.scroll.current, | |
| }); | |
| } | |
| event = ({ y }) => { | |
| if (!sniffer.isDevice) { | |
| this.scroll.target += y; | |
| this.clampTarget(); | |
| } else { | |
| this.scroll.target = y; | |
| } | |
| } | |
| clampTarget() { | |
| this.scroll.target = Math.round(Math.min(Math.max(this.scroll.target, 0), config.docHeight)); | |
| } | |
| onResize = () => { | |
| Object.assign(config, { width: window.innerWidth, height: window.innerHeight }); | |
| if (!sniffer.isDevice) { | |
| this.setMaxHeight(); | |
| this.clampTarget(); | |
| } | |
| } | |
| update = () => { | |
| this.scroll.current = 0; | |
| this.scroll.target = 0; | |
| this.setMaxHeight(); | |
| preload(this.setMaxHeight); | |
| } | |
| addListeners() { | |
| EventBus.on(ScrollControllerEvents.SCROLL, this.event); | |
| 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 debounce from 'lodash.debounce'; | |
| import EventBus from './EventBus'; | |
| class GlobalResize { | |
| constructor() { | |
| window.addEventListener('resize', debounce(this.onResize, 200)); | |
| } | |
| onResize = () => { | |
| EventBus.emit(GlobalResize.events.RESIZE); | |
| } | |
| } | |
| GlobalResize.events = { | |
| RESIZE: 'GlobalResize.events.RESIZE', | |
| }; | |
| export default new GlobalResize(); | |
| export const Events = GlobalResize.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
| function preload(callback) { | |
| const images = [].slice.call(document.querySelectorAll('img'), 0); | |
| images.forEach((image) => { | |
| const img = document.createElement('img'); | |
| img.addEventListener('load', () => { | |
| images.splice(images.indexOf(image), 1); | |
| if (images.length === 0) callback(); | |
| }); | |
| img.src = image.src; | |
| }); | |
| } | |
| export default preload; |
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 VirtualScroll from 'virtual-scroll'; | |
| import sniffer from 'sniffer'; | |
| import EventBus from './EventBus'; | |
| class ScrollController { | |
| constructor() { | |
| this.setup(); | |
| } | |
| setup() { | |
| if (sniffer.isDevice) { | |
| window.addEventListener('scroll', this.onNativeScroll, { passive: true }); | |
| } else { | |
| this.vs = new VirtualScroll({ | |
| limitInertia: false, | |
| mouseMultiplier: 0.4, | |
| touchMultiplier: 3, | |
| firefoxMultiplier: 90, | |
| passive: true, | |
| }); | |
| this.vs.on(this.onScroll); | |
| } | |
| } | |
| onScroll = (e) => { | |
| EventBus.emit(ScrollController.events.SCROLL, { | |
| y: Math.round(e.deltaY * -1), | |
| }); | |
| } | |
| onNativeScroll = () => { | |
| EventBus.emit(ScrollController.events.SCROLL, { | |
| y: window.scrollY, | |
| }); | |
| } | |
| } | |
| ScrollController.events = { | |
| SCROLL: 'ScrollController.events.SCROLL', | |
| }; | |
| export default new ScrollController(); | |
| export const Events = ScrollController.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 config from '../config' | |
| 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 = config.container | |
| this.dom = { | |
| el: this.el, | |
| sections: document.querySelectorAll('.js-smooth-section') | |
| } | |
| this.state = { | |
| current: 0, | |
| target: 0, | |
| threshold: 100, | |
| isResizing: false, | |
| toggled: false | |
| } | |
| this.init() | |
| } | |
| init() { | |
| this.on() | |
| } | |
| on() { | |
| this.setStyles() | |
| this.getCache() | |
| this.addListeners() | |
| preload(this.onResize) | |
| } | |
| setStyles() { | |
| config.body.classList.add('is-virtual-scroll') | |
| Object.assign(this.dom.el.style, { | |
| position: 'fixed', | |
| top: 0, | |
| left: 0, | |
| width: '100%', | |
| }) | |
| } | |
| run = ({ smooth }) => { | |
| this.state.current = smooth | |
| this.transformSections() | |
| } | |
| transformSections() { | |
| const { current, isResizing } = this.state | |
| const translate3d = `translate3d(0, ${-current.toFixed(2)}px, 0)` | |
| this.sections.forEach((data) => { | |
| const { el, bounds } = data | |
| const isVisible = this.isVisible(bounds) | |
| if (isVisible || isResizing) { | |
| Object.assign(data, { out: false }) | |
| el.style.transform = translate3d | |
| } else if (!data.out) { | |
| Object.assign(data, { out: true }) | |
| el.style.transform = translate3d | |
| } | |
| }) | |
| } | |
| isVisible(bounds) { | |
| const { current, threshold } = this.state | |
| const { top, bottom } = bounds | |
| const start = top - current | |
| const end = bottom - current | |
| const isVisible = start < (threshold + config.height) && end > -threshold | |
| return isVisible | |
| } | |
| getCache() { | |
| this.getSections() | |
| } | |
| getSections() { | |
| if (!this.dom.sections) return | |
| this.sections = [] | |
| this.dom.sections.forEach((el) => { | |
| Object.assign(el.style, { transform: '' }) | |
| const { top, bottom } = el.getBoundingClientRect() | |
| const state = { | |
| el, | |
| bounds: { | |
| top, | |
| bottom, | |
| }, | |
| out: true, | |
| } | |
| this.sections.push(state) | |
| }) | |
| } | |
| onResize = () => { | |
| this.state.isResizing = true | |
| if (this.sections) { | |
| this.sections.forEach((section) => { | |
| section.el.style.transform = '' | |
| const bounds = section.el.getBoundingClientRect() | |
| section.bounds.top = bounds.top | |
| section.bounds.bottom = bounds.bottom | |
| }) | |
| this.transformSections() | |
| } | |
| this.state.isResizing = false | |
| } | |
| addListeners() { | |
| EventBus.on(GlobalRAFEvents.TICK, this.run) | |
| EventBus.on(GlobalResizeEvents.RESIZE, this.onResize) | |
| } | |
| } | |
| export default Smooth |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment