-
-
Save Efetivos/76f543098e5b10039eeccf22159d1052 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 imagesLoaded from 'imagesloaded' | |
| import TweenMax from 'gsap' | |
| export default class Smooth { | |
| constructor(options = {}) { | |
| this.bindAll() | |
| TweenMax.defaultEase = Linear.easeNone | |
| this.el = options.el || document.body | |
| const { | |
| sections = this.el.querySelectorAll('[data-smooth-section]'), | |
| elems = this.el.querySelectorAll('[data-from]'), | |
| threshold = 200, | |
| ease = 0.125, | |
| mouseMultiplier = 0.5, | |
| touchMultiplier = 2.5, | |
| firefoxMultiplier = 90, | |
| preventTouch = true, | |
| passive = false | |
| } = options | |
| this.dom = { | |
| el: this.el, | |
| sections: sections, | |
| elems: elems | |
| } | |
| this.state = { | |
| resizing: false, | |
| locked: false | |
| } | |
| this.data = { | |
| threshold: threshold, | |
| ease: ease, | |
| current: 0, | |
| last: 0, | |
| target: 0, | |
| bounding: 0, | |
| height: 0, | |
| max: 0, | |
| phone: window.matchMedia("(max-width: 640px)").matches | |
| } | |
| this.vs = new VirtualScroll({ | |
| el: this.el, | |
| mouseMultiplier: mouseMultiplier, | |
| touchMultiplier: touchMultiplier, | |
| firefoxMultiplier: firefoxMultiplier, | |
| preventTouch: preventTouch, | |
| passive: passive | |
| }) | |
| this.init() | |
| } | |
| bindAll() { | |
| ['run', 'event', 'resize'] | |
| .forEach(fn => this[fn] = this[fn].bind(this)) | |
| } | |
| init() { | |
| this.on() | |
| } | |
| on() { | |
| this.dom.el.classList.add('is-virtual-scroll') | |
| this.setStyles() | |
| this.getCache() | |
| this.getBounding() | |
| this.addListeners() | |
| this.preload() | |
| } | |
| setStyles() { | |
| this.dom.el.style.position = 'fixed' | |
| this.dom.el.style.top = 0 | |
| this.dom.el.style.left = 0 | |
| this.dom.el.style.width = '100%' | |
| } | |
| event(e) { | |
| this.data.target += Math.round(e.deltaY * -1) | |
| this.clamp() | |
| } | |
| clamp() { | |
| this.data.target = Math.round(Math.min(Math.max(this.data.target, 0), this.data.max)) | |
| } | |
| run() { | |
| if (this.state.resizing) return | |
| this.data.current += (this.data.target - this.data.current) * this.data.ease | |
| this.transformSections() | |
| this.animateElems() | |
| this.data.last = this.data.current | |
| } | |
| transformSections() { | |
| if (!this.sections) return | |
| const current = this.data.current | |
| const translate = this.data.current.toFixed(2) | |
| this.sections.forEach((data, index) => { | |
| const translate3d = `translate3d(0, ${-translate}px, 0)` | |
| const { isVisible } = this.isVisible(data) | |
| if (isVisible || this.state.resizing) this.dom.sections[index].style.transform = translate3d | |
| }) | |
| } | |
| animateElems() { | |
| if (!this.elems) return | |
| this.elems.forEach((data, index) => { | |
| const { isVisible, start, end } = this.isVisible(data, 0.01) | |
| if (isVisible) { | |
| this.intersectRatio(data, start, end) | |
| data.tl.progress(data.progress.current) | |
| } | |
| }) | |
| } | |
| intersectRatio(data, top, bottom) { | |
| const start = top - this.data.height | |
| const end = (this.data.height + bottom + data.height) * data.duration | |
| data.progress.current = Math.abs(start / end) | |
| data.progress.current = Math.max(0, Math.min(1, data.progress.current)) | |
| } | |
| isVisible(bounds, offset) { | |
| const current = this.data.current | |
| const threshold = !offset ? this.data.threshold : offset | |
| const start = bounds.top - current | |
| const end = bounds.bottom - current | |
| const isVisible = start < (threshold + this.data.height) && end > -threshold | |
| return { | |
| isVisible, | |
| start, | |
| end | |
| } | |
| } | |
| getCache() { | |
| this.getSections() | |
| this.getElems() | |
| } | |
| getSections() { | |
| if (!this.dom.sections) return | |
| this.sections = [] | |
| this.dom.sections.forEach((el) => { | |
| el.style.transform = '' | |
| const bounds = el.getBoundingClientRect() | |
| this.sections.push({ | |
| top: bounds.top, | |
| bottom: bounds.bottom | |
| }) | |
| }) | |
| } | |
| getElems() { | |
| if (!this.dom.elems) return | |
| this.elems = [] | |
| this.dom.elems.forEach(el => { | |
| if (el.dataset.animateMobile === undefined && this.data.phone) return | |
| const bounds = el.getBoundingClientRect() | |
| const tl = new TimelineLite({ paused: true }) | |
| const from = JSON.parse(el.dataset.from) | |
| const to = JSON.parse(el.dataset.to) | |
| tl.fromTo(el, 1, from, to) | |
| tl.progress(1) | |
| const boundsUpdated = el.getBoundingClientRect() | |
| tl.progress(0) | |
| this.elems.push({ | |
| el: el, | |
| tl: tl, | |
| top: bounds.top > this.data.height ? bounds.top : this.data.height, | |
| bottom: boundsUpdated.bottom, | |
| height: boundsUpdated.bottom - bounds.top, | |
| duration: el.dataset.duration ? el.dataset.duration : 1, | |
| progress: { | |
| current: 0 | |
| } | |
| }) | |
| }) | |
| } | |
| getBounding() { | |
| const bounding = this.dom.el.getBoundingClientRect() | |
| this.data.height = window.innerHeight | |
| this.data.bounding = bounding | |
| this.data.max = bounding.height - this.data.height | |
| } | |
| preload() { | |
| imagesLoaded(this.dom.el, (instance) => { | |
| this.resize() | |
| }) | |
| } | |
| resize() { | |
| this.state.resizing = true | |
| this.data.phone = window.matchMedia("(max-width: 640px)").matches | |
| this.getCache() | |
| this.transformSections() | |
| this.getBounding() | |
| this.state.resizing = false | |
| } | |
| destroy() { | |
| this.removeListeners() | |
| this.vs = null | |
| this.dom = null | |
| this.data = null | |
| this.elems = null | |
| this.sections = null | |
| } | |
| removeListeners() { | |
| this.vs.off(this.event) | |
| TweenMax.ticker.removeEventListener('tick', this.run) | |
| window.removeEventListener('resize', this.resize) | |
| } | |
| addListeners() { | |
| this.vs.on(this.event) | |
| TweenMax.ticker.addEventListener('tick', this.run) | |
| window.addEventListener('resize', this.resize) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment