├── .gitignore ├── demos ├── performances │ ├── index.js │ ├── custom.js │ ├── index.html │ └── build.js ├── scale │ ├── index.js │ ├── index.html │ ├── custom.js │ └── build.js ├── opacity │ ├── index.js │ ├── index.html │ └── custom.js ├── parallax-page │ ├── index.js │ ├── index.html │ └── custom.js ├── split │ ├── index.js │ ├── custom.js │ └── index.html ├── native-horizontal │ ├── index.js │ ├── index.html │ └── custom.js ├── horizontal │ ├── index.js │ ├── index.html │ └── custom.js ├── parallax │ ├── index.js │ ├── index.html │ └── custom.js ├── main.css ├── callback │ └── index.html ├── native-scrollbar │ └── index.html └── gsap │ └── index.html ├── LICENSE ├── package.json ├── README.md ├── index.js └── smooth-scrolling.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store -------------------------------------------------------------------------------- /demos/performances/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | extends: true, 5 | section: document.querySelector('.vs-section') 6 | }) 7 | 8 | scroll.init() -------------------------------------------------------------------------------- /demos/scale/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const image = document.querySelector('img'); 4 | const scroll = new Custom({ 5 | extends: true, 6 | img: image 7 | }) 8 | 9 | scroll.init() -------------------------------------------------------------------------------- /demos/opacity/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | extends: true, 5 | section: document.querySelector('.vs-section'), 6 | opacity: document.querySelector('h1') 7 | }) 8 | 9 | scroll.init() -------------------------------------------------------------------------------- /demos/parallax-page/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | preload: true, 5 | native: true, 6 | section: document.querySelector('.vs-section'), 7 | divs: document.querySelectorAll('.vs-div') 8 | }) 9 | 10 | scroll.init() -------------------------------------------------------------------------------- /demos/split/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | extends: true, 5 | native: true, 6 | section: document.querySelector('.vs-sections'), 7 | sections: document.querySelectorAll('.vs-split') 8 | }) 9 | 10 | scroll.init() -------------------------------------------------------------------------------- /demos/native-horizontal/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | preload: false, 5 | native: true, 6 | direction: 'vertical', 7 | section: document.querySelector('.vs-section'), 8 | divs: document.querySelectorAll('.vs-div') 9 | }) 10 | 11 | scroll.init() -------------------------------------------------------------------------------- /demos/horizontal/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | preload: false, 5 | native: false, 6 | direction: 'horizontal', 7 | section: document.querySelector('.vs-section'), 8 | divs: document.querySelectorAll('.vs-div') 9 | }) 10 | 11 | scroll.init() -------------------------------------------------------------------------------- /demos/parallax/index.js: -------------------------------------------------------------------------------- 1 | import Custom from './custom' 2 | 3 | const scroll = new Custom({ 4 | extends: true, 5 | preload: true, 6 | noscrollbar: true, 7 | section: document.querySelector('.vs-section'), 8 | divs: document.querySelectorAll('.vs-div') 9 | }) 10 | 11 | scroll.init() 12 | 13 | // setTimeout(() => { 14 | // scroll.destroy() 15 | // }, 1500) -------------------------------------------------------------------------------- /demos/scale/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scale demo | smooth-scrolling 7 | 8 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demos/scale/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Custom extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.dom.img = opt.img 8 | } 9 | 10 | init() { 11 | super.init() 12 | } 13 | 14 | run() { 15 | super.run() 16 | const current = Math.round(Math.abs(this.vars.current)); 17 | const scale = Math.max(0.8, Math.min(0.8 + current / window.innerHeight * 1.5, 10)) 18 | this.dom.img.style[this.prefix] = `scale3d(${scale},${scale},${scale})`; 19 | } 20 | 21 | resize() { 22 | this.vars.bounding = window.innerHeight * 1.5 23 | super.resize() 24 | } 25 | } 26 | 27 | export default Custom -------------------------------------------------------------------------------- /demos/opacity/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | opacity demo | smooth-scrolling 7 | 8 | 23 | 24 | 25 |
26 |

Opacity

27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /demos/performances/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Custom extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.perfs = { 8 | now: null, 9 | last: null 10 | } 11 | this.dom.section = opt.section 12 | } 13 | 14 | init() { 15 | super.init(); 16 | } 17 | 18 | run() { 19 | this.perfs.now = window.performance.now() 20 | super.run() 21 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)) 22 | console.log(this.perfs.now - this.perfs.last) 23 | this.perfs.last = this.perfs.now 24 | } 25 | 26 | resize() { 27 | this.vars.bounding = this.dom.section.getBoundingClientRect().height - this.vars.height 28 | super.resize() 29 | } 30 | } 31 | 32 | export default Custom -------------------------------------------------------------------------------- /demos/opacity/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Custom extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.dom.section = opt.section 8 | this.dom.opacity = opt.opacity 9 | } 10 | 11 | init() { 12 | super.init() 13 | } 14 | 15 | run() { 16 | super.run() 17 | const current = Math.round(Math.abs(this.vars.current)) 18 | const opacity = Math.max(0, Math.min(1 - current / (this.vars.height * .5), 1)) 19 | this.dom.opacity.style.opacity = opacity.toFixed(2); 20 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)) 21 | } 22 | 23 | resize() { 24 | this.vars.bounding = this.dom.section.getBoundingClientRect().height - this.vars.height 25 | super.resize() 26 | } 27 | } 28 | 29 | export default Custom -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Baptiste Briel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demos/parallax/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | parallax demo | smooth-scrolling 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demos/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | } 4 | 5 | html, 6 | body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | .is-virtual-scroll { 12 | overflow: hidden; 13 | } 14 | 15 | .is-native-scroll.y-scroll { 16 | overflow-y: scroll; 17 | overflow-x: hidden; 18 | } 19 | 20 | .is-native-scroll.x-scroll { 21 | overflow-y: hidden; 22 | overflow-x: scroll; 23 | } 24 | 25 | .vs-section { 26 | position: fixed; 27 | top: 0; right: 0; left: 0; 28 | width: 100%; height: auto; 29 | margin: auto; 30 | text-align: center; 31 | will-change: transform; 32 | } 33 | 34 | .vs-scrollbar { 35 | display: block; 36 | position: absolute; 37 | transition: transform .6s; 38 | } 39 | 40 | .vs-scrollbar.vs-vertical { 41 | top: 0; right: -5px; bottom: 0; 42 | width: 15px; height: 100%; 43 | transform: translate3d(5px,0,0); 44 | } 45 | 46 | .vs-scrollbar.vs-horizontal { 47 | bottom: -5px; left: 0; right: 0; 48 | width: 100%; height: 15px; 49 | transform: translate3d(0,5px,0); 50 | } 51 | 52 | .is-dragging .vs-scrollbar.vs-horizontal, 53 | .is-dragging .vs-scrollbar.vs-vertical, 54 | .vs-scrollbar.vs-horizontal:hover, 55 | .vs-scrollbar.vs-vertical:hover { 56 | transform: none; 57 | } 58 | 59 | .vs-scrollbar .vs-scrolldrag { 60 | width: 100%; 61 | height: auto; 62 | background: #ccc; 63 | cursor: pointer; 64 | } 65 | 66 | .vs-scroll-view { 67 | position: relative; 68 | width: 1px; 69 | } -------------------------------------------------------------------------------- /demos/parallax-page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | parallax page demo | smooth-scrolling 7 | 8 | 17 | 18 | 19 |
20 |

vs-section + vs.div parallax example

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /demos/callback/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | callback demo | smooth-scrolling 7 | 8 | 31 | 32 | 33 |

current scroll: 0px

34 |
35 |

callback

36 | 37 |
38 | 39 | 48 | 49 | -------------------------------------------------------------------------------- /demos/performances/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | performances demo | smooth-scrolling 7 | 8 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /demos/native-horizontal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | native horizontal demo | smooth-scrolling 7 | 8 | 41 | 42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /demos/native-scrollbar/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | native scrollbar demo | smooth-scrolling 7 | 8 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /demos/horizontal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | horizontal demo | smooth-scrolling 7 | 8 | 38 | 39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smooth-scrolling", 3 | "version": "2.3.12", 4 | "description": "Smooth is a small JavaScript module based on VirtualScroll to create smooth scrolling and parallax effects on scroll.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "dom-classes": "github:npm-dom/dom-classes", 8 | "dom-create-element": "^1.0.2", 9 | "dom-events": "^0.1.1", 10 | "prefix": "^1.0.0", 11 | "virtual-scroll": "^1.2.1" 12 | }, 13 | "devDependencies": { 14 | "@babel/core": "^7.4.4", 15 | "@babel/preset-env": "^7.4.5", 16 | "babelify": "^10.0.0", 17 | "dom-css": "^2.1.0", 18 | "uglifyjs": "^2.4.11", 19 | "watchify": "^3.11.1" 20 | }, 21 | "scripts": { 22 | "dev": "watchify -v -t babelify index.js -o smooth-scrolling.js", 23 | "demo-parallax": "watchify -v -t babelify demos/parallax/index.js -o demos/parallax/build.js", 24 | "demo-parallax-page": "watchify -v -t babelify demos/parallax-page/index.js -o demos/parallax-page/build.js", 25 | "demo-horizontal": "watchify -v -t babelify demos/horizontal/index.js -o demos/horizontal/build.js", 26 | "demo-native-horizontal": "watchify -v -t babelify demos/native-horizontal/index.js -o demos/native-horizontal/build.js", 27 | "demo-opacity": "watchify -v -t babelify demos/opacity/index.js -o demos/opacity/build.js", 28 | "demo-scale": "watchify -v -t babelify demos/scale/index.js -o demos/scale/build.js", 29 | "demo-split": "watchify -v -t babelify demos/split/index.js -o demos/split/build.js", 30 | "demo-performances": "watchify -v -t babelify demos/performances/index.js -o demos/performances/build.js" 31 | }, 32 | "browserify": { 33 | "transform": [ 34 | [ 35 | "babelify", 36 | { 37 | "presets": [ 38 | "@babel/preset-env" 39 | ] 40 | } 41 | ] 42 | ] 43 | }, 44 | "author": "Baptiste Briel", 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /demos/native-horizontal/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Parallax extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.createExtraBound() 8 | this.resizing = false 9 | this.cache = null 10 | this.dom.divs = Array.prototype.slice.call(opt.divs, 0) 11 | } 12 | 13 | createExtraBound() { 14 | ['getCache', 'inViewport'] 15 | .forEach((fn) => this[fn] = this[fn].bind(this)) 16 | } 17 | 18 | resize() { 19 | this.resizing = true 20 | this.getCache() 21 | super.resize() 22 | this.dom.scroll.style.width = '' 23 | this.dom.scroll.style.height = `${this.vars.bounding}px` 24 | this.resizing = false 25 | } 26 | 27 | getCache() { 28 | this.cache = [] 29 | const unit = (this.vars.width / 3) 30 | this.dom.divs.forEach((el, index) => { 31 | el.style.display = 'inline-block' 32 | el.style.transform = 'none' 33 | el.style.width = `${unit}px` 34 | const scrollX = this.vars.target 35 | const bounding = el.getBoundingClientRect() 36 | const bounds = { 37 | el: el, 38 | state: true, 39 | left: bounding.left + scrollX, 40 | right: bounding.right + scrollX, 41 | center: unit / 2 42 | } 43 | this.cache.push(bounds) 44 | }) 45 | this.dom.section.style.width = `${this.vars.width}px` 46 | this.vars.bounding = (unit * this.dom.divs.length) 47 | } 48 | 49 | run() { 50 | this.dom.divs.forEach(this.inViewport) 51 | this.dom.section.style[this.prefix] = `translate3d(${this.vars.current * -1}px,0,0)` 52 | super.run() 53 | } 54 | 55 | inViewport(el, index) { 56 | if(!this.cache || this.resizing) return 57 | const cache = this.cache[index] 58 | const current = this.vars.current 59 | const left = Math.round(cache.left - current) 60 | const right = Math.round(cache.right - current) 61 | const inview = right > 0 && left < this.vars.width 62 | if(inview) { 63 | if(!el.state) { 64 | el.innerHTML = 'in viewport' 65 | el.state = true 66 | } 67 | } else { 68 | el.state = false 69 | el.innerHTML = '' 70 | } 71 | } 72 | } 73 | 74 | export default Parallax -------------------------------------------------------------------------------- /demos/split/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | import css from 'dom-css' 3 | 4 | class Split extends Smooth { 5 | 6 | constructor(opt) { 7 | super(opt) 8 | this.createExtraBound() 9 | this.sections = null 10 | this.dom.sections = Array.prototype.slice.call(opt.sections, 0) 11 | } 12 | 13 | createExtraBound() { 14 | ['getCache', 'inViewport'] 15 | .forEach((fn) => this[fn] = this[fn].bind(this)) 16 | } 17 | 18 | resize() { 19 | this.dom.sections.forEach((el, index) => css(el, { 20 | 'display': 'block', 21 | 'position': 'relative', 22 | 'top': 0, 23 | 'transform': 'none' 24 | })) 25 | this.vars.bounding = this.dom.sections[this.dom.sections.length - 1].getBoundingClientRect().bottom - (this.vars.native ? 0 : this.vars.height) 26 | css(this.dom.section, 'height', this.vars.bounding) 27 | this.getCache() 28 | this.dom.sections.forEach((el, index) => css(el, { 29 | 'position': 'fixed', 30 | 'width': '100%', 31 | 'top': this.sections[index].top 32 | })) 33 | super.resize() 34 | } 35 | 36 | getCache() { 37 | this.sections = [] 38 | this.dom.sections.forEach((el, index) => { 39 | const bounding = el.getBoundingClientRect(); 40 | const bounds = { 41 | el: el, 42 | state: true, 43 | top: bounding.top, 44 | bottom: bounding.bottom, 45 | speed: '-1' 46 | } 47 | this.sections.push(bounds) 48 | }) 49 | } 50 | 51 | run() { 52 | this.dom.sections.forEach(this.inViewport) 53 | super.run() 54 | } 55 | 56 | inViewport(el, index) { 57 | if(!this.sections) return 58 | const cache = this.sections[index] 59 | const current = this.vars.current 60 | const transform = current * cache.speed 61 | const top = Math.round(cache.top + transform) 62 | const bottom = Math.round(cache.bottom + transform) 63 | const inview = bottom > 0 && top < this.vars.height 64 | if(inview) { 65 | // !cache.state && (this.dom.section.appendChild(cache.el), cache.state = true); 66 | el.style.display = 'block' 67 | el.style[this.prefix] = this.getTransform(transform) 68 | } else { 69 | el.style.display = 'none' 70 | el.style[this.prefix] = 'none' 71 | // cache.state && cache.el.parentNode && (cache.el.parentNode.removeChild(cache.el), cache.state = false); 72 | } 73 | } 74 | } 75 | 76 | export default Split -------------------------------------------------------------------------------- /demos/parallax/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Parallax extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.createExtraBound() 8 | this.resizing = false 9 | this.cache = null 10 | this.dom.divs = Array.prototype.slice.call(opt.divs, 0) 11 | } 12 | 13 | createExtraBound() { 14 | ['getCache', 'inViewport'] 15 | .forEach((fn) => this[fn] = this[fn].bind(this)) 16 | } 17 | 18 | init() { 19 | super.init() 20 | } 21 | 22 | resize() { 23 | this.resizing = true 24 | this.reset() 25 | this.getCache() 26 | super.resize() 27 | this.resizing = false 28 | } 29 | 30 | reset() { 31 | if(!this.cache) return 32 | this.dom.divs.forEach((el, index) => { 33 | const cache = this.cache[index] 34 | !cache.state && (document.body.appendChild(cache.el), cache.state = true) 35 | el.style.display = 'block' 36 | }) 37 | } 38 | 39 | getCache() { 40 | this.cache = [] 41 | this.dom.divs.forEach((el, index) => { 42 | el.style.display = 'block' 43 | el.style.transform = 'none' 44 | const bounding = el.getBoundingClientRect() 45 | const bounds = { 46 | el: el, 47 | state: true, 48 | top: bounding.top, 49 | left: bounding.left, 50 | bottom: bounding.bottom, 51 | speed: el.getAttribute('data-speed') || '-1' 52 | } 53 | this.vars.bounding = bounding.bottom > this.vars.bounding ? bounding.bottom - window.innerHeight : this.vars.bounding 54 | this.cache.push(bounds) 55 | }) 56 | } 57 | 58 | run() { 59 | this.dom.divs.forEach(this.inViewport) 60 | super.run() 61 | } 62 | 63 | inViewport(el, index) { 64 | if(!this.cache || this.resizing) return 65 | const cache = this.cache[index] 66 | const current = this.vars.current 67 | const transform = current * cache.speed 68 | const top = Math.round(cache.top + transform) 69 | const bottom = Math.round(cache.bottom + transform) 70 | const inview = bottom > -100 && top < this.vars.height + 100 71 | if(inview) { 72 | !cache.state && (document.body.appendChild(cache.el), cache.state = true) 73 | el.style.display = 'block' 74 | el.style[this.prefix] = this.getTransform(transform) 75 | } else { 76 | // el.style.display = 'none' 77 | // el.style[this.prefix] = 'none' 78 | cache.state && cache.el.parentNode && (cache.el.parentNode.removeChild(cache.el), cache.state = false) 79 | } 80 | } 81 | } 82 | 83 | export default Parallax -------------------------------------------------------------------------------- /demos/parallax-page/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Parallax extends Smooth { 4 | 5 | constructor(opt) { 6 | super(opt) 7 | this.createExtraBound() 8 | this.resizing = false 9 | this.cache = null 10 | this.dom.divs = Array.prototype.slice.call(opt.divs, 0) 11 | } 12 | 13 | createExtraBound() { 14 | ['getCache', 'inViewport'] 15 | .forEach((fn) => this[fn] = this[fn].bind(this)) 16 | } 17 | 18 | resize() { 19 | this.resizing = true 20 | this.getCache() 21 | super.resize() 22 | this.resizing = false 23 | } 24 | 25 | getCache() { 26 | this.cache = [] 27 | this.dom.divs.forEach((el, index) => { 28 | el.style.display = 'block' 29 | el.style.transform = 'none' 30 | const scrollY = this.vars.target 31 | const bounding = el.getBoundingClientRect() 32 | const bounds = { 33 | el: el, 34 | state: true, 35 | top: bounding.top + scrollY, 36 | left: bounding.left, 37 | center: bounding.height / 2, 38 | bottom: bounding.bottom + scrollY, 39 | speed: el.getAttribute('data-speed') || '-1' 40 | } 41 | if(index === 4) { 42 | console.log(bounding.top, scrollY, bounds.top) 43 | } 44 | // this.vars.bounding = bounding.bottom > this.vars.bounding ? bounding.bottom - window.innerHeight : this.vars.bounding; 45 | this.cache.push(bounds) 46 | }) 47 | // get bounding value based on the container (.vs-section) height 48 | this.vars.bounding = this.dom.section.getBoundingClientRect().height - (this.vars.native ? 0 : this.vars.height) 49 | } 50 | 51 | run() { 52 | this.dom.divs.forEach(this.inViewport) 53 | this.dom.section.style[this.prefix] = this.getTransform(this.vars.current * -1) 54 | super.run() 55 | } 56 | 57 | inViewport(el, index) { 58 | if(!this.cache || this.resizing) return 59 | const cache = this.cache[index] 60 | const current = this.vars.current 61 | const transform = ((cache.top + cache.center) - current) * cache.speed 62 | const top = Math.round((cache.top + transform) - current) 63 | const bottom = Math.round((cache.bottom + transform) - current) 64 | const inview = bottom > 0 && top < this.vars.height 65 | if(inview) { 66 | el.style.border = '2px solid green' 67 | el.style.display = 'block' 68 | el.style[this.prefix] = this.getTransform(transform) 69 | } else { 70 | // add red border if out of viewport 71 | el.style.border = '2px solid red' 72 | } 73 | } 74 | } 75 | 76 | export default Parallax -------------------------------------------------------------------------------- /demos/horizontal/custom.js: -------------------------------------------------------------------------------- 1 | import Smooth from '../../index' 2 | 3 | class Parallax extends Smooth { 4 | 5 | constructor(opt) { 6 | 7 | super(opt) 8 | 9 | this.createExtraBound() 10 | 11 | this.resizing = false 12 | this.cache = null 13 | this.dom.divs = Array.prototype.slice.call(opt.divs, 0) 14 | } 15 | 16 | createExtraBound() { 17 | 18 | ['getCache', 'inViewport'] 19 | .forEach((fn) => this[fn] = this[fn].bind(this)) 20 | } 21 | 22 | resize() { 23 | 24 | this.resizing = true 25 | 26 | this.getCache() 27 | super.resize() 28 | 29 | this.resizing = false 30 | } 31 | 32 | getCache() { 33 | 34 | this.cache = [] 35 | 36 | const unit = (this.vars.width / 3) 37 | 38 | this.dom.divs.forEach((el, index) => { 39 | 40 | el.style.display = 'inline-block' 41 | el.style.transform = 'none' 42 | el.style.width = `${unit}px` 43 | 44 | const scrollX = this.vars.target 45 | const bounding = el.getBoundingClientRect() 46 | const bounds = { 47 | el: el, 48 | state: true, 49 | left: bounding.left + scrollX, 50 | right: bounding.right + scrollX, 51 | center: unit / 2 52 | } 53 | 54 | this.cache.push(bounds) 55 | }) 56 | 57 | this.dom.section.style.width = `${this.vars.width}px` 58 | this.vars.bounding = (unit * this.dom.divs.length) - this.vars.width 59 | } 60 | 61 | run() { 62 | 63 | this.dom.divs.forEach(this.inViewport); 64 | 65 | this.dom.section.style[this.prefix] = this.getTransform(this.vars.current * -1) 66 | 67 | super.run() 68 | } 69 | 70 | inViewport(el, index) { 71 | 72 | if(!this.cache || this.resizing) return 73 | 74 | const cache = this.cache[index] 75 | const current = this.vars.current 76 | const left = Math.round(cache.left - current) 77 | const right = Math.round(cache.right - current) 78 | const inview = right > 0 && left < this.vars.width 79 | 80 | if(inview) { 81 | 82 | if(!el.state) { 83 | el.innerHTML = 'in viewport' 84 | el.state = true 85 | } 86 | 87 | } else { 88 | 89 | el.state = false 90 | el.innerHTML = '' 91 | } 92 | } 93 | } 94 | 95 | export default Parallax -------------------------------------------------------------------------------- /demos/gsap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gsap scrolltrigger demo | smooth-scrolling 7 | 8 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 50 | 51 | 108 | 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smooth 2 | 3 | Smooth is a small JavaScript module based on [VirtualScroll](http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/) to create smooth scrolling and parallax effects on scroll. 4 | It works both with fake scrollbars and native scrolling. 5 | 6 | ### Usage 7 | 8 | `npm install smooth-scrolling` 9 | 10 | ```javascript 11 | import Smooth from 'smooth-scrolling' 12 | 13 | const section = document.querySelector('.vs-section') 14 | const smooth = new Smooth({ 15 | native: true, 16 | section: section, 17 | ease: 0.1 18 | }) 19 | 20 | smooth.init() 21 | ``` 22 | 23 | ### Options 24 | 25 | - `listener`: on-scroll events listener & parent container for all elements 26 | - `direction` : vertical or horizontal scrolling behavior 27 | - `native`: use the default scrollbar 28 | - `section` : the element to transform 29 | - `ease` : the easing value (usually between 0 and 1) 30 | - `vs` : you can pass some option for virtuall-scroll: limitInertia, mouseMultiplier, etc 31 | - `preload` : if set to false, there will be no resize function called after all images loaded 32 | - `noscrollbar` : if using virtual-scroll and set to true, it will not build a custom scrollbar 33 | - `callback`: function called on requestAnimationFrame 34 | 35 | ### Methods 36 | 37 | #### `smooth.init()` 38 | 39 | Will add all event listeners and DOM elements. 40 | 41 | #### `smooth.on()` 42 | 43 | Will listen to either window scroll event (if native), otherwise VirtualScroll 44 | 45 | #### `smooth.off()` 46 | 47 | Will stop listening to onscroll/wheel events. 48 | 49 | #### `smooth.destroy()` 50 | 51 | Will remove all event listeners and DOM elements. 52 | 53 | #### `smooth.scrollTo(offset)` 54 | 55 | Basic scrollTo function. 56 | 57 | ### Extends Smooth 58 | 59 | ```javascript 60 | import Smooth from 'smooth-scrolling' 61 | 62 | class Custom extends Smooth { 63 | 64 | constructor(opt = {}) { 65 | super(opt) 66 | this.dom.section = opt.section 67 | this.dom.opacity = opt.opacity 68 | } 69 | 70 | run() { 71 | super.run() 72 | 73 | const current = Math.round(Math.abs(this.vars.current)) 74 | const opacity = Math.max(0, Math.min(1 - current / (this.vars.height * .5), 1)) 75 | 76 | this.dom.opacity.style.opacity = opacity.toFixed(2) 77 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)) 78 | } 79 | 80 | resize() { 81 | this.vars.bounding = this.dom.section.getBoundingClientRect().height - this.vars.height 82 | super.resize() 83 | } 84 | } 85 | 86 | export default Custom 87 | ``` 88 | 89 | ```javascript 90 | // ...and later on 91 | import Custom from './custom-smooth-scrolling' 92 | 93 | const section = document.querySelector('.vs-section') 94 | const opacity = document.querySelector('.vs-opacity') 95 | 96 | const smooth = new Custom({ 97 | section: section, 98 | opacity: opacity, 99 | ease: 0.1 100 | }) 101 | 102 | smooth.init() 103 | ``` 104 | 105 | ### Development 106 | 107 | `git clone git@github.com:baptistebriel/smooth-scrolling.git` 108 | 109 | `cd smooth-scrolling/ && npm i && npm run dev` 110 | 111 | You can use `[http-server](https://www.npmjs.com/package/http-server)` or [MAMP](https://www.mamp.info) to preview the demos. 112 | 113 | ### Demos 114 | 115 | `npm run demo-parallax` 116 | 117 | `npm run demo-parallax-page` 118 | 119 | `npm run demo-horizontal` 120 | 121 | `npm run demo-native-horizontal` 122 | 123 | `npm run demo-opacity` 124 | 125 | `npm run demo-scale` 126 | 127 | `npm run demo-split` 128 | 129 | `npm run demo-performances` 130 | 131 | ### Examples 132 | 133 | - [etq.store](http://etq.store) 134 | - [femmefatale.paris](http://femmefatale.paris) 135 | - [buildin.amsterdam](http://buildin.amsterdam) 136 | - [romainpsd.com](https://romainpsd.com) 137 | - [flavinsky.com](http://flavinsky.com) 138 | - [alisharaf.com](http://alisharaf.com) 139 | - [bbriel.me](http://bbriel.me) 140 | - [studiochevojon.com](http://studiochevojon.com) 141 | - [andeinerseite.video](http://andeinerseite.video) 142 | - [eginstill.com](http://eginstill.com) 143 | - [blackballoon.fr](http://www.blackballoon.fr) 144 | - & more to come! 145 | 146 | ## Further understanding 147 | 148 | If you didn't already read [the tutorial](http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/), I highly recommend it. 149 | Smooth.js is basically what's explained on the blog post. I just needed a simple script to get things done without having to write lots of code every time I wanted to use this technique. 150 | 151 | ## License 152 | 153 | MIT, see [LICENSE.md](https://github.com/BaptisteBriel/smooth/blob/master/LICENSE). 154 | -------------------------------------------------------------------------------- /demos/split/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | split view demo | smooth-scrolling 7 | 8 | 23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 | 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 | 91 |
92 |
93 | 94 |
95 |
96 | 97 |
98 |
99 | 100 |
101 |
102 | 103 |
104 |
105 | 106 |
107 |
108 | 109 |
110 |
111 | 112 |
113 |
114 | 115 |
116 |
117 | 118 |
119 |
120 | 121 |
122 |
123 | 124 |
125 |
126 | 127 |
128 |
129 | 130 |
131 |
132 | 133 |
134 |
135 | 136 | 137 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import classes from 'dom-classes' 2 | import create from 'dom-create-element' 3 | import prefix from 'prefix' 4 | import vs from 'virtual-scroll' 5 | import event from 'dom-events' 6 | 7 | export default class Smooth { 8 | 9 | constructor(opt = {}) { 10 | this.createBound() 11 | this.options = opt 12 | this.prefix = prefix('transform') 13 | this.rAF = undefined 14 | // It seems that under heavy load, Firefox will still call the RAF callback even though the RAF has been canceled 15 | // To prevent that we set a flag to prevent any callback to be executed when RAF is removed 16 | this.isRAFCanceled = false 17 | const constructorName = this.constructor.name ? this.constructor.name : 'Smooth' 18 | this.extends = typeof opt.extends === 'undefined' ? this.constructor !== Smooth : opt.extends 19 | this.callback = this.options.callback || null 20 | this.vars = { 21 | direction: this.options.direction || 'vertical', 22 | native: this.options.native || false, 23 | ease: this.options.ease || 0.075, 24 | preload: this.options.preload || false, 25 | current: 0, 26 | last: 0, 27 | target: 0, 28 | height: window.innerHeight, 29 | width: window.innerWidth, 30 | bounding: 0, 31 | timer: null, 32 | ticking: false 33 | } 34 | this.vs = this.vars.native ? null : new vs({ 35 | limitInertia: this.options.vs && this.options.vs.limitInertia || false, 36 | mouseMultiplier: this.options.vs && this.options.vs.mouseMultiplier || 1, 37 | touchMultiplier: this.options.vs && this.options.vs.touchMultiplier || 1.5, 38 | firefoxMultiplier: this.options.vs && this.options.vs.firefoxMultiplier || 30, 39 | preventTouch: this.options.vs && this.options.vs.preventTouch || true 40 | }) 41 | this.dom = { 42 | listener: this.options.listener || document.body, 43 | section: this.options.section || document.querySelector('.vs-section') || null, 44 | scrollbar: this.vars.native || this.options.noscrollbar ? null : { 45 | state: { 46 | clicked: false, 47 | x: 0 48 | }, 49 | el: create({ selector: 'div', styles: `vs-scrollbar vs-${this.vars.direction} vs-scrollbar-${constructorName.toLowerCase()}` }), 50 | drag: { 51 | el: create({ selector: 'div', styles: 'vs-scrolldrag' }), 52 | delta: 0, 53 | height: 50 54 | } 55 | } 56 | } 57 | } 58 | 59 | createBound() { 60 | ['run', 'calc', 'debounce', 'resize', 'mouseUp', 'mouseDown', 'mouseMove', 'calcScroll', 'scrollTo'] 61 | .forEach((fn) => this[fn] = this[fn].bind(this)); 62 | } 63 | 64 | init() { 65 | this.addClasses() 66 | this.vars.preload && this.preloadImages() 67 | this.vars.native ? this.addFakeScrollHeight() : !this.options.noscrollbar && this.addFakeScrollBar() 68 | this.addEvents() 69 | this.resize() 70 | } 71 | 72 | addClasses() { 73 | const type = this.vars.native ? 'native' : 'virtual' 74 | const direction = this.vars.direction === 'vertical' ? 'y' : 'x' 75 | classes.add(this.dom.listener, `is-${type}-scroll`) 76 | classes.add(this.dom.listener, `${direction}-scroll`) 77 | } 78 | 79 | preloadImages() { 80 | const images = Array.prototype.slice.call(this.dom.listener.querySelectorAll('img'), 0) 81 | images.forEach((image) => { 82 | const img = document.createElement('img') 83 | event.once(img, 'load', () => { 84 | images.splice(images.indexOf(image), 1) 85 | images.length === 0 && this.resize() 86 | }) 87 | img.src = image.getAttribute('src') 88 | }) 89 | } 90 | 91 | calc(e) { 92 | const delta = this.vars.direction == 'horizontal' ? e.deltaX : e.deltaY 93 | this.vars.target += delta * -1 94 | this.clampTarget() 95 | } 96 | 97 | debounce() { 98 | const win = this.dom.listener === document.body 99 | this.vars.target = this.vars.direction === 'vertical' ? win ? window.scrollY || window.pageYOffset : this.dom.listener.scrollTop : win ? window.scrollX || window.pageXOffset : this.dom.listener.scrollLeft 100 | clearTimeout(this.vars.timer) 101 | if(!this.vars.ticking) { 102 | this.vars.ticking = true; 103 | classes.add(this.dom.listener, 'is-scrolling') 104 | } 105 | this.vars.timer = setTimeout(() => { 106 | this.vars.ticking = false 107 | classes.remove(this.dom.listener, 'is-scrolling') 108 | }, 200) 109 | } 110 | 111 | run() { 112 | if (this.isRAFCanceled) return 113 | this.vars.current += (this.vars.target - this.vars.current) * this.vars.ease 114 | this.vars.current < .1 && (this.vars.current = 0) 115 | this.requestAnimationFrame() 116 | if(!this.extends){ 117 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)) 118 | } 119 | if(!this.vars.native && !this.options.noscrollbar) { 120 | const size = this.dom.scrollbar.drag.height 121 | const bounds = this.vars.direction === 'vertical' ? this.vars.height : this.vars.width 122 | const value = (Math.abs(this.vars.current) / (this.vars.bounding / (bounds - size))) + (size / .5) - size 123 | const clamp = Math.max(0, Math.min(value-size, value+size)) 124 | this.dom.scrollbar.drag.el.style[this.prefix] = this.getTransform(clamp.toFixed(2)) 125 | } 126 | if (this.callback && this.vars.current !== this.vars.last) { 127 | this.callback(this.vars.current) 128 | } 129 | this.vars.last = this.vars.current 130 | } 131 | 132 | getTransform(value) { 133 | return this.vars.direction === 'vertical' ? `translate3d(0,${value}px,0)` : `translate3d(${value}px,0,0)` 134 | } 135 | 136 | on(requestAnimationFrame = true) { 137 | if (this.isRAFCanceled) { 138 | this.isRAFCanceled = false 139 | } 140 | const node = this.dom.listener === document.body ? window : this.dom.listener 141 | this.vars.native ? event.on(node, 'scroll', this.debounce) : (this.vs && this.vs.on(this.calc)) 142 | requestAnimationFrame && this.requestAnimationFrame() 143 | } 144 | 145 | off(cancelAnimationFrame = true) { 146 | const node = this.dom.listener === document.body ? window : this.dom.listener 147 | this.vars.native ? event.off(node, 'scroll', this.debounce) : (this.vs && this.vs.off(this.calc)) 148 | cancelAnimationFrame && this.cancelAnimationFrame() 149 | } 150 | 151 | requestAnimationFrame() { 152 | this.rAF = requestAnimationFrame(this.run) 153 | } 154 | 155 | cancelAnimationFrame() { 156 | this.isRAFCanceled = true 157 | cancelAnimationFrame(this.rAF) 158 | } 159 | 160 | addEvents() { 161 | this.on() 162 | event.on(window, 'resize', this.resize) 163 | } 164 | 165 | removeEvents() { 166 | this.off() 167 | event.off(window, 'resize', this.resize) 168 | } 169 | 170 | addFakeScrollBar() { 171 | this.dom.listener.appendChild(this.dom.scrollbar.el) 172 | this.dom.scrollbar.el.appendChild(this.dom.scrollbar.drag.el) 173 | event.on(this.dom.scrollbar.el, 'click', this.calcScroll) 174 | event.on(this.dom.scrollbar.el, 'mousedown', this.mouseDown) 175 | event.on(document, 'mousemove', this.mouseMove) 176 | event.on(document, 'mouseup', this.mouseUp) 177 | } 178 | 179 | removeFakeScrollBar() { 180 | event.off(this.dom.scrollbar.el, 'click', this.calcScroll) 181 | event.off(this.dom.scrollbar.el, 'mousedown', this.mouseDown) 182 | event.off(document, 'mousemove', this.mouseMove) 183 | event.off(document, 'mouseup', this.mouseUp) 184 | this.dom.listener.removeChild(this.dom.scrollbar.el) 185 | } 186 | 187 | mouseDown(e) { 188 | e.preventDefault() 189 | e.which == 1 && (this.dom.scrollbar.state.clicked = true) 190 | } 191 | 192 | mouseUp(e) { 193 | this.dom.scrollbar.state.clicked = false 194 | classes.remove(this.dom.listener, 'is-dragging') 195 | } 196 | 197 | mouseMove(e) { 198 | this.dom.scrollbar.state.clicked && this.calcScroll(e) 199 | } 200 | 201 | addFakeScrollHeight() { 202 | this.dom.scroll = create({ 203 | selector: 'div', 204 | styles: 'vs-scroll-view' 205 | }) 206 | this.dom.listener.appendChild(this.dom.scroll) 207 | } 208 | 209 | removeFakeScrollHeight() { 210 | this.dom.listener.removeChild(this.dom.scroll) 211 | } 212 | 213 | calcScroll(e) { 214 | const client = this.vars.direction == 'vertical' ? e.clientY : e.clientX 215 | const bounds = this.vars.direction == 'vertical' ? this.vars.height : this.vars.width 216 | const delta = client * (this.vars.bounding / bounds) 217 | classes.add(this.dom.listener, 'is-dragging') 218 | this.vars.target = delta 219 | this.clampTarget() 220 | this.dom.scrollbar && (this.dom.scrollbar.drag.delta = this.vars.target) 221 | } 222 | 223 | scrollTo(offset) { 224 | if(this.vars.native) { 225 | this.vars.direction == 'vertical' ? window.scrollTo(0, offset) : window.scrollTo(offset, 0) 226 | } else { 227 | this.vars.target = offset 228 | this.clampTarget() 229 | } 230 | } 231 | 232 | resize() { 233 | const prop = this.vars.direction === 'vertical' ? 'height' : 'width'; 234 | this.vars.height = window.innerHeight 235 | this.vars.width = window.innerWidth 236 | if(!this.extends) { 237 | const bounding = this.dom.section.getBoundingClientRect() 238 | this.vars.bounding = this.vars.direction === 'vertical' ? bounding.height - (this.vars.native ? 0 : this.vars.height) : bounding.right - (this.vars.native ? 0 : this.vars.width) 239 | } 240 | if(!this.vars.native && !this.options.noscrollbar) { 241 | this.dom.scrollbar.drag.height = this.vars.height * (this.vars.height / (this.vars.bounding + this.vars.height)) 242 | this.dom.scrollbar.drag.el.style[prop] = `${this.dom.scrollbar.drag.height}px` 243 | } else if(this.vars.native) { 244 | this.dom.scroll.style[prop] = `${this.vars.bounding}px` 245 | } 246 | !this.vars.native && this.clampTarget(); 247 | } 248 | 249 | clampTarget() { 250 | this.vars.target = Math.round(Math.max(0, Math.min(this.vars.target, this.vars.bounding))) 251 | } 252 | 253 | destroy() { 254 | if(this.vars.native) { 255 | classes.remove(this.dom.listener, 'is-native-scroll') 256 | this.removeFakeScrollHeight() 257 | } else { 258 | classes.remove(this.dom.listener, 'is-virtual-scroll') 259 | !this.options.noscrollbar && this.removeFakeScrollBar() 260 | } 261 | this.vars.direction === 'vertical' ? classes.remove(this.dom.listener, 'y-scroll') : classes.remove(this.dom.listener, 'x-scroll') 262 | this.vars.current = 0 263 | this.vs && (this.vs.destroy(), this.vs = null) 264 | this.removeEvents() 265 | } 266 | } 267 | 268 | window.Smooth = Smooth -------------------------------------------------------------------------------- /smooth-scrolling.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}; 32 | 33 | _classCallCheck(this, Smooth); 34 | 35 | this.createBound(); 36 | this.options = opt; 37 | this.prefix = (0, _prefix["default"])('transform'); 38 | this.rAF = undefined; // It seems that under heavy load, Firefox will still call the RAF callback even though the RAF has been canceled 39 | // To prevent that we set a flag to prevent any callback to be executed when RAF is removed 40 | 41 | this.isRAFCanceled = false; 42 | var constructorName = this.constructor.name ? this.constructor.name : 'Smooth'; 43 | this["extends"] = typeof opt["extends"] === 'undefined' ? this.constructor !== Smooth : opt["extends"]; 44 | this.callback = this.options.callback || null; 45 | this.vars = { 46 | direction: this.options.direction || 'vertical', 47 | "native": this.options["native"] || false, 48 | ease: this.options.ease || 0.075, 49 | preload: this.options.preload || false, 50 | current: 0, 51 | last: 0, 52 | target: 0, 53 | height: window.innerHeight, 54 | width: window.innerWidth, 55 | bounding: 0, 56 | timer: null, 57 | ticking: false 58 | }; 59 | this.vs = this.vars["native"] ? null : new _virtualScroll["default"]({ 60 | limitInertia: this.options.vs && this.options.vs.limitInertia || false, 61 | mouseMultiplier: this.options.vs && this.options.vs.mouseMultiplier || 1, 62 | touchMultiplier: this.options.vs && this.options.vs.touchMultiplier || 1.5, 63 | firefoxMultiplier: this.options.vs && this.options.vs.firefoxMultiplier || 30, 64 | preventTouch: this.options.vs && this.options.vs.preventTouch || true 65 | }); 66 | this.dom = { 67 | listener: this.options.listener || document.body, 68 | section: this.options.section || document.querySelector('.vs-section') || null, 69 | scrollbar: this.vars["native"] || this.options.noscrollbar ? null : { 70 | state: { 71 | clicked: false, 72 | x: 0 73 | }, 74 | el: (0, _domCreateElement["default"])({ 75 | selector: 'div', 76 | styles: "vs-scrollbar vs-".concat(this.vars.direction, " vs-scrollbar-").concat(constructorName.toLowerCase()) 77 | }), 78 | drag: { 79 | el: (0, _domCreateElement["default"])({ 80 | selector: 'div', 81 | styles: 'vs-scrolldrag' 82 | }), 83 | delta: 0, 84 | height: 50 85 | } 86 | } 87 | }; 88 | } 89 | 90 | _createClass(Smooth, [{ 91 | key: "createBound", 92 | value: function createBound() { 93 | var _this = this; 94 | 95 | ['run', 'calc', 'debounce', 'resize', 'mouseUp', 'mouseDown', 'mouseMove', 'calcScroll', 'scrollTo'].forEach(function (fn) { 96 | return _this[fn] = _this[fn].bind(_this); 97 | }); 98 | } 99 | }, { 100 | key: "init", 101 | value: function init() { 102 | this.addClasses(); 103 | this.vars.preload && this.preloadImages(); 104 | this.vars["native"] ? this.addFakeScrollHeight() : !this.options.noscrollbar && this.addFakeScrollBar(); 105 | this.addEvents(); 106 | this.resize(); 107 | } 108 | }, { 109 | key: "addClasses", 110 | value: function addClasses() { 111 | var type = this.vars["native"] ? 'native' : 'virtual'; 112 | var direction = this.vars.direction === 'vertical' ? 'y' : 'x'; 113 | 114 | _domClasses["default"].add(this.dom.listener, "is-".concat(type, "-scroll")); 115 | 116 | _domClasses["default"].add(this.dom.listener, "".concat(direction, "-scroll")); 117 | } 118 | }, { 119 | key: "preloadImages", 120 | value: function preloadImages() { 121 | var _this2 = this; 122 | 123 | var images = Array.prototype.slice.call(this.dom.listener.querySelectorAll('img'), 0); 124 | images.forEach(function (image) { 125 | var img = document.createElement('img'); 126 | 127 | _domEvents["default"].once(img, 'load', function () { 128 | images.splice(images.indexOf(image), 1); 129 | images.length === 0 && _this2.resize(); 130 | }); 131 | 132 | img.src = image.getAttribute('src'); 133 | }); 134 | } 135 | }, { 136 | key: "calc", 137 | value: function calc(e) { 138 | var delta = this.vars.direction == 'horizontal' ? e.deltaX : e.deltaY; 139 | this.vars.target += delta * -1; 140 | this.clampTarget(); 141 | } 142 | }, { 143 | key: "debounce", 144 | value: function debounce() { 145 | var _this3 = this; 146 | 147 | var win = this.dom.listener === document.body; 148 | this.vars.target = this.vars.direction === 'vertical' ? win ? window.scrollY || window.pageYOffset : this.dom.listener.scrollTop : win ? window.scrollX || window.pageXOffset : this.dom.listener.scrollLeft; 149 | clearTimeout(this.vars.timer); 150 | 151 | if (!this.vars.ticking) { 152 | this.vars.ticking = true; 153 | 154 | _domClasses["default"].add(this.dom.listener, 'is-scrolling'); 155 | } 156 | 157 | this.vars.timer = setTimeout(function () { 158 | _this3.vars.ticking = false; 159 | 160 | _domClasses["default"].remove(_this3.dom.listener, 'is-scrolling'); 161 | }, 200); 162 | } 163 | }, { 164 | key: "run", 165 | value: function run() { 166 | if (this.isRAFCanceled) return; 167 | this.vars.current += (this.vars.target - this.vars.current) * this.vars.ease; 168 | this.vars.current < .1 && (this.vars.current = 0); 169 | this.requestAnimationFrame(); 170 | 171 | if (!this["extends"]) { 172 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)); 173 | } 174 | 175 | if (!this.vars["native"] && !this.options.noscrollbar) { 176 | var size = this.dom.scrollbar.drag.height; 177 | var bounds = this.vars.direction === 'vertical' ? this.vars.height : this.vars.width; 178 | var value = Math.abs(this.vars.current) / (this.vars.bounding / (bounds - size)) + size / .5 - size; 179 | var clamp = Math.max(0, Math.min(value - size, value + size)); 180 | this.dom.scrollbar.drag.el.style[this.prefix] = this.getTransform(clamp.toFixed(2)); 181 | } 182 | 183 | if (this.callback && this.vars.current !== this.vars.last) { 184 | this.callback(this.vars.current); 185 | } 186 | 187 | this.vars.last = this.vars.current; 188 | } 189 | }, { 190 | key: "getTransform", 191 | value: function getTransform(value) { 192 | return this.vars.direction === 'vertical' ? "translate3d(0,".concat(value, "px,0)") : "translate3d(".concat(value, "px,0,0)"); 193 | } 194 | }, { 195 | key: "on", 196 | value: function on() { 197 | var requestAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 198 | 199 | if (this.isRAFCanceled) { 200 | this.isRAFCanceled = false; 201 | } 202 | 203 | var node = this.dom.listener === document.body ? window : this.dom.listener; 204 | this.vars["native"] ? _domEvents["default"].on(node, 'scroll', this.debounce) : this.vs && this.vs.on(this.calc); 205 | requestAnimationFrame && this.requestAnimationFrame(); 206 | } 207 | }, { 208 | key: "off", 209 | value: function off() { 210 | var cancelAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 211 | var node = this.dom.listener === document.body ? window : this.dom.listener; 212 | this.vars["native"] ? _domEvents["default"].off(node, 'scroll', this.debounce) : this.vs && this.vs.off(this.calc); 213 | cancelAnimationFrame && this.cancelAnimationFrame(); 214 | } 215 | }, { 216 | key: "requestAnimationFrame", 217 | value: function (_requestAnimationFrame) { 218 | function requestAnimationFrame() { 219 | return _requestAnimationFrame.apply(this, arguments); 220 | } 221 | 222 | requestAnimationFrame.toString = function () { 223 | return _requestAnimationFrame.toString(); 224 | }; 225 | 226 | return requestAnimationFrame; 227 | }(function () { 228 | this.rAF = requestAnimationFrame(this.run); 229 | }) 230 | }, { 231 | key: "cancelAnimationFrame", 232 | value: function (_cancelAnimationFrame) { 233 | function cancelAnimationFrame() { 234 | return _cancelAnimationFrame.apply(this, arguments); 235 | } 236 | 237 | cancelAnimationFrame.toString = function () { 238 | return _cancelAnimationFrame.toString(); 239 | }; 240 | 241 | return cancelAnimationFrame; 242 | }(function () { 243 | this.isRAFCanceled = true; 244 | cancelAnimationFrame(this.rAF); 245 | }) 246 | }, { 247 | key: "addEvents", 248 | value: function addEvents() { 249 | this.on(); 250 | 251 | _domEvents["default"].on(window, 'resize', this.resize); 252 | } 253 | }, { 254 | key: "removeEvents", 255 | value: function removeEvents() { 256 | this.off(); 257 | 258 | _domEvents["default"].off(window, 'resize', this.resize); 259 | } 260 | }, { 261 | key: "addFakeScrollBar", 262 | value: function addFakeScrollBar() { 263 | this.dom.listener.appendChild(this.dom.scrollbar.el); 264 | this.dom.scrollbar.el.appendChild(this.dom.scrollbar.drag.el); 265 | 266 | _domEvents["default"].on(this.dom.scrollbar.el, 'click', this.calcScroll); 267 | 268 | _domEvents["default"].on(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 269 | 270 | _domEvents["default"].on(document, 'mousemove', this.mouseMove); 271 | 272 | _domEvents["default"].on(document, 'mouseup', this.mouseUp); 273 | } 274 | }, { 275 | key: "removeFakeScrollBar", 276 | value: function removeFakeScrollBar() { 277 | _domEvents["default"].off(this.dom.scrollbar.el, 'click', this.calcScroll); 278 | 279 | _domEvents["default"].off(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 280 | 281 | _domEvents["default"].off(document, 'mousemove', this.mouseMove); 282 | 283 | _domEvents["default"].off(document, 'mouseup', this.mouseUp); 284 | 285 | this.dom.listener.removeChild(this.dom.scrollbar.el); 286 | } 287 | }, { 288 | key: "mouseDown", 289 | value: function mouseDown(e) { 290 | e.preventDefault(); 291 | e.which == 1 && (this.dom.scrollbar.state.clicked = true); 292 | } 293 | }, { 294 | key: "mouseUp", 295 | value: function mouseUp(e) { 296 | this.dom.scrollbar.state.clicked = false; 297 | 298 | _domClasses["default"].remove(this.dom.listener, 'is-dragging'); 299 | } 300 | }, { 301 | key: "mouseMove", 302 | value: function mouseMove(e) { 303 | this.dom.scrollbar.state.clicked && this.calcScroll(e); 304 | } 305 | }, { 306 | key: "addFakeScrollHeight", 307 | value: function addFakeScrollHeight() { 308 | this.dom.scroll = (0, _domCreateElement["default"])({ 309 | selector: 'div', 310 | styles: 'vs-scroll-view' 311 | }); 312 | this.dom.listener.appendChild(this.dom.scroll); 313 | } 314 | }, { 315 | key: "removeFakeScrollHeight", 316 | value: function removeFakeScrollHeight() { 317 | this.dom.listener.removeChild(this.dom.scroll); 318 | } 319 | }, { 320 | key: "calcScroll", 321 | value: function calcScroll(e) { 322 | var client = this.vars.direction == 'vertical' ? e.clientY : e.clientX; 323 | var bounds = this.vars.direction == 'vertical' ? this.vars.height : this.vars.width; 324 | var delta = client * (this.vars.bounding / bounds); 325 | 326 | _domClasses["default"].add(this.dom.listener, 'is-dragging'); 327 | 328 | this.vars.target = delta; 329 | this.clampTarget(); 330 | this.dom.scrollbar && (this.dom.scrollbar.drag.delta = this.vars.target); 331 | } 332 | }, { 333 | key: "scrollTo", 334 | value: function scrollTo(offset) { 335 | if (this.vars["native"]) { 336 | this.vars.direction == 'vertical' ? window.scrollTo(0, offset) : window.scrollTo(offset, 0); 337 | } else { 338 | this.vars.target = offset; 339 | this.clampTarget(); 340 | } 341 | } 342 | }, { 343 | key: "resize", 344 | value: function resize() { 345 | var prop = this.vars.direction === 'vertical' ? 'height' : 'width'; 346 | this.vars.height = window.innerHeight; 347 | this.vars.width = window.innerWidth; 348 | 349 | if (!this["extends"]) { 350 | var bounding = this.dom.section.getBoundingClientRect(); 351 | this.vars.bounding = this.vars.direction === 'vertical' ? bounding.height - (this.vars["native"] ? 0 : this.vars.height) : bounding.right - (this.vars["native"] ? 0 : this.vars.width); 352 | } 353 | 354 | if (!this.vars["native"] && !this.options.noscrollbar) { 355 | this.dom.scrollbar.drag.height = this.vars.height * (this.vars.height / (this.vars.bounding + this.vars.height)); 356 | this.dom.scrollbar.drag.el.style[prop] = "".concat(this.dom.scrollbar.drag.height, "px"); 357 | } else if (this.vars["native"]) { 358 | this.dom.scroll.style[prop] = "".concat(this.vars.bounding, "px"); 359 | } 360 | 361 | !this.vars["native"] && this.clampTarget(); 362 | } 363 | }, { 364 | key: "clampTarget", 365 | value: function clampTarget() { 366 | this.vars.target = Math.round(Math.max(0, Math.min(this.vars.target, this.vars.bounding))); 367 | } 368 | }, { 369 | key: "destroy", 370 | value: function destroy() { 371 | if (this.vars["native"]) { 372 | _domClasses["default"].remove(this.dom.listener, 'is-native-scroll'); 373 | 374 | this.removeFakeScrollHeight(); 375 | } else { 376 | _domClasses["default"].remove(this.dom.listener, 'is-virtual-scroll'); 377 | 378 | !this.options.noscrollbar && this.removeFakeScrollBar(); 379 | } 380 | 381 | this.vars.direction === 'vertical' ? _domClasses["default"].remove(this.dom.listener, 'y-scroll') : _domClasses["default"].remove(this.dom.listener, 'x-scroll'); 382 | this.vars.current = 0; 383 | this.vs && (this.vs.destroy(), this.vs = null); 384 | this.removeEvents(); 385 | } 386 | }]); 387 | 388 | return Smooth; 389 | }(); 390 | 391 | exports["default"] = Smooth; 392 | window.Smooth = Smooth; 393 | 394 | },{"dom-classes":3,"dom-create-element":4,"dom-events":5,"prefix":9,"virtual-scroll":15}],2:[function(require,module,exports){ 395 | 'use strict'; 396 | 397 | var toString = Object.prototype.toString, 398 | hasOwnProperty = Object.prototype.hasOwnProperty; 399 | 400 | module.exports = function(object) { 401 | if(!object) return console.warn('bindAll requires at least one argument.'); 402 | 403 | var functions = Array.prototype.slice.call(arguments, 1); 404 | 405 | if (functions.length === 0) { 406 | 407 | for (var method in object) { 408 | if(hasOwnProperty.call(object, method)) { 409 | if(typeof object[method] == 'function' && toString.call(object[method]) == "[object Function]") { 410 | functions.push(method); 411 | } 412 | } 413 | } 414 | } 415 | 416 | for(var i = 0; i < functions.length; i++) { 417 | var f = functions[i]; 418 | object[f] = bind(object[f], object); 419 | } 420 | }; 421 | 422 | /* 423 | Faster bind without specific-case checking. (see https://coderwall.com/p/oi3j3w). 424 | bindAll is only needed for events binding so no need to make slow fixes for constructor 425 | or partial application. 426 | */ 427 | function bind(func, context) { 428 | return function() { 429 | return func.apply(context, arguments); 430 | }; 431 | } 432 | },{}],3:[function(require,module,exports){ 433 | /** 434 | * Module dependencies. 435 | */ 436 | 437 | var index = require('indexof'); 438 | 439 | /** 440 | * Whitespace regexp. 441 | */ 442 | 443 | var whitespaceRe = /\s+/; 444 | 445 | /** 446 | * toString reference. 447 | */ 448 | 449 | var toString = Object.prototype.toString; 450 | 451 | module.exports = classes; 452 | module.exports.add = add; 453 | module.exports.contains = has; 454 | module.exports.has = has; 455 | module.exports.toggle = toggle; 456 | module.exports.remove = remove; 457 | module.exports.removeMatching = removeMatching; 458 | 459 | function classes (el) { 460 | if (el.classList) { 461 | return el.classList; 462 | } 463 | 464 | var str = el.className.replace(/^\s+|\s+$/g, ''); 465 | var arr = str.split(whitespaceRe); 466 | if ('' === arr[0]) arr.shift(); 467 | return arr; 468 | } 469 | 470 | function add (el, name) { 471 | // classList 472 | if (el.classList) { 473 | el.classList.add(name); 474 | return; 475 | } 476 | 477 | // fallback 478 | var arr = classes(el); 479 | var i = index(arr, name); 480 | if (!~i) arr.push(name); 481 | el.className = arr.join(' '); 482 | } 483 | 484 | function has (el, name) { 485 | return el.classList 486 | ? el.classList.contains(name) 487 | : !! ~index(classes(el), name); 488 | } 489 | 490 | function remove (el, name) { 491 | if ('[object RegExp]' == toString.call(name)) { 492 | return removeMatching(el, name); 493 | } 494 | 495 | // classList 496 | if (el.classList) { 497 | el.classList.remove(name); 498 | return; 499 | } 500 | 501 | // fallback 502 | var arr = classes(el); 503 | var i = index(arr, name); 504 | if (~i) arr.splice(i, 1); 505 | el.className = arr.join(' '); 506 | } 507 | 508 | function removeMatching (el, re, ref) { 509 | var arr = Array.prototype.slice.call(classes(el)); 510 | for (var i = 0; i < arr.length; i++) { 511 | if (re.test(arr[i])) { 512 | remove(el, arr[i]); 513 | } 514 | } 515 | } 516 | 517 | function toggle (el, name) { 518 | // classList 519 | if (el.classList) { 520 | return el.classList.toggle(name); 521 | } 522 | 523 | // fallback 524 | if (has(el, name)) { 525 | remove(el, name); 526 | } else { 527 | add(el, name); 528 | } 529 | } 530 | 531 | },{"indexof":6}],4:[function(require,module,exports){ 532 | /* 533 | `dom-create-element` 534 | 535 | var create = require('dom-create-element'); 536 | 537 | var el = create({ 538 | selector: 'div', 539 | styles: 'preloader', 540 | html: 'Text' 541 | }); 542 | */ 543 | 544 | module.exports = create; 545 | 546 | function create(opt) { 547 | 548 | opt = opt || {}; 549 | 550 | var el = document.createElement(opt.selector); 551 | 552 | if(opt.attr) for(var index in opt.attr) 553 | opt.attr.hasOwnProperty(index) && el.setAttribute(index, opt.attr[index]); 554 | 555 | "a" == opt.selector && opt.link && ( 556 | el.href = opt.link, 557 | opt.target && el.setAttribute("target", opt.target) 558 | ); 559 | 560 | "img" == opt.selector && opt.src && ( 561 | el.src = opt.src, 562 | opt.lazyload && ( 563 | el.style.opacity = 0, 564 | el.onload = function(){ 565 | el.style.opacity = 1; 566 | } 567 | ) 568 | ); 569 | 570 | opt.id && (el.id = opt.id); 571 | opt.styles && (el.className = opt.styles); 572 | 573 | opt.html && (el.innerHTML = opt.html); 574 | opt.children && (el.appendChild(opt.children)); 575 | 576 | return el; 577 | }; 578 | },{}],5:[function(require,module,exports){ 579 | 580 | var synth = require('synthetic-dom-events'); 581 | 582 | var on = function(element, name, fn, capture) { 583 | return element.addEventListener(name, fn, capture || false); 584 | }; 585 | 586 | var off = function(element, name, fn, capture) { 587 | return element.removeEventListener(name, fn, capture || false); 588 | }; 589 | 590 | var once = function (element, name, fn, capture) { 591 | function tmp (ev) { 592 | off(element, name, tmp, capture); 593 | fn(ev); 594 | } 595 | on(element, name, tmp, capture); 596 | }; 597 | 598 | var emit = function(element, name, opt) { 599 | var ev = synth(name, opt); 600 | element.dispatchEvent(ev); 601 | }; 602 | 603 | if (!document.addEventListener) { 604 | on = function(element, name, fn) { 605 | return element.attachEvent('on' + name, fn); 606 | }; 607 | } 608 | 609 | if (!document.removeEventListener) { 610 | off = function(element, name, fn) { 611 | return element.detachEvent('on' + name, fn); 612 | }; 613 | } 614 | 615 | if (!document.dispatchEvent) { 616 | emit = function(element, name, opt) { 617 | var ev = synth(name, opt); 618 | return element.fireEvent('on' + ev.type, ev); 619 | }; 620 | } 621 | 622 | module.exports = { 623 | on: on, 624 | off: off, 625 | once: once, 626 | emit: emit 627 | }; 628 | 629 | },{"synthetic-dom-events":10}],6:[function(require,module,exports){ 630 | 631 | var indexOf = [].indexOf; 632 | 633 | module.exports = function(arr, obj){ 634 | if (indexOf) return arr.indexOf(obj); 635 | for (var i = 0; i < arr.length; ++i) { 636 | if (arr[i] === obj) return i; 637 | } 638 | return -1; 639 | }; 640 | },{}],7:[function(require,module,exports){ 641 | // Generated by CoffeeScript 1.9.2 642 | (function() { 643 | var root; 644 | 645 | root = typeof exports !== "undefined" && exports !== null ? exports : this; 646 | 647 | root.Lethargy = (function() { 648 | function Lethargy(stability, sensitivity, tolerance, delay) { 649 | this.stability = stability != null ? Math.abs(stability) : 8; 650 | this.sensitivity = sensitivity != null ? 1 + Math.abs(sensitivity) : 100; 651 | this.tolerance = tolerance != null ? 1 + Math.abs(tolerance) : 1.1; 652 | this.delay = delay != null ? delay : 150; 653 | this.lastUpDeltas = (function() { 654 | var i, ref, results; 655 | results = []; 656 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 657 | results.push(null); 658 | } 659 | return results; 660 | }).call(this); 661 | this.lastDownDeltas = (function() { 662 | var i, ref, results; 663 | results = []; 664 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 665 | results.push(null); 666 | } 667 | return results; 668 | }).call(this); 669 | this.deltasTimestamp = (function() { 670 | var i, ref, results; 671 | results = []; 672 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 673 | results.push(null); 674 | } 675 | return results; 676 | }).call(this); 677 | } 678 | 679 | Lethargy.prototype.check = function(e) { 680 | var lastDelta; 681 | e = e.originalEvent || e; 682 | if (e.wheelDelta != null) { 683 | lastDelta = e.wheelDelta; 684 | } else if (e.deltaY != null) { 685 | lastDelta = e.deltaY * -40; 686 | } else if ((e.detail != null) || e.detail === 0) { 687 | lastDelta = e.detail * -40; 688 | } 689 | this.deltasTimestamp.push(Date.now()); 690 | this.deltasTimestamp.shift(); 691 | if (lastDelta > 0) { 692 | this.lastUpDeltas.push(lastDelta); 693 | this.lastUpDeltas.shift(); 694 | return this.isInertia(1); 695 | } else { 696 | this.lastDownDeltas.push(lastDelta); 697 | this.lastDownDeltas.shift(); 698 | return this.isInertia(-1); 699 | } 700 | return false; 701 | }; 702 | 703 | Lethargy.prototype.isInertia = function(direction) { 704 | var lastDeltas, lastDeltasNew, lastDeltasOld, newAverage, newSum, oldAverage, oldSum; 705 | lastDeltas = direction === -1 ? this.lastDownDeltas : this.lastUpDeltas; 706 | if (lastDeltas[0] === null) { 707 | return direction; 708 | } 709 | if (this.deltasTimestamp[(this.stability * 2) - 2] + this.delay > Date.now() && lastDeltas[0] === lastDeltas[(this.stability * 2) - 1]) { 710 | return false; 711 | } 712 | lastDeltasOld = lastDeltas.slice(0, this.stability); 713 | lastDeltasNew = lastDeltas.slice(this.stability, this.stability * 2); 714 | oldSum = lastDeltasOld.reduce(function(t, s) { 715 | return t + s; 716 | }); 717 | newSum = lastDeltasNew.reduce(function(t, s) { 718 | return t + s; 719 | }); 720 | oldAverage = oldSum / lastDeltasOld.length; 721 | newAverage = newSum / lastDeltasNew.length; 722 | if (Math.abs(oldAverage) < Math.abs(newAverage * this.tolerance) && (this.sensitivity < Math.abs(newAverage))) { 723 | return direction; 724 | } else { 725 | return false; 726 | } 727 | }; 728 | 729 | Lethargy.prototype.showLastUpDeltas = function() { 730 | return this.lastUpDeltas; 731 | }; 732 | 733 | Lethargy.prototype.showLastDownDeltas = function() { 734 | return this.lastDownDeltas; 735 | }; 736 | 737 | return Lethargy; 738 | 739 | })(); 740 | 741 | }).call(this); 742 | 743 | },{}],8:[function(require,module,exports){ 744 | /* 745 | object-assign 746 | (c) Sindre Sorhus 747 | @license MIT 748 | */ 749 | 750 | 'use strict'; 751 | /* eslint-disable no-unused-vars */ 752 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 753 | var hasOwnProperty = Object.prototype.hasOwnProperty; 754 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 755 | 756 | function toObject(val) { 757 | if (val === null || val === undefined) { 758 | throw new TypeError('Object.assign cannot be called with null or undefined'); 759 | } 760 | 761 | return Object(val); 762 | } 763 | 764 | function shouldUseNative() { 765 | try { 766 | if (!Object.assign) { 767 | return false; 768 | } 769 | 770 | // Detect buggy property enumeration order in older V8 versions. 771 | 772 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 773 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 774 | test1[5] = 'de'; 775 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 776 | return false; 777 | } 778 | 779 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 780 | var test2 = {}; 781 | for (var i = 0; i < 10; i++) { 782 | test2['_' + String.fromCharCode(i)] = i; 783 | } 784 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 785 | return test2[n]; 786 | }); 787 | if (order2.join('') !== '0123456789') { 788 | return false; 789 | } 790 | 791 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 792 | var test3 = {}; 793 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 794 | test3[letter] = letter; 795 | }); 796 | if (Object.keys(Object.assign({}, test3)).join('') !== 797 | 'abcdefghijklmnopqrst') { 798 | return false; 799 | } 800 | 801 | return true; 802 | } catch (err) { 803 | // We don't expect any of the above to throw, but better to be safe. 804 | return false; 805 | } 806 | } 807 | 808 | module.exports = shouldUseNative() ? Object.assign : function (target, source) { 809 | var from; 810 | var to = toObject(target); 811 | var symbols; 812 | 813 | for (var s = 1; s < arguments.length; s++) { 814 | from = Object(arguments[s]); 815 | 816 | for (var key in from) { 817 | if (hasOwnProperty.call(from, key)) { 818 | to[key] = from[key]; 819 | } 820 | } 821 | 822 | if (getOwnPropertySymbols) { 823 | symbols = getOwnPropertySymbols(from); 824 | for (var i = 0; i < symbols.length; i++) { 825 | if (propIsEnumerable.call(from, symbols[i])) { 826 | to[symbols[i]] = from[symbols[i]]; 827 | } 828 | } 829 | } 830 | } 831 | 832 | return to; 833 | }; 834 | 835 | },{}],9:[function(require,module,exports){ 836 | // check document first so it doesn't error in node.js 837 | var style = typeof document != 'undefined' 838 | ? document.createElement('p').style 839 | : {} 840 | 841 | var prefixes = ['O', 'ms', 'Moz', 'Webkit'] 842 | var upper = /([A-Z])/g 843 | var memo = {} 844 | 845 | /** 846 | * prefix `key` 847 | * 848 | * prefix('transform') // => WebkitTransform 849 | * 850 | * @param {String} key 851 | * @return {String} 852 | * @api public 853 | */ 854 | function prefix(key){ 855 | // Camel case 856 | key = key.replace(/-([a-z])/g, function(_, char){ 857 | return char.toUpperCase() 858 | }) 859 | 860 | // Without prefix 861 | if (style[key] !== undefined) return key 862 | 863 | // With prefix 864 | var Key = key.charAt(0).toUpperCase() + key.slice(1) 865 | var i = prefixes.length 866 | while (i--) { 867 | var name = prefixes[i] + Key 868 | if (style[name] !== undefined) return name 869 | } 870 | 871 | return key 872 | } 873 | 874 | /** 875 | * Memoized version of `prefix` 876 | * 877 | * @param {String} key 878 | * @return {String} 879 | * @api public 880 | */ 881 | function prefixMemozied(key){ 882 | return key in memo 883 | ? memo[key] 884 | : memo[key] = prefix(key) 885 | } 886 | 887 | /** 888 | * Create a dashed prefix 889 | * 890 | * @param {String} key 891 | * @return {String} 892 | * @api public 893 | */ 894 | function prefixDashed(key){ 895 | key = prefix(key) 896 | if (upper.test(key)) { 897 | key = '-' + key.replace(upper, '-$1') 898 | upper.lastIndex = 0 899 | } 900 | return key.toLowerCase() 901 | } 902 | 903 | module.exports = prefixMemozied 904 | module.exports.dash = prefixDashed 905 | 906 | },{}],10:[function(require,module,exports){ 907 | 908 | // for compression 909 | var win = window; 910 | var doc = document || {}; 911 | var root = doc.documentElement || {}; 912 | 913 | // detect if we need to use firefox KeyEvents vs KeyboardEvents 914 | var use_key_event = true; 915 | try { 916 | doc.createEvent('KeyEvents'); 917 | } 918 | catch (err) { 919 | use_key_event = false; 920 | } 921 | 922 | // Workaround for https://bugs.webkit.org/show_bug.cgi?id=16735 923 | function check_kb(ev, opts) { 924 | if (ev.ctrlKey != (opts.ctrlKey || false) || 925 | ev.altKey != (opts.altKey || false) || 926 | ev.shiftKey != (opts.shiftKey || false) || 927 | ev.metaKey != (opts.metaKey || false) || 928 | ev.keyCode != (opts.keyCode || 0) || 929 | ev.charCode != (opts.charCode || 0)) { 930 | 931 | ev = document.createEvent('Event'); 932 | ev.initEvent(opts.type, opts.bubbles, opts.cancelable); 933 | ev.ctrlKey = opts.ctrlKey || false; 934 | ev.altKey = opts.altKey || false; 935 | ev.shiftKey = opts.shiftKey || false; 936 | ev.metaKey = opts.metaKey || false; 937 | ev.keyCode = opts.keyCode || 0; 938 | ev.charCode = opts.charCode || 0; 939 | } 940 | 941 | return ev; 942 | } 943 | 944 | // modern browsers, do a proper dispatchEvent() 945 | var modern = function(type, opts) { 946 | opts = opts || {}; 947 | 948 | // which init fn do we use 949 | var family = typeOf(type); 950 | var init_fam = family; 951 | if (family === 'KeyboardEvent' && use_key_event) { 952 | family = 'KeyEvents'; 953 | init_fam = 'KeyEvent'; 954 | } 955 | 956 | var ev = doc.createEvent(family); 957 | var init_fn = 'init' + init_fam; 958 | var init = typeof ev[init_fn] === 'function' ? init_fn : 'initEvent'; 959 | 960 | var sig = initSignatures[init]; 961 | var args = []; 962 | var used = {}; 963 | 964 | opts.type = type; 965 | for (var i = 0; i < sig.length; ++i) { 966 | var key = sig[i]; 967 | var val = opts[key]; 968 | // if no user specified value, then use event default 969 | if (val === undefined) { 970 | val = ev[key]; 971 | } 972 | used[key] = true; 973 | args.push(val); 974 | } 975 | ev[init].apply(ev, args); 976 | 977 | // webkit key event issue workaround 978 | if (family === 'KeyboardEvent') { 979 | ev = check_kb(ev, opts); 980 | } 981 | 982 | // attach remaining unused options to the object 983 | for (var key in opts) { 984 | if (!used[key]) { 985 | ev[key] = opts[key]; 986 | } 987 | } 988 | 989 | return ev; 990 | }; 991 | 992 | var legacy = function (type, opts) { 993 | opts = opts || {}; 994 | var ev = doc.createEventObject(); 995 | 996 | ev.type = type; 997 | for (var key in opts) { 998 | if (opts[key] !== undefined) { 999 | ev[key] = opts[key]; 1000 | } 1001 | } 1002 | 1003 | return ev; 1004 | }; 1005 | 1006 | // expose either the modern version of event generation or legacy 1007 | // depending on what we support 1008 | // avoids if statements in the code later 1009 | module.exports = doc.createEvent ? modern : legacy; 1010 | 1011 | var initSignatures = require('./init.json'); 1012 | var types = require('./types.json'); 1013 | var typeOf = (function () { 1014 | var typs = {}; 1015 | for (var key in types) { 1016 | var ts = types[key]; 1017 | for (var i = 0; i < ts.length; i++) { 1018 | typs[ts[i]] = key; 1019 | } 1020 | } 1021 | 1022 | return function (name) { 1023 | return typs[name] || 'Event'; 1024 | }; 1025 | })(); 1026 | 1027 | },{"./init.json":11,"./types.json":12}],11:[function(require,module,exports){ 1028 | module.exports={ 1029 | "initEvent" : [ 1030 | "type", 1031 | "bubbles", 1032 | "cancelable" 1033 | ], 1034 | "initUIEvent" : [ 1035 | "type", 1036 | "bubbles", 1037 | "cancelable", 1038 | "view", 1039 | "detail" 1040 | ], 1041 | "initMouseEvent" : [ 1042 | "type", 1043 | "bubbles", 1044 | "cancelable", 1045 | "view", 1046 | "detail", 1047 | "screenX", 1048 | "screenY", 1049 | "clientX", 1050 | "clientY", 1051 | "ctrlKey", 1052 | "altKey", 1053 | "shiftKey", 1054 | "metaKey", 1055 | "button", 1056 | "relatedTarget" 1057 | ], 1058 | "initMutationEvent" : [ 1059 | "type", 1060 | "bubbles", 1061 | "cancelable", 1062 | "relatedNode", 1063 | "prevValue", 1064 | "newValue", 1065 | "attrName", 1066 | "attrChange" 1067 | ], 1068 | "initKeyboardEvent" : [ 1069 | "type", 1070 | "bubbles", 1071 | "cancelable", 1072 | "view", 1073 | "ctrlKey", 1074 | "altKey", 1075 | "shiftKey", 1076 | "metaKey", 1077 | "keyCode", 1078 | "charCode" 1079 | ], 1080 | "initKeyEvent" : [ 1081 | "type", 1082 | "bubbles", 1083 | "cancelable", 1084 | "view", 1085 | "ctrlKey", 1086 | "altKey", 1087 | "shiftKey", 1088 | "metaKey", 1089 | "keyCode", 1090 | "charCode" 1091 | ] 1092 | } 1093 | 1094 | },{}],12:[function(require,module,exports){ 1095 | module.exports={ 1096 | "MouseEvent" : [ 1097 | "click", 1098 | "mousedown", 1099 | "mouseup", 1100 | "mouseover", 1101 | "mousemove", 1102 | "mouseout" 1103 | ], 1104 | "KeyboardEvent" : [ 1105 | "keydown", 1106 | "keyup", 1107 | "keypress" 1108 | ], 1109 | "MutationEvent" : [ 1110 | "DOMSubtreeModified", 1111 | "DOMNodeInserted", 1112 | "DOMNodeRemoved", 1113 | "DOMNodeRemovedFromDocument", 1114 | "DOMNodeInsertedIntoDocument", 1115 | "DOMAttrModified", 1116 | "DOMCharacterDataModified" 1117 | ], 1118 | "HTMLEvents" : [ 1119 | "load", 1120 | "unload", 1121 | "abort", 1122 | "error", 1123 | "select", 1124 | "change", 1125 | "submit", 1126 | "reset", 1127 | "focus", 1128 | "blur", 1129 | "resize", 1130 | "scroll" 1131 | ], 1132 | "UIEvent" : [ 1133 | "DOMFocusIn", 1134 | "DOMFocusOut", 1135 | "DOMActivate" 1136 | ] 1137 | } 1138 | 1139 | },{}],13:[function(require,module,exports){ 1140 | function E () { 1141 | // Keep this empty so it's easier to inherit from 1142 | // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 1143 | } 1144 | 1145 | E.prototype = { 1146 | on: function (name, callback, ctx) { 1147 | var e = this.e || (this.e = {}); 1148 | 1149 | (e[name] || (e[name] = [])).push({ 1150 | fn: callback, 1151 | ctx: ctx 1152 | }); 1153 | 1154 | return this; 1155 | }, 1156 | 1157 | once: function (name, callback, ctx) { 1158 | var self = this; 1159 | function listener () { 1160 | self.off(name, listener); 1161 | callback.apply(ctx, arguments); 1162 | }; 1163 | 1164 | listener._ = callback 1165 | return this.on(name, listener, ctx); 1166 | }, 1167 | 1168 | emit: function (name) { 1169 | var data = [].slice.call(arguments, 1); 1170 | var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 1171 | var i = 0; 1172 | var len = evtArr.length; 1173 | 1174 | for (i; i < len; i++) { 1175 | evtArr[i].fn.apply(evtArr[i].ctx, data); 1176 | } 1177 | 1178 | return this; 1179 | }, 1180 | 1181 | off: function (name, callback) { 1182 | var e = this.e || (this.e = {}); 1183 | var evts = e[name]; 1184 | var liveEvents = []; 1185 | 1186 | if (evts && callback) { 1187 | for (var i = 0, len = evts.length; i < len; i++) { 1188 | if (evts[i].fn !== callback && evts[i].fn._ !== callback) 1189 | liveEvents.push(evts[i]); 1190 | } 1191 | } 1192 | 1193 | // Remove event from queue to prevent memory leak 1194 | // Suggested by https://github.com/lazd 1195 | // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 1196 | 1197 | (liveEvents.length) 1198 | ? e[name] = liveEvents 1199 | : delete e[name]; 1200 | 1201 | return this; 1202 | } 1203 | }; 1204 | 1205 | module.exports = E; 1206 | 1207 | },{}],14:[function(require,module,exports){ 1208 | 'use strict'; 1209 | 1210 | module.exports = function(source) { 1211 | return JSON.parse(JSON.stringify(source)); 1212 | }; 1213 | },{}],15:[function(require,module,exports){ 1214 | 'use strict'; 1215 | 1216 | var objectAssign = require('object-assign'); 1217 | var Emitter = require('tiny-emitter'); 1218 | var Lethargy = require('lethargy').Lethargy; 1219 | var support = require('./support'); 1220 | var clone = require('./clone'); 1221 | var bindAll = require('bindall-standalone'); 1222 | var EVT_ID = 'virtualscroll'; 1223 | 1224 | module.exports = VirtualScroll; 1225 | 1226 | var keyCodes = { 1227 | LEFT: 37, 1228 | UP: 38, 1229 | RIGHT: 39, 1230 | DOWN: 40, 1231 | SPACE: 32 1232 | }; 1233 | 1234 | function VirtualScroll(options) { 1235 | bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown'); 1236 | 1237 | this.el = window; 1238 | if (options && options.el) { 1239 | this.el = options.el; 1240 | delete options.el; 1241 | } 1242 | this.options = objectAssign({ 1243 | mouseMultiplier: 1, 1244 | touchMultiplier: 2, 1245 | firefoxMultiplier: 15, 1246 | keyStep: 120, 1247 | preventTouch: false, 1248 | unpreventTouchClass: 'vs-touchmove-allowed', 1249 | limitInertia: false 1250 | }, options); 1251 | 1252 | if (this.options.limitInertia) this._lethargy = new Lethargy(); 1253 | 1254 | this._emitter = new Emitter(); 1255 | this._event = { 1256 | y: 0, 1257 | x: 0, 1258 | deltaX: 0, 1259 | deltaY: 0 1260 | }; 1261 | this.touchStartX = null; 1262 | this.touchStartY = null; 1263 | this.bodyTouchAction = null; 1264 | 1265 | if (this.options.passive !== undefined) { 1266 | this.listenerOptions = {passive: this.options.passive}; 1267 | } 1268 | } 1269 | 1270 | VirtualScroll.prototype._notify = function(e) { 1271 | var evt = this._event; 1272 | evt.x += evt.deltaX; 1273 | evt.y += evt.deltaY; 1274 | 1275 | this._emitter.emit(EVT_ID, { 1276 | x: evt.x, 1277 | y: evt.y, 1278 | deltaX: evt.deltaX, 1279 | deltaY: evt.deltaY, 1280 | originalEvent: e 1281 | }); 1282 | }; 1283 | 1284 | VirtualScroll.prototype._onWheel = function(e) { 1285 | var options = this.options; 1286 | if (this._lethargy && this._lethargy.check(e) === false) return; 1287 | var evt = this._event; 1288 | 1289 | // In Chrome and in Firefox (at least the new one) 1290 | evt.deltaX = e.wheelDeltaX || e.deltaX * -1; 1291 | evt.deltaY = e.wheelDeltaY || e.deltaY * -1; 1292 | 1293 | // for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad 1294 | // real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes 1295 | if(support.isFirefox && e.deltaMode == 1) { 1296 | evt.deltaX *= options.firefoxMultiplier; 1297 | evt.deltaY *= options.firefoxMultiplier; 1298 | } 1299 | 1300 | evt.deltaX *= options.mouseMultiplier; 1301 | evt.deltaY *= options.mouseMultiplier; 1302 | 1303 | this._notify(e); 1304 | }; 1305 | 1306 | VirtualScroll.prototype._onMouseWheel = function(e) { 1307 | if (this.options.limitInertia && this._lethargy.check(e) === false) return; 1308 | 1309 | var evt = this._event; 1310 | 1311 | // In Safari, IE and in Chrome if 'wheel' isn't defined 1312 | evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0; 1313 | evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta; 1314 | 1315 | this._notify(e); 1316 | }; 1317 | 1318 | VirtualScroll.prototype._onTouchStart = function(e) { 1319 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1320 | this.touchStartX = t.pageX; 1321 | this.touchStartY = t.pageY; 1322 | }; 1323 | 1324 | VirtualScroll.prototype._onTouchMove = function(e) { 1325 | var options = this.options; 1326 | if(options.preventTouch 1327 | && !e.target.classList.contains(options.unpreventTouchClass)) { 1328 | e.preventDefault(); 1329 | } 1330 | 1331 | var evt = this._event; 1332 | 1333 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1334 | 1335 | evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier; 1336 | evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier; 1337 | 1338 | this.touchStartX = t.pageX; 1339 | this.touchStartY = t.pageY; 1340 | 1341 | this._notify(e); 1342 | }; 1343 | 1344 | VirtualScroll.prototype._onKeyDown = function(e) { 1345 | var evt = this._event; 1346 | evt.deltaX = evt.deltaY = 0; 1347 | var windowHeight = window.innerHeight - 40 1348 | 1349 | switch(e.keyCode) { 1350 | case keyCodes.LEFT: 1351 | case keyCodes.UP: 1352 | evt.deltaY = this.options.keyStep; 1353 | break; 1354 | 1355 | case keyCodes.RIGHT: 1356 | case keyCodes.DOWN: 1357 | evt.deltaY = - this.options.keyStep; 1358 | break; 1359 | case keyCodes.SPACE && e.shiftKey: 1360 | evt.deltaY = windowHeight; 1361 | break; 1362 | case keyCodes.SPACE: 1363 | evt.deltaY = - windowHeight; 1364 | break; 1365 | default: 1366 | return; 1367 | } 1368 | 1369 | this._notify(e); 1370 | }; 1371 | 1372 | VirtualScroll.prototype._bind = function() { 1373 | if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel, this.listenerOptions); 1374 | if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel, this.listenerOptions); 1375 | 1376 | if(support.hasTouch) { 1377 | this.el.addEventListener('touchstart', this._onTouchStart, this.listenerOptions); 1378 | this.el.addEventListener('touchmove', this._onTouchMove, this.listenerOptions); 1379 | } 1380 | 1381 | if(support.hasPointer && support.hasTouchWin) { 1382 | this.bodyTouchAction = document.body.style.msTouchAction; 1383 | document.body.style.msTouchAction = 'none'; 1384 | this.el.addEventListener('MSPointerDown', this._onTouchStart, true); 1385 | this.el.addEventListener('MSPointerMove', this._onTouchMove, true); 1386 | } 1387 | 1388 | if(support.hasKeyDown) document.addEventListener('keydown', this._onKeyDown); 1389 | }; 1390 | 1391 | VirtualScroll.prototype._unbind = function() { 1392 | if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel); 1393 | if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel); 1394 | 1395 | if(support.hasTouch) { 1396 | this.el.removeEventListener('touchstart', this._onTouchStart); 1397 | this.el.removeEventListener('touchmove', this._onTouchMove); 1398 | } 1399 | 1400 | if(support.hasPointer && support.hasTouchWin) { 1401 | document.body.style.msTouchAction = this.bodyTouchAction; 1402 | this.el.removeEventListener('MSPointerDown', this._onTouchStart, true); 1403 | this.el.removeEventListener('MSPointerMove', this._onTouchMove, true); 1404 | } 1405 | 1406 | if(support.hasKeyDown) document.removeEventListener('keydown', this._onKeyDown); 1407 | }; 1408 | 1409 | VirtualScroll.prototype.on = function(cb, ctx) { 1410 | this._emitter.on(EVT_ID, cb, ctx); 1411 | 1412 | var events = this._emitter.e; 1413 | if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind(); 1414 | }; 1415 | 1416 | VirtualScroll.prototype.off = function(cb, ctx) { 1417 | this._emitter.off(EVT_ID, cb, ctx); 1418 | 1419 | var events = this._emitter.e; 1420 | if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind(); 1421 | }; 1422 | 1423 | VirtualScroll.prototype.reset = function() { 1424 | var evt = this._event; 1425 | evt.x = 0; 1426 | evt.y = 0; 1427 | }; 1428 | 1429 | VirtualScroll.prototype.destroy = function() { 1430 | this._emitter.off(); 1431 | this._unbind(); 1432 | }; 1433 | 1434 | },{"./clone":14,"./support":16,"bindall-standalone":2,"lethargy":7,"object-assign":8,"tiny-emitter":13}],16:[function(require,module,exports){ 1435 | 'use strict'; 1436 | 1437 | module.exports = (function getSupport() { 1438 | return { 1439 | hasWheelEvent: 'onwheel' in document, 1440 | hasMouseWheelEvent: 'onmousewheel' in document, 1441 | hasTouch: 'ontouchstart' in document, 1442 | hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1, 1443 | hasPointer: !!window.navigator.msPointerEnabled, 1444 | hasKeyDown: 'onkeydown' in document, 1445 | isFirefox: navigator.userAgent.indexOf('Firefox') > -1 1446 | }; 1447 | })(); 1448 | 1449 | },{}]},{},[1]); 1450 | -------------------------------------------------------------------------------- /demos/scale/build.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}; 124 | 125 | _classCallCheck(this, Smooth); 126 | 127 | this.createBound(); 128 | this.options = opt; 129 | this.prefix = (0, _prefix["default"])('transform'); 130 | this.rAF = undefined; // It seems that under heavy load, Firefox will still call the RAF callback even though the RAF has been canceled 131 | // To prevent that we set a flag to prevent any callback to be executed when RAF is removed 132 | 133 | this.isRAFCanceled = false; 134 | var constructorName = this.constructor.name ? this.constructor.name : 'Smooth'; 135 | this["extends"] = typeof opt["extends"] === 'undefined' ? this.constructor !== Smooth : opt["extends"]; 136 | this.callback = this.options.callback || null; 137 | this.vars = { 138 | direction: this.options.direction || 'vertical', 139 | "native": this.options["native"] || false, 140 | ease: this.options.ease || 0.075, 141 | preload: this.options.preload || false, 142 | current: 0, 143 | last: 0, 144 | target: 0, 145 | height: window.innerHeight, 146 | width: window.innerWidth, 147 | bounding: 0, 148 | timer: null, 149 | ticking: false 150 | }; 151 | this.vs = this.vars["native"] ? null : new _virtualScroll["default"]({ 152 | limitInertia: this.options.vs && this.options.vs.limitInertia || false, 153 | mouseMultiplier: this.options.vs && this.options.vs.mouseMultiplier || 1, 154 | touchMultiplier: this.options.vs && this.options.vs.touchMultiplier || 1.5, 155 | firefoxMultiplier: this.options.vs && this.options.vs.firefoxMultiplier || 30, 156 | preventTouch: this.options.vs && this.options.vs.preventTouch || true 157 | }); 158 | this.dom = { 159 | listener: this.options.listener || document.body, 160 | section: this.options.section || document.querySelector('.vs-section') || null, 161 | scrollbar: this.vars["native"] || this.options.noscrollbar ? null : { 162 | state: { 163 | clicked: false, 164 | x: 0 165 | }, 166 | el: (0, _domCreateElement["default"])({ 167 | selector: 'div', 168 | styles: "vs-scrollbar vs-".concat(this.vars.direction, " vs-scrollbar-").concat(constructorName.toLowerCase()) 169 | }), 170 | drag: { 171 | el: (0, _domCreateElement["default"])({ 172 | selector: 'div', 173 | styles: 'vs-scrolldrag' 174 | }), 175 | delta: 0, 176 | height: 50 177 | } 178 | } 179 | }; 180 | } 181 | 182 | _createClass(Smooth, [{ 183 | key: "createBound", 184 | value: function createBound() { 185 | var _this = this; 186 | 187 | ['run', 'calc', 'debounce', 'resize', 'mouseUp', 'mouseDown', 'mouseMove', 'calcScroll', 'scrollTo'].forEach(function (fn) { 188 | return _this[fn] = _this[fn].bind(_this); 189 | }); 190 | } 191 | }, { 192 | key: "init", 193 | value: function init() { 194 | this.addClasses(); 195 | this.vars.preload && this.preloadImages(); 196 | this.vars["native"] ? this.addFakeScrollHeight() : !this.options.noscrollbar && this.addFakeScrollBar(); 197 | this.addEvents(); 198 | this.resize(); 199 | } 200 | }, { 201 | key: "addClasses", 202 | value: function addClasses() { 203 | var type = this.vars["native"] ? 'native' : 'virtual'; 204 | var direction = this.vars.direction === 'vertical' ? 'y' : 'x'; 205 | 206 | _domClasses["default"].add(this.dom.listener, "is-".concat(type, "-scroll")); 207 | 208 | _domClasses["default"].add(this.dom.listener, "".concat(direction, "-scroll")); 209 | } 210 | }, { 211 | key: "preloadImages", 212 | value: function preloadImages() { 213 | var _this2 = this; 214 | 215 | var images = Array.prototype.slice.call(this.dom.listener.querySelectorAll('img'), 0); 216 | images.forEach(function (image) { 217 | var img = document.createElement('img'); 218 | 219 | _domEvents["default"].once(img, 'load', function () { 220 | images.splice(images.indexOf(image), 1); 221 | images.length === 0 && _this2.resize(); 222 | }); 223 | 224 | img.src = image.getAttribute('src'); 225 | }); 226 | } 227 | }, { 228 | key: "calc", 229 | value: function calc(e) { 230 | var delta = this.vars.direction == 'horizontal' ? e.deltaX : e.deltaY; 231 | this.vars.target += delta * -1; 232 | this.clampTarget(); 233 | } 234 | }, { 235 | key: "debounce", 236 | value: function debounce() { 237 | var _this3 = this; 238 | 239 | var win = this.dom.listener === document.body; 240 | this.vars.target = this.vars.direction === 'vertical' ? win ? window.scrollY || window.pageYOffset : this.dom.listener.scrollTop : win ? window.scrollX || window.pageXOffset : this.dom.listener.scrollLeft; 241 | clearTimeout(this.vars.timer); 242 | 243 | if (!this.vars.ticking) { 244 | this.vars.ticking = true; 245 | 246 | _domClasses["default"].add(this.dom.listener, 'is-scrolling'); 247 | } 248 | 249 | this.vars.timer = setTimeout(function () { 250 | _this3.vars.ticking = false; 251 | 252 | _domClasses["default"].remove(_this3.dom.listener, 'is-scrolling'); 253 | }, 200); 254 | } 255 | }, { 256 | key: "run", 257 | value: function run() { 258 | if (this.isRAFCanceled) return; 259 | this.vars.current += (this.vars.target - this.vars.current) * this.vars.ease; 260 | this.vars.current < .1 && (this.vars.current = 0); 261 | this.requestAnimationFrame(); 262 | 263 | if (!this["extends"]) { 264 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)); 265 | } 266 | 267 | if (!this.vars["native"] && !this.options.noscrollbar) { 268 | var size = this.dom.scrollbar.drag.height; 269 | var bounds = this.vars.direction === 'vertical' ? this.vars.height : this.vars.width; 270 | var value = Math.abs(this.vars.current) / (this.vars.bounding / (bounds - size)) + size / .5 - size; 271 | var clamp = Math.max(0, Math.min(value - size, value + size)); 272 | this.dom.scrollbar.drag.el.style[this.prefix] = this.getTransform(clamp.toFixed(2)); 273 | } 274 | 275 | if (this.callback && this.vars.current !== this.vars.last) { 276 | this.callback(this.vars.current); 277 | } 278 | 279 | this.vars.last = this.vars.current; 280 | } 281 | }, { 282 | key: "getTransform", 283 | value: function getTransform(value) { 284 | return this.vars.direction === 'vertical' ? "translate3d(0,".concat(value, "px,0)") : "translate3d(".concat(value, "px,0,0)"); 285 | } 286 | }, { 287 | key: "on", 288 | value: function on() { 289 | var requestAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 290 | 291 | if (this.isRAFCanceled) { 292 | this.isRAFCanceled = false; 293 | } 294 | 295 | var node = this.dom.listener === document.body ? window : this.dom.listener; 296 | this.vars["native"] ? _domEvents["default"].on(node, 'scroll', this.debounce) : this.vs && this.vs.on(this.calc); 297 | requestAnimationFrame && this.requestAnimationFrame(); 298 | } 299 | }, { 300 | key: "off", 301 | value: function off() { 302 | var cancelAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 303 | var node = this.dom.listener === document.body ? window : this.dom.listener; 304 | this.vars["native"] ? _domEvents["default"].off(node, 'scroll', this.debounce) : this.vs && this.vs.off(this.calc); 305 | cancelAnimationFrame && this.cancelAnimationFrame(); 306 | } 307 | }, { 308 | key: "requestAnimationFrame", 309 | value: function (_requestAnimationFrame) { 310 | function requestAnimationFrame() { 311 | return _requestAnimationFrame.apply(this, arguments); 312 | } 313 | 314 | requestAnimationFrame.toString = function () { 315 | return _requestAnimationFrame.toString(); 316 | }; 317 | 318 | return requestAnimationFrame; 319 | }(function () { 320 | this.rAF = requestAnimationFrame(this.run); 321 | }) 322 | }, { 323 | key: "cancelAnimationFrame", 324 | value: function (_cancelAnimationFrame) { 325 | function cancelAnimationFrame() { 326 | return _cancelAnimationFrame.apply(this, arguments); 327 | } 328 | 329 | cancelAnimationFrame.toString = function () { 330 | return _cancelAnimationFrame.toString(); 331 | }; 332 | 333 | return cancelAnimationFrame; 334 | }(function () { 335 | this.isRAFCanceled = true; 336 | cancelAnimationFrame(this.rAF); 337 | }) 338 | }, { 339 | key: "addEvents", 340 | value: function addEvents() { 341 | this.on(); 342 | 343 | _domEvents["default"].on(window, 'resize', this.resize); 344 | } 345 | }, { 346 | key: "removeEvents", 347 | value: function removeEvents() { 348 | this.off(); 349 | 350 | _domEvents["default"].off(window, 'resize', this.resize); 351 | } 352 | }, { 353 | key: "addFakeScrollBar", 354 | value: function addFakeScrollBar() { 355 | this.dom.listener.appendChild(this.dom.scrollbar.el); 356 | this.dom.scrollbar.el.appendChild(this.dom.scrollbar.drag.el); 357 | 358 | _domEvents["default"].on(this.dom.scrollbar.el, 'click', this.calcScroll); 359 | 360 | _domEvents["default"].on(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 361 | 362 | _domEvents["default"].on(document, 'mousemove', this.mouseMove); 363 | 364 | _domEvents["default"].on(document, 'mouseup', this.mouseUp); 365 | } 366 | }, { 367 | key: "removeFakeScrollBar", 368 | value: function removeFakeScrollBar() { 369 | _domEvents["default"].off(this.dom.scrollbar.el, 'click', this.calcScroll); 370 | 371 | _domEvents["default"].off(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 372 | 373 | _domEvents["default"].off(document, 'mousemove', this.mouseMove); 374 | 375 | _domEvents["default"].off(document, 'mouseup', this.mouseUp); 376 | 377 | this.dom.listener.removeChild(this.dom.scrollbar.el); 378 | } 379 | }, { 380 | key: "mouseDown", 381 | value: function mouseDown(e) { 382 | e.preventDefault(); 383 | e.which == 1 && (this.dom.scrollbar.state.clicked = true); 384 | } 385 | }, { 386 | key: "mouseUp", 387 | value: function mouseUp(e) { 388 | this.dom.scrollbar.state.clicked = false; 389 | 390 | _domClasses["default"].remove(this.dom.listener, 'is-dragging'); 391 | } 392 | }, { 393 | key: "mouseMove", 394 | value: function mouseMove(e) { 395 | this.dom.scrollbar.state.clicked && this.calcScroll(e); 396 | } 397 | }, { 398 | key: "addFakeScrollHeight", 399 | value: function addFakeScrollHeight() { 400 | this.dom.scroll = (0, _domCreateElement["default"])({ 401 | selector: 'div', 402 | styles: 'vs-scroll-view' 403 | }); 404 | this.dom.listener.appendChild(this.dom.scroll); 405 | } 406 | }, { 407 | key: "removeFakeScrollHeight", 408 | value: function removeFakeScrollHeight() { 409 | this.dom.listener.removeChild(this.dom.scroll); 410 | } 411 | }, { 412 | key: "calcScroll", 413 | value: function calcScroll(e) { 414 | var client = this.vars.direction == 'vertical' ? e.clientY : e.clientX; 415 | var bounds = this.vars.direction == 'vertical' ? this.vars.height : this.vars.width; 416 | var delta = client * (this.vars.bounding / bounds); 417 | 418 | _domClasses["default"].add(this.dom.listener, 'is-dragging'); 419 | 420 | this.vars.target = delta; 421 | this.clampTarget(); 422 | this.dom.scrollbar && (this.dom.scrollbar.drag.delta = this.vars.target); 423 | } 424 | }, { 425 | key: "scrollTo", 426 | value: function scrollTo(offset) { 427 | if (this.vars["native"]) { 428 | this.vars.direction == 'vertical' ? window.scrollTo(0, offset) : window.scrollTo(offset, 0); 429 | } else { 430 | this.vars.target = offset; 431 | this.clampTarget(); 432 | } 433 | } 434 | }, { 435 | key: "resize", 436 | value: function resize() { 437 | var prop = this.vars.direction === 'vertical' ? 'height' : 'width'; 438 | this.vars.height = window.innerHeight; 439 | this.vars.width = window.innerWidth; 440 | 441 | if (!this["extends"]) { 442 | var bounding = this.dom.section.getBoundingClientRect(); 443 | this.vars.bounding = this.vars.direction === 'vertical' ? bounding.height - (this.vars["native"] ? 0 : this.vars.height) : bounding.right - (this.vars["native"] ? 0 : this.vars.width); 444 | } 445 | 446 | if (!this.vars["native"] && !this.options.noscrollbar) { 447 | this.dom.scrollbar.drag.height = this.vars.height * (this.vars.height / (this.vars.bounding + this.vars.height)); 448 | this.dom.scrollbar.drag.el.style[prop] = "".concat(this.dom.scrollbar.drag.height, "px"); 449 | } else if (this.vars["native"]) { 450 | this.dom.scroll.style[prop] = "".concat(this.vars.bounding, "px"); 451 | } 452 | 453 | !this.vars["native"] && this.clampTarget(); 454 | } 455 | }, { 456 | key: "clampTarget", 457 | value: function clampTarget() { 458 | this.vars.target = Math.round(Math.max(0, Math.min(this.vars.target, this.vars.bounding))); 459 | } 460 | }, { 461 | key: "destroy", 462 | value: function destroy() { 463 | if (this.vars["native"]) { 464 | _domClasses["default"].remove(this.dom.listener, 'is-native-scroll'); 465 | 466 | this.removeFakeScrollHeight(); 467 | } else { 468 | _domClasses["default"].remove(this.dom.listener, 'is-virtual-scroll'); 469 | 470 | !this.options.noscrollbar && this.removeFakeScrollBar(); 471 | } 472 | 473 | this.vars.direction === 'vertical' ? _domClasses["default"].remove(this.dom.listener, 'y-scroll') : _domClasses["default"].remove(this.dom.listener, 'x-scroll'); 474 | this.vars.current = 0; 475 | this.vs && (this.vs.destroy(), this.vs = null); 476 | this.removeEvents(); 477 | } 478 | }]); 479 | 480 | return Smooth; 481 | }(); 482 | 483 | exports["default"] = Smooth; 484 | window.Smooth = Smooth; 485 | 486 | },{"dom-classes":5,"dom-create-element":6,"dom-events":7,"prefix":11,"virtual-scroll":17}],4:[function(require,module,exports){ 487 | 'use strict'; 488 | 489 | var toString = Object.prototype.toString, 490 | hasOwnProperty = Object.prototype.hasOwnProperty; 491 | 492 | module.exports = function(object) { 493 | if(!object) return console.warn('bindAll requires at least one argument.'); 494 | 495 | var functions = Array.prototype.slice.call(arguments, 1); 496 | 497 | if (functions.length === 0) { 498 | 499 | for (var method in object) { 500 | if(hasOwnProperty.call(object, method)) { 501 | if(typeof object[method] == 'function' && toString.call(object[method]) == "[object Function]") { 502 | functions.push(method); 503 | } 504 | } 505 | } 506 | } 507 | 508 | for(var i = 0; i < functions.length; i++) { 509 | var f = functions[i]; 510 | object[f] = bind(object[f], object); 511 | } 512 | }; 513 | 514 | /* 515 | Faster bind without specific-case checking. (see https://coderwall.com/p/oi3j3w). 516 | bindAll is only needed for events binding so no need to make slow fixes for constructor 517 | or partial application. 518 | */ 519 | function bind(func, context) { 520 | return function() { 521 | return func.apply(context, arguments); 522 | }; 523 | } 524 | },{}],5:[function(require,module,exports){ 525 | /** 526 | * Module dependencies. 527 | */ 528 | 529 | var index = require('indexof'); 530 | 531 | /** 532 | * Whitespace regexp. 533 | */ 534 | 535 | var whitespaceRe = /\s+/; 536 | 537 | /** 538 | * toString reference. 539 | */ 540 | 541 | var toString = Object.prototype.toString; 542 | 543 | module.exports = classes; 544 | module.exports.add = add; 545 | module.exports.contains = has; 546 | module.exports.has = has; 547 | module.exports.toggle = toggle; 548 | module.exports.remove = remove; 549 | module.exports.removeMatching = removeMatching; 550 | 551 | function classes (el) { 552 | if (el.classList) { 553 | return el.classList; 554 | } 555 | 556 | var str = el.className.replace(/^\s+|\s+$/g, ''); 557 | var arr = str.split(whitespaceRe); 558 | if ('' === arr[0]) arr.shift(); 559 | return arr; 560 | } 561 | 562 | function add (el, name) { 563 | // classList 564 | if (el.classList) { 565 | el.classList.add(name); 566 | return; 567 | } 568 | 569 | // fallback 570 | var arr = classes(el); 571 | var i = index(arr, name); 572 | if (!~i) arr.push(name); 573 | el.className = arr.join(' '); 574 | } 575 | 576 | function has (el, name) { 577 | return el.classList 578 | ? el.classList.contains(name) 579 | : !! ~index(classes(el), name); 580 | } 581 | 582 | function remove (el, name) { 583 | if ('[object RegExp]' == toString.call(name)) { 584 | return removeMatching(el, name); 585 | } 586 | 587 | // classList 588 | if (el.classList) { 589 | el.classList.remove(name); 590 | return; 591 | } 592 | 593 | // fallback 594 | var arr = classes(el); 595 | var i = index(arr, name); 596 | if (~i) arr.splice(i, 1); 597 | el.className = arr.join(' '); 598 | } 599 | 600 | function removeMatching (el, re, ref) { 601 | var arr = Array.prototype.slice.call(classes(el)); 602 | for (var i = 0; i < arr.length; i++) { 603 | if (re.test(arr[i])) { 604 | remove(el, arr[i]); 605 | } 606 | } 607 | } 608 | 609 | function toggle (el, name) { 610 | // classList 611 | if (el.classList) { 612 | return el.classList.toggle(name); 613 | } 614 | 615 | // fallback 616 | if (has(el, name)) { 617 | remove(el, name); 618 | } else { 619 | add(el, name); 620 | } 621 | } 622 | 623 | },{"indexof":8}],6:[function(require,module,exports){ 624 | /* 625 | `dom-create-element` 626 | 627 | var create = require('dom-create-element'); 628 | 629 | var el = create({ 630 | selector: 'div', 631 | styles: 'preloader', 632 | html: 'Text' 633 | }); 634 | */ 635 | 636 | module.exports = create; 637 | 638 | function create(opt) { 639 | 640 | opt = opt || {}; 641 | 642 | var el = document.createElement(opt.selector); 643 | 644 | if(opt.attr) for(var index in opt.attr) 645 | opt.attr.hasOwnProperty(index) && el.setAttribute(index, opt.attr[index]); 646 | 647 | "a" == opt.selector && opt.link && ( 648 | el.href = opt.link, 649 | opt.target && el.setAttribute("target", opt.target) 650 | ); 651 | 652 | "img" == opt.selector && opt.src && ( 653 | el.src = opt.src, 654 | opt.lazyload && ( 655 | el.style.opacity = 0, 656 | el.onload = function(){ 657 | el.style.opacity = 1; 658 | } 659 | ) 660 | ); 661 | 662 | opt.id && (el.id = opt.id); 663 | opt.styles && (el.className = opt.styles); 664 | 665 | opt.html && (el.innerHTML = opt.html); 666 | opt.children && (el.appendChild(opt.children)); 667 | 668 | return el; 669 | }; 670 | },{}],7:[function(require,module,exports){ 671 | 672 | var synth = require('synthetic-dom-events'); 673 | 674 | var on = function(element, name, fn, capture) { 675 | return element.addEventListener(name, fn, capture || false); 676 | }; 677 | 678 | var off = function(element, name, fn, capture) { 679 | return element.removeEventListener(name, fn, capture || false); 680 | }; 681 | 682 | var once = function (element, name, fn, capture) { 683 | function tmp (ev) { 684 | off(element, name, tmp, capture); 685 | fn(ev); 686 | } 687 | on(element, name, tmp, capture); 688 | }; 689 | 690 | var emit = function(element, name, opt) { 691 | var ev = synth(name, opt); 692 | element.dispatchEvent(ev); 693 | }; 694 | 695 | if (!document.addEventListener) { 696 | on = function(element, name, fn) { 697 | return element.attachEvent('on' + name, fn); 698 | }; 699 | } 700 | 701 | if (!document.removeEventListener) { 702 | off = function(element, name, fn) { 703 | return element.detachEvent('on' + name, fn); 704 | }; 705 | } 706 | 707 | if (!document.dispatchEvent) { 708 | emit = function(element, name, opt) { 709 | var ev = synth(name, opt); 710 | return element.fireEvent('on' + ev.type, ev); 711 | }; 712 | } 713 | 714 | module.exports = { 715 | on: on, 716 | off: off, 717 | once: once, 718 | emit: emit 719 | }; 720 | 721 | },{"synthetic-dom-events":12}],8:[function(require,module,exports){ 722 | 723 | var indexOf = [].indexOf; 724 | 725 | module.exports = function(arr, obj){ 726 | if (indexOf) return arr.indexOf(obj); 727 | for (var i = 0; i < arr.length; ++i) { 728 | if (arr[i] === obj) return i; 729 | } 730 | return -1; 731 | }; 732 | },{}],9:[function(require,module,exports){ 733 | // Generated by CoffeeScript 1.9.2 734 | (function() { 735 | var root; 736 | 737 | root = typeof exports !== "undefined" && exports !== null ? exports : this; 738 | 739 | root.Lethargy = (function() { 740 | function Lethargy(stability, sensitivity, tolerance, delay) { 741 | this.stability = stability != null ? Math.abs(stability) : 8; 742 | this.sensitivity = sensitivity != null ? 1 + Math.abs(sensitivity) : 100; 743 | this.tolerance = tolerance != null ? 1 + Math.abs(tolerance) : 1.1; 744 | this.delay = delay != null ? delay : 150; 745 | this.lastUpDeltas = (function() { 746 | var i, ref, results; 747 | results = []; 748 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 749 | results.push(null); 750 | } 751 | return results; 752 | }).call(this); 753 | this.lastDownDeltas = (function() { 754 | var i, ref, results; 755 | results = []; 756 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 757 | results.push(null); 758 | } 759 | return results; 760 | }).call(this); 761 | this.deltasTimestamp = (function() { 762 | var i, ref, results; 763 | results = []; 764 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 765 | results.push(null); 766 | } 767 | return results; 768 | }).call(this); 769 | } 770 | 771 | Lethargy.prototype.check = function(e) { 772 | var lastDelta; 773 | e = e.originalEvent || e; 774 | if (e.wheelDelta != null) { 775 | lastDelta = e.wheelDelta; 776 | } else if (e.deltaY != null) { 777 | lastDelta = e.deltaY * -40; 778 | } else if ((e.detail != null) || e.detail === 0) { 779 | lastDelta = e.detail * -40; 780 | } 781 | this.deltasTimestamp.push(Date.now()); 782 | this.deltasTimestamp.shift(); 783 | if (lastDelta > 0) { 784 | this.lastUpDeltas.push(lastDelta); 785 | this.lastUpDeltas.shift(); 786 | return this.isInertia(1); 787 | } else { 788 | this.lastDownDeltas.push(lastDelta); 789 | this.lastDownDeltas.shift(); 790 | return this.isInertia(-1); 791 | } 792 | return false; 793 | }; 794 | 795 | Lethargy.prototype.isInertia = function(direction) { 796 | var lastDeltas, lastDeltasNew, lastDeltasOld, newAverage, newSum, oldAverage, oldSum; 797 | lastDeltas = direction === -1 ? this.lastDownDeltas : this.lastUpDeltas; 798 | if (lastDeltas[0] === null) { 799 | return direction; 800 | } 801 | if (this.deltasTimestamp[(this.stability * 2) - 2] + this.delay > Date.now() && lastDeltas[0] === lastDeltas[(this.stability * 2) - 1]) { 802 | return false; 803 | } 804 | lastDeltasOld = lastDeltas.slice(0, this.stability); 805 | lastDeltasNew = lastDeltas.slice(this.stability, this.stability * 2); 806 | oldSum = lastDeltasOld.reduce(function(t, s) { 807 | return t + s; 808 | }); 809 | newSum = lastDeltasNew.reduce(function(t, s) { 810 | return t + s; 811 | }); 812 | oldAverage = oldSum / lastDeltasOld.length; 813 | newAverage = newSum / lastDeltasNew.length; 814 | if (Math.abs(oldAverage) < Math.abs(newAverage * this.tolerance) && (this.sensitivity < Math.abs(newAverage))) { 815 | return direction; 816 | } else { 817 | return false; 818 | } 819 | }; 820 | 821 | Lethargy.prototype.showLastUpDeltas = function() { 822 | return this.lastUpDeltas; 823 | }; 824 | 825 | Lethargy.prototype.showLastDownDeltas = function() { 826 | return this.lastDownDeltas; 827 | }; 828 | 829 | return Lethargy; 830 | 831 | })(); 832 | 833 | }).call(this); 834 | 835 | },{}],10:[function(require,module,exports){ 836 | /* 837 | object-assign 838 | (c) Sindre Sorhus 839 | @license MIT 840 | */ 841 | 842 | 'use strict'; 843 | /* eslint-disable no-unused-vars */ 844 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 845 | var hasOwnProperty = Object.prototype.hasOwnProperty; 846 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 847 | 848 | function toObject(val) { 849 | if (val === null || val === undefined) { 850 | throw new TypeError('Object.assign cannot be called with null or undefined'); 851 | } 852 | 853 | return Object(val); 854 | } 855 | 856 | function shouldUseNative() { 857 | try { 858 | if (!Object.assign) { 859 | return false; 860 | } 861 | 862 | // Detect buggy property enumeration order in older V8 versions. 863 | 864 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 865 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 866 | test1[5] = 'de'; 867 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 868 | return false; 869 | } 870 | 871 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 872 | var test2 = {}; 873 | for (var i = 0; i < 10; i++) { 874 | test2['_' + String.fromCharCode(i)] = i; 875 | } 876 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 877 | return test2[n]; 878 | }); 879 | if (order2.join('') !== '0123456789') { 880 | return false; 881 | } 882 | 883 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 884 | var test3 = {}; 885 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 886 | test3[letter] = letter; 887 | }); 888 | if (Object.keys(Object.assign({}, test3)).join('') !== 889 | 'abcdefghijklmnopqrst') { 890 | return false; 891 | } 892 | 893 | return true; 894 | } catch (err) { 895 | // We don't expect any of the above to throw, but better to be safe. 896 | return false; 897 | } 898 | } 899 | 900 | module.exports = shouldUseNative() ? Object.assign : function (target, source) { 901 | var from; 902 | var to = toObject(target); 903 | var symbols; 904 | 905 | for (var s = 1; s < arguments.length; s++) { 906 | from = Object(arguments[s]); 907 | 908 | for (var key in from) { 909 | if (hasOwnProperty.call(from, key)) { 910 | to[key] = from[key]; 911 | } 912 | } 913 | 914 | if (getOwnPropertySymbols) { 915 | symbols = getOwnPropertySymbols(from); 916 | for (var i = 0; i < symbols.length; i++) { 917 | if (propIsEnumerable.call(from, symbols[i])) { 918 | to[symbols[i]] = from[symbols[i]]; 919 | } 920 | } 921 | } 922 | } 923 | 924 | return to; 925 | }; 926 | 927 | },{}],11:[function(require,module,exports){ 928 | // check document first so it doesn't error in node.js 929 | var style = typeof document != 'undefined' 930 | ? document.createElement('p').style 931 | : {} 932 | 933 | var prefixes = ['O', 'ms', 'Moz', 'Webkit'] 934 | var upper = /([A-Z])/g 935 | var memo = {} 936 | 937 | /** 938 | * prefix `key` 939 | * 940 | * prefix('transform') // => WebkitTransform 941 | * 942 | * @param {String} key 943 | * @return {String} 944 | * @api public 945 | */ 946 | function prefix(key){ 947 | // Camel case 948 | key = key.replace(/-([a-z])/g, function(_, char){ 949 | return char.toUpperCase() 950 | }) 951 | 952 | // Without prefix 953 | if (style[key] !== undefined) return key 954 | 955 | // With prefix 956 | var Key = key.charAt(0).toUpperCase() + key.slice(1) 957 | var i = prefixes.length 958 | while (i--) { 959 | var name = prefixes[i] + Key 960 | if (style[name] !== undefined) return name 961 | } 962 | 963 | return key 964 | } 965 | 966 | /** 967 | * Memoized version of `prefix` 968 | * 969 | * @param {String} key 970 | * @return {String} 971 | * @api public 972 | */ 973 | function prefixMemozied(key){ 974 | return key in memo 975 | ? memo[key] 976 | : memo[key] = prefix(key) 977 | } 978 | 979 | /** 980 | * Create a dashed prefix 981 | * 982 | * @param {String} key 983 | * @return {String} 984 | * @api public 985 | */ 986 | function prefixDashed(key){ 987 | key = prefix(key) 988 | if (upper.test(key)) { 989 | key = '-' + key.replace(upper, '-$1') 990 | upper.lastIndex = 0 991 | } 992 | return key.toLowerCase() 993 | } 994 | 995 | module.exports = prefixMemozied 996 | module.exports.dash = prefixDashed 997 | 998 | },{}],12:[function(require,module,exports){ 999 | 1000 | // for compression 1001 | var win = window; 1002 | var doc = document || {}; 1003 | var root = doc.documentElement || {}; 1004 | 1005 | // detect if we need to use firefox KeyEvents vs KeyboardEvents 1006 | var use_key_event = true; 1007 | try { 1008 | doc.createEvent('KeyEvents'); 1009 | } 1010 | catch (err) { 1011 | use_key_event = false; 1012 | } 1013 | 1014 | // Workaround for https://bugs.webkit.org/show_bug.cgi?id=16735 1015 | function check_kb(ev, opts) { 1016 | if (ev.ctrlKey != (opts.ctrlKey || false) || 1017 | ev.altKey != (opts.altKey || false) || 1018 | ev.shiftKey != (opts.shiftKey || false) || 1019 | ev.metaKey != (opts.metaKey || false) || 1020 | ev.keyCode != (opts.keyCode || 0) || 1021 | ev.charCode != (opts.charCode || 0)) { 1022 | 1023 | ev = document.createEvent('Event'); 1024 | ev.initEvent(opts.type, opts.bubbles, opts.cancelable); 1025 | ev.ctrlKey = opts.ctrlKey || false; 1026 | ev.altKey = opts.altKey || false; 1027 | ev.shiftKey = opts.shiftKey || false; 1028 | ev.metaKey = opts.metaKey || false; 1029 | ev.keyCode = opts.keyCode || 0; 1030 | ev.charCode = opts.charCode || 0; 1031 | } 1032 | 1033 | return ev; 1034 | } 1035 | 1036 | // modern browsers, do a proper dispatchEvent() 1037 | var modern = function(type, opts) { 1038 | opts = opts || {}; 1039 | 1040 | // which init fn do we use 1041 | var family = typeOf(type); 1042 | var init_fam = family; 1043 | if (family === 'KeyboardEvent' && use_key_event) { 1044 | family = 'KeyEvents'; 1045 | init_fam = 'KeyEvent'; 1046 | } 1047 | 1048 | var ev = doc.createEvent(family); 1049 | var init_fn = 'init' + init_fam; 1050 | var init = typeof ev[init_fn] === 'function' ? init_fn : 'initEvent'; 1051 | 1052 | var sig = initSignatures[init]; 1053 | var args = []; 1054 | var used = {}; 1055 | 1056 | opts.type = type; 1057 | for (var i = 0; i < sig.length; ++i) { 1058 | var key = sig[i]; 1059 | var val = opts[key]; 1060 | // if no user specified value, then use event default 1061 | if (val === undefined) { 1062 | val = ev[key]; 1063 | } 1064 | used[key] = true; 1065 | args.push(val); 1066 | } 1067 | ev[init].apply(ev, args); 1068 | 1069 | // webkit key event issue workaround 1070 | if (family === 'KeyboardEvent') { 1071 | ev = check_kb(ev, opts); 1072 | } 1073 | 1074 | // attach remaining unused options to the object 1075 | for (var key in opts) { 1076 | if (!used[key]) { 1077 | ev[key] = opts[key]; 1078 | } 1079 | } 1080 | 1081 | return ev; 1082 | }; 1083 | 1084 | var legacy = function (type, opts) { 1085 | opts = opts || {}; 1086 | var ev = doc.createEventObject(); 1087 | 1088 | ev.type = type; 1089 | for (var key in opts) { 1090 | if (opts[key] !== undefined) { 1091 | ev[key] = opts[key]; 1092 | } 1093 | } 1094 | 1095 | return ev; 1096 | }; 1097 | 1098 | // expose either the modern version of event generation or legacy 1099 | // depending on what we support 1100 | // avoids if statements in the code later 1101 | module.exports = doc.createEvent ? modern : legacy; 1102 | 1103 | var initSignatures = require('./init.json'); 1104 | var types = require('./types.json'); 1105 | var typeOf = (function () { 1106 | var typs = {}; 1107 | for (var key in types) { 1108 | var ts = types[key]; 1109 | for (var i = 0; i < ts.length; i++) { 1110 | typs[ts[i]] = key; 1111 | } 1112 | } 1113 | 1114 | return function (name) { 1115 | return typs[name] || 'Event'; 1116 | }; 1117 | })(); 1118 | 1119 | },{"./init.json":13,"./types.json":14}],13:[function(require,module,exports){ 1120 | module.exports={ 1121 | "initEvent" : [ 1122 | "type", 1123 | "bubbles", 1124 | "cancelable" 1125 | ], 1126 | "initUIEvent" : [ 1127 | "type", 1128 | "bubbles", 1129 | "cancelable", 1130 | "view", 1131 | "detail" 1132 | ], 1133 | "initMouseEvent" : [ 1134 | "type", 1135 | "bubbles", 1136 | "cancelable", 1137 | "view", 1138 | "detail", 1139 | "screenX", 1140 | "screenY", 1141 | "clientX", 1142 | "clientY", 1143 | "ctrlKey", 1144 | "altKey", 1145 | "shiftKey", 1146 | "metaKey", 1147 | "button", 1148 | "relatedTarget" 1149 | ], 1150 | "initMutationEvent" : [ 1151 | "type", 1152 | "bubbles", 1153 | "cancelable", 1154 | "relatedNode", 1155 | "prevValue", 1156 | "newValue", 1157 | "attrName", 1158 | "attrChange" 1159 | ], 1160 | "initKeyboardEvent" : [ 1161 | "type", 1162 | "bubbles", 1163 | "cancelable", 1164 | "view", 1165 | "ctrlKey", 1166 | "altKey", 1167 | "shiftKey", 1168 | "metaKey", 1169 | "keyCode", 1170 | "charCode" 1171 | ], 1172 | "initKeyEvent" : [ 1173 | "type", 1174 | "bubbles", 1175 | "cancelable", 1176 | "view", 1177 | "ctrlKey", 1178 | "altKey", 1179 | "shiftKey", 1180 | "metaKey", 1181 | "keyCode", 1182 | "charCode" 1183 | ] 1184 | } 1185 | 1186 | },{}],14:[function(require,module,exports){ 1187 | module.exports={ 1188 | "MouseEvent" : [ 1189 | "click", 1190 | "mousedown", 1191 | "mouseup", 1192 | "mouseover", 1193 | "mousemove", 1194 | "mouseout" 1195 | ], 1196 | "KeyboardEvent" : [ 1197 | "keydown", 1198 | "keyup", 1199 | "keypress" 1200 | ], 1201 | "MutationEvent" : [ 1202 | "DOMSubtreeModified", 1203 | "DOMNodeInserted", 1204 | "DOMNodeRemoved", 1205 | "DOMNodeRemovedFromDocument", 1206 | "DOMNodeInsertedIntoDocument", 1207 | "DOMAttrModified", 1208 | "DOMCharacterDataModified" 1209 | ], 1210 | "HTMLEvents" : [ 1211 | "load", 1212 | "unload", 1213 | "abort", 1214 | "error", 1215 | "select", 1216 | "change", 1217 | "submit", 1218 | "reset", 1219 | "focus", 1220 | "blur", 1221 | "resize", 1222 | "scroll" 1223 | ], 1224 | "UIEvent" : [ 1225 | "DOMFocusIn", 1226 | "DOMFocusOut", 1227 | "DOMActivate" 1228 | ] 1229 | } 1230 | 1231 | },{}],15:[function(require,module,exports){ 1232 | function E () { 1233 | // Keep this empty so it's easier to inherit from 1234 | // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 1235 | } 1236 | 1237 | E.prototype = { 1238 | on: function (name, callback, ctx) { 1239 | var e = this.e || (this.e = {}); 1240 | 1241 | (e[name] || (e[name] = [])).push({ 1242 | fn: callback, 1243 | ctx: ctx 1244 | }); 1245 | 1246 | return this; 1247 | }, 1248 | 1249 | once: function (name, callback, ctx) { 1250 | var self = this; 1251 | function listener () { 1252 | self.off(name, listener); 1253 | callback.apply(ctx, arguments); 1254 | }; 1255 | 1256 | listener._ = callback 1257 | return this.on(name, listener, ctx); 1258 | }, 1259 | 1260 | emit: function (name) { 1261 | var data = [].slice.call(arguments, 1); 1262 | var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 1263 | var i = 0; 1264 | var len = evtArr.length; 1265 | 1266 | for (i; i < len; i++) { 1267 | evtArr[i].fn.apply(evtArr[i].ctx, data); 1268 | } 1269 | 1270 | return this; 1271 | }, 1272 | 1273 | off: function (name, callback) { 1274 | var e = this.e || (this.e = {}); 1275 | var evts = e[name]; 1276 | var liveEvents = []; 1277 | 1278 | if (evts && callback) { 1279 | for (var i = 0, len = evts.length; i < len; i++) { 1280 | if (evts[i].fn !== callback && evts[i].fn._ !== callback) 1281 | liveEvents.push(evts[i]); 1282 | } 1283 | } 1284 | 1285 | // Remove event from queue to prevent memory leak 1286 | // Suggested by https://github.com/lazd 1287 | // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 1288 | 1289 | (liveEvents.length) 1290 | ? e[name] = liveEvents 1291 | : delete e[name]; 1292 | 1293 | return this; 1294 | } 1295 | }; 1296 | 1297 | module.exports = E; 1298 | 1299 | },{}],16:[function(require,module,exports){ 1300 | 'use strict'; 1301 | 1302 | module.exports = function(source) { 1303 | return JSON.parse(JSON.stringify(source)); 1304 | }; 1305 | },{}],17:[function(require,module,exports){ 1306 | 'use strict'; 1307 | 1308 | var objectAssign = require('object-assign'); 1309 | var Emitter = require('tiny-emitter'); 1310 | var Lethargy = require('lethargy').Lethargy; 1311 | var support = require('./support'); 1312 | var clone = require('./clone'); 1313 | var bindAll = require('bindall-standalone'); 1314 | var EVT_ID = 'virtualscroll'; 1315 | 1316 | module.exports = VirtualScroll; 1317 | 1318 | var keyCodes = { 1319 | LEFT: 37, 1320 | UP: 38, 1321 | RIGHT: 39, 1322 | DOWN: 40, 1323 | SPACE: 32 1324 | }; 1325 | 1326 | function VirtualScroll(options) { 1327 | bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown'); 1328 | 1329 | this.el = window; 1330 | if (options && options.el) { 1331 | this.el = options.el; 1332 | delete options.el; 1333 | } 1334 | this.options = objectAssign({ 1335 | mouseMultiplier: 1, 1336 | touchMultiplier: 2, 1337 | firefoxMultiplier: 15, 1338 | keyStep: 120, 1339 | preventTouch: false, 1340 | unpreventTouchClass: 'vs-touchmove-allowed', 1341 | limitInertia: false 1342 | }, options); 1343 | 1344 | if (this.options.limitInertia) this._lethargy = new Lethargy(); 1345 | 1346 | this._emitter = new Emitter(); 1347 | this._event = { 1348 | y: 0, 1349 | x: 0, 1350 | deltaX: 0, 1351 | deltaY: 0 1352 | }; 1353 | this.touchStartX = null; 1354 | this.touchStartY = null; 1355 | this.bodyTouchAction = null; 1356 | 1357 | if (this.options.passive !== undefined) { 1358 | this.listenerOptions = {passive: this.options.passive}; 1359 | } 1360 | } 1361 | 1362 | VirtualScroll.prototype._notify = function(e) { 1363 | var evt = this._event; 1364 | evt.x += evt.deltaX; 1365 | evt.y += evt.deltaY; 1366 | 1367 | this._emitter.emit(EVT_ID, { 1368 | x: evt.x, 1369 | y: evt.y, 1370 | deltaX: evt.deltaX, 1371 | deltaY: evt.deltaY, 1372 | originalEvent: e 1373 | }); 1374 | }; 1375 | 1376 | VirtualScroll.prototype._onWheel = function(e) { 1377 | var options = this.options; 1378 | if (this._lethargy && this._lethargy.check(e) === false) return; 1379 | var evt = this._event; 1380 | 1381 | // In Chrome and in Firefox (at least the new one) 1382 | evt.deltaX = e.wheelDeltaX || e.deltaX * -1; 1383 | evt.deltaY = e.wheelDeltaY || e.deltaY * -1; 1384 | 1385 | // for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad 1386 | // real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes 1387 | if(support.isFirefox && e.deltaMode == 1) { 1388 | evt.deltaX *= options.firefoxMultiplier; 1389 | evt.deltaY *= options.firefoxMultiplier; 1390 | } 1391 | 1392 | evt.deltaX *= options.mouseMultiplier; 1393 | evt.deltaY *= options.mouseMultiplier; 1394 | 1395 | this._notify(e); 1396 | }; 1397 | 1398 | VirtualScroll.prototype._onMouseWheel = function(e) { 1399 | if (this.options.limitInertia && this._lethargy.check(e) === false) return; 1400 | 1401 | var evt = this._event; 1402 | 1403 | // In Safari, IE and in Chrome if 'wheel' isn't defined 1404 | evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0; 1405 | evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta; 1406 | 1407 | this._notify(e); 1408 | }; 1409 | 1410 | VirtualScroll.prototype._onTouchStart = function(e) { 1411 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1412 | this.touchStartX = t.pageX; 1413 | this.touchStartY = t.pageY; 1414 | }; 1415 | 1416 | VirtualScroll.prototype._onTouchMove = function(e) { 1417 | var options = this.options; 1418 | if(options.preventTouch 1419 | && !e.target.classList.contains(options.unpreventTouchClass)) { 1420 | e.preventDefault(); 1421 | } 1422 | 1423 | var evt = this._event; 1424 | 1425 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1426 | 1427 | evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier; 1428 | evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier; 1429 | 1430 | this.touchStartX = t.pageX; 1431 | this.touchStartY = t.pageY; 1432 | 1433 | this._notify(e); 1434 | }; 1435 | 1436 | VirtualScroll.prototype._onKeyDown = function(e) { 1437 | var evt = this._event; 1438 | evt.deltaX = evt.deltaY = 0; 1439 | var windowHeight = window.innerHeight - 40 1440 | 1441 | switch(e.keyCode) { 1442 | case keyCodes.LEFT: 1443 | case keyCodes.UP: 1444 | evt.deltaY = this.options.keyStep; 1445 | break; 1446 | 1447 | case keyCodes.RIGHT: 1448 | case keyCodes.DOWN: 1449 | evt.deltaY = - this.options.keyStep; 1450 | break; 1451 | case keyCodes.SPACE && e.shiftKey: 1452 | evt.deltaY = windowHeight; 1453 | break; 1454 | case keyCodes.SPACE: 1455 | evt.deltaY = - windowHeight; 1456 | break; 1457 | default: 1458 | return; 1459 | } 1460 | 1461 | this._notify(e); 1462 | }; 1463 | 1464 | VirtualScroll.prototype._bind = function() { 1465 | if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel, this.listenerOptions); 1466 | if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel, this.listenerOptions); 1467 | 1468 | if(support.hasTouch) { 1469 | this.el.addEventListener('touchstart', this._onTouchStart, this.listenerOptions); 1470 | this.el.addEventListener('touchmove', this._onTouchMove, this.listenerOptions); 1471 | } 1472 | 1473 | if(support.hasPointer && support.hasTouchWin) { 1474 | this.bodyTouchAction = document.body.style.msTouchAction; 1475 | document.body.style.msTouchAction = 'none'; 1476 | this.el.addEventListener('MSPointerDown', this._onTouchStart, true); 1477 | this.el.addEventListener('MSPointerMove', this._onTouchMove, true); 1478 | } 1479 | 1480 | if(support.hasKeyDown) document.addEventListener('keydown', this._onKeyDown); 1481 | }; 1482 | 1483 | VirtualScroll.prototype._unbind = function() { 1484 | if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel); 1485 | if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel); 1486 | 1487 | if(support.hasTouch) { 1488 | this.el.removeEventListener('touchstart', this._onTouchStart); 1489 | this.el.removeEventListener('touchmove', this._onTouchMove); 1490 | } 1491 | 1492 | if(support.hasPointer && support.hasTouchWin) { 1493 | document.body.style.msTouchAction = this.bodyTouchAction; 1494 | this.el.removeEventListener('MSPointerDown', this._onTouchStart, true); 1495 | this.el.removeEventListener('MSPointerMove', this._onTouchMove, true); 1496 | } 1497 | 1498 | if(support.hasKeyDown) document.removeEventListener('keydown', this._onKeyDown); 1499 | }; 1500 | 1501 | VirtualScroll.prototype.on = function(cb, ctx) { 1502 | this._emitter.on(EVT_ID, cb, ctx); 1503 | 1504 | var events = this._emitter.e; 1505 | if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind(); 1506 | }; 1507 | 1508 | VirtualScroll.prototype.off = function(cb, ctx) { 1509 | this._emitter.off(EVT_ID, cb, ctx); 1510 | 1511 | var events = this._emitter.e; 1512 | if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind(); 1513 | }; 1514 | 1515 | VirtualScroll.prototype.reset = function() { 1516 | var evt = this._event; 1517 | evt.x = 0; 1518 | evt.y = 0; 1519 | }; 1520 | 1521 | VirtualScroll.prototype.destroy = function() { 1522 | this._emitter.off(); 1523 | this._unbind(); 1524 | }; 1525 | 1526 | },{"./clone":16,"./support":18,"bindall-standalone":4,"lethargy":9,"object-assign":10,"tiny-emitter":15}],18:[function(require,module,exports){ 1527 | 'use strict'; 1528 | 1529 | module.exports = (function getSupport() { 1530 | return { 1531 | hasWheelEvent: 'onwheel' in document, 1532 | hasMouseWheelEvent: 'onmousewheel' in document, 1533 | hasTouch: 'ontouchstart' in document, 1534 | hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1, 1535 | hasPointer: !!window.navigator.msPointerEnabled, 1536 | hasKeyDown: 'onkeydown' in document, 1537 | isFirefox: navigator.userAgent.indexOf('Firefox') > -1 1538 | }; 1539 | })(); 1540 | 1541 | },{}]},{},[2]); 1542 | -------------------------------------------------------------------------------- /demos/performances/build.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}; 129 | 130 | _classCallCheck(this, Smooth); 131 | 132 | this.createBound(); 133 | this.options = opt; 134 | this.prefix = (0, _prefix["default"])('transform'); 135 | this.rAF = undefined; // It seems that under heavy load, Firefox will still call the RAF callback even though the RAF has been canceled 136 | // To prevent that we set a flag to prevent any callback to be executed when RAF is removed 137 | 138 | this.isRAFCanceled = false; 139 | var constructorName = this.constructor.name ? this.constructor.name : 'Smooth'; 140 | this["extends"] = typeof opt["extends"] === 'undefined' ? this.constructor !== Smooth : opt["extends"]; 141 | this.callback = this.options.callback || null; 142 | this.vars = { 143 | direction: this.options.direction || 'vertical', 144 | "native": this.options["native"] || false, 145 | ease: this.options.ease || 0.075, 146 | preload: this.options.preload || false, 147 | current: 0, 148 | last: 0, 149 | target: 0, 150 | height: window.innerHeight, 151 | width: window.innerWidth, 152 | bounding: 0, 153 | timer: null, 154 | ticking: false 155 | }; 156 | this.vs = this.vars["native"] ? null : new _virtualScroll["default"]({ 157 | limitInertia: this.options.vs && this.options.vs.limitInertia || false, 158 | mouseMultiplier: this.options.vs && this.options.vs.mouseMultiplier || 1, 159 | touchMultiplier: this.options.vs && this.options.vs.touchMultiplier || 1.5, 160 | firefoxMultiplier: this.options.vs && this.options.vs.firefoxMultiplier || 30, 161 | preventTouch: this.options.vs && this.options.vs.preventTouch || true 162 | }); 163 | this.dom = { 164 | listener: this.options.listener || document.body, 165 | section: this.options.section || document.querySelector('.vs-section') || null, 166 | scrollbar: this.vars["native"] || this.options.noscrollbar ? null : { 167 | state: { 168 | clicked: false, 169 | x: 0 170 | }, 171 | el: (0, _domCreateElement["default"])({ 172 | selector: 'div', 173 | styles: "vs-scrollbar vs-".concat(this.vars.direction, " vs-scrollbar-").concat(constructorName.toLowerCase()) 174 | }), 175 | drag: { 176 | el: (0, _domCreateElement["default"])({ 177 | selector: 'div', 178 | styles: 'vs-scrolldrag' 179 | }), 180 | delta: 0, 181 | height: 50 182 | } 183 | } 184 | }; 185 | } 186 | 187 | _createClass(Smooth, [{ 188 | key: "createBound", 189 | value: function createBound() { 190 | var _this = this; 191 | 192 | ['run', 'calc', 'debounce', 'resize', 'mouseUp', 'mouseDown', 'mouseMove', 'calcScroll', 'scrollTo'].forEach(function (fn) { 193 | return _this[fn] = _this[fn].bind(_this); 194 | }); 195 | } 196 | }, { 197 | key: "init", 198 | value: function init() { 199 | this.addClasses(); 200 | this.vars.preload && this.preloadImages(); 201 | this.vars["native"] ? this.addFakeScrollHeight() : !this.options.noscrollbar && this.addFakeScrollBar(); 202 | this.addEvents(); 203 | this.resize(); 204 | } 205 | }, { 206 | key: "addClasses", 207 | value: function addClasses() { 208 | var type = this.vars["native"] ? 'native' : 'virtual'; 209 | var direction = this.vars.direction === 'vertical' ? 'y' : 'x'; 210 | 211 | _domClasses["default"].add(this.dom.listener, "is-".concat(type, "-scroll")); 212 | 213 | _domClasses["default"].add(this.dom.listener, "".concat(direction, "-scroll")); 214 | } 215 | }, { 216 | key: "preloadImages", 217 | value: function preloadImages() { 218 | var _this2 = this; 219 | 220 | var images = Array.prototype.slice.call(this.dom.listener.querySelectorAll('img'), 0); 221 | images.forEach(function (image) { 222 | var img = document.createElement('img'); 223 | 224 | _domEvents["default"].once(img, 'load', function () { 225 | images.splice(images.indexOf(image), 1); 226 | images.length === 0 && _this2.resize(); 227 | }); 228 | 229 | img.src = image.getAttribute('src'); 230 | }); 231 | } 232 | }, { 233 | key: "calc", 234 | value: function calc(e) { 235 | var delta = this.vars.direction == 'horizontal' ? e.deltaX : e.deltaY; 236 | this.vars.target += delta * -1; 237 | this.clampTarget(); 238 | } 239 | }, { 240 | key: "debounce", 241 | value: function debounce() { 242 | var _this3 = this; 243 | 244 | var win = this.dom.listener === document.body; 245 | this.vars.target = this.vars.direction === 'vertical' ? win ? window.scrollY || window.pageYOffset : this.dom.listener.scrollTop : win ? window.scrollX || window.pageXOffset : this.dom.listener.scrollLeft; 246 | clearTimeout(this.vars.timer); 247 | 248 | if (!this.vars.ticking) { 249 | this.vars.ticking = true; 250 | 251 | _domClasses["default"].add(this.dom.listener, 'is-scrolling'); 252 | } 253 | 254 | this.vars.timer = setTimeout(function () { 255 | _this3.vars.ticking = false; 256 | 257 | _domClasses["default"].remove(_this3.dom.listener, 'is-scrolling'); 258 | }, 200); 259 | } 260 | }, { 261 | key: "run", 262 | value: function run() { 263 | if (this.isRAFCanceled) return; 264 | this.vars.current += (this.vars.target - this.vars.current) * this.vars.ease; 265 | this.vars.current < .1 && (this.vars.current = 0); 266 | this.requestAnimationFrame(); 267 | 268 | if (!this["extends"]) { 269 | this.dom.section.style[this.prefix] = this.getTransform(-this.vars.current.toFixed(2)); 270 | } 271 | 272 | if (!this.vars["native"] && !this.options.noscrollbar) { 273 | var size = this.dom.scrollbar.drag.height; 274 | var bounds = this.vars.direction === 'vertical' ? this.vars.height : this.vars.width; 275 | var value = Math.abs(this.vars.current) / (this.vars.bounding / (bounds - size)) + size / .5 - size; 276 | var clamp = Math.max(0, Math.min(value - size, value + size)); 277 | this.dom.scrollbar.drag.el.style[this.prefix] = this.getTransform(clamp.toFixed(2)); 278 | } 279 | 280 | if (this.callback && this.vars.current !== this.vars.last) { 281 | this.callback(this.vars.current); 282 | } 283 | 284 | this.vars.last = this.vars.current; 285 | } 286 | }, { 287 | key: "getTransform", 288 | value: function getTransform(value) { 289 | return this.vars.direction === 'vertical' ? "translate3d(0,".concat(value, "px,0)") : "translate3d(".concat(value, "px,0,0)"); 290 | } 291 | }, { 292 | key: "on", 293 | value: function on() { 294 | var requestAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 295 | 296 | if (this.isRAFCanceled) { 297 | this.isRAFCanceled = false; 298 | } 299 | 300 | var node = this.dom.listener === document.body ? window : this.dom.listener; 301 | this.vars["native"] ? _domEvents["default"].on(node, 'scroll', this.debounce) : this.vs && this.vs.on(this.calc); 302 | requestAnimationFrame && this.requestAnimationFrame(); 303 | } 304 | }, { 305 | key: "off", 306 | value: function off() { 307 | var cancelAnimationFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 308 | var node = this.dom.listener === document.body ? window : this.dom.listener; 309 | this.vars["native"] ? _domEvents["default"].off(node, 'scroll', this.debounce) : this.vs && this.vs.off(this.calc); 310 | cancelAnimationFrame && this.cancelAnimationFrame(); 311 | } 312 | }, { 313 | key: "requestAnimationFrame", 314 | value: function (_requestAnimationFrame) { 315 | function requestAnimationFrame() { 316 | return _requestAnimationFrame.apply(this, arguments); 317 | } 318 | 319 | requestAnimationFrame.toString = function () { 320 | return _requestAnimationFrame.toString(); 321 | }; 322 | 323 | return requestAnimationFrame; 324 | }(function () { 325 | this.rAF = requestAnimationFrame(this.run); 326 | }) 327 | }, { 328 | key: "cancelAnimationFrame", 329 | value: function (_cancelAnimationFrame) { 330 | function cancelAnimationFrame() { 331 | return _cancelAnimationFrame.apply(this, arguments); 332 | } 333 | 334 | cancelAnimationFrame.toString = function () { 335 | return _cancelAnimationFrame.toString(); 336 | }; 337 | 338 | return cancelAnimationFrame; 339 | }(function () { 340 | this.isRAFCanceled = true; 341 | cancelAnimationFrame(this.rAF); 342 | }) 343 | }, { 344 | key: "addEvents", 345 | value: function addEvents() { 346 | this.on(); 347 | 348 | _domEvents["default"].on(window, 'resize', this.resize); 349 | } 350 | }, { 351 | key: "removeEvents", 352 | value: function removeEvents() { 353 | this.off(); 354 | 355 | _domEvents["default"].off(window, 'resize', this.resize); 356 | } 357 | }, { 358 | key: "addFakeScrollBar", 359 | value: function addFakeScrollBar() { 360 | this.dom.listener.appendChild(this.dom.scrollbar.el); 361 | this.dom.scrollbar.el.appendChild(this.dom.scrollbar.drag.el); 362 | 363 | _domEvents["default"].on(this.dom.scrollbar.el, 'click', this.calcScroll); 364 | 365 | _domEvents["default"].on(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 366 | 367 | _domEvents["default"].on(document, 'mousemove', this.mouseMove); 368 | 369 | _domEvents["default"].on(document, 'mouseup', this.mouseUp); 370 | } 371 | }, { 372 | key: "removeFakeScrollBar", 373 | value: function removeFakeScrollBar() { 374 | _domEvents["default"].off(this.dom.scrollbar.el, 'click', this.calcScroll); 375 | 376 | _domEvents["default"].off(this.dom.scrollbar.el, 'mousedown', this.mouseDown); 377 | 378 | _domEvents["default"].off(document, 'mousemove', this.mouseMove); 379 | 380 | _domEvents["default"].off(document, 'mouseup', this.mouseUp); 381 | 382 | this.dom.listener.removeChild(this.dom.scrollbar.el); 383 | } 384 | }, { 385 | key: "mouseDown", 386 | value: function mouseDown(e) { 387 | e.preventDefault(); 388 | e.which == 1 && (this.dom.scrollbar.state.clicked = true); 389 | } 390 | }, { 391 | key: "mouseUp", 392 | value: function mouseUp(e) { 393 | this.dom.scrollbar.state.clicked = false; 394 | 395 | _domClasses["default"].remove(this.dom.listener, 'is-dragging'); 396 | } 397 | }, { 398 | key: "mouseMove", 399 | value: function mouseMove(e) { 400 | this.dom.scrollbar.state.clicked && this.calcScroll(e); 401 | } 402 | }, { 403 | key: "addFakeScrollHeight", 404 | value: function addFakeScrollHeight() { 405 | this.dom.scroll = (0, _domCreateElement["default"])({ 406 | selector: 'div', 407 | styles: 'vs-scroll-view' 408 | }); 409 | this.dom.listener.appendChild(this.dom.scroll); 410 | } 411 | }, { 412 | key: "removeFakeScrollHeight", 413 | value: function removeFakeScrollHeight() { 414 | this.dom.listener.removeChild(this.dom.scroll); 415 | } 416 | }, { 417 | key: "calcScroll", 418 | value: function calcScroll(e) { 419 | var client = this.vars.direction == 'vertical' ? e.clientY : e.clientX; 420 | var bounds = this.vars.direction == 'vertical' ? this.vars.height : this.vars.width; 421 | var delta = client * (this.vars.bounding / bounds); 422 | 423 | _domClasses["default"].add(this.dom.listener, 'is-dragging'); 424 | 425 | this.vars.target = delta; 426 | this.clampTarget(); 427 | this.dom.scrollbar && (this.dom.scrollbar.drag.delta = this.vars.target); 428 | } 429 | }, { 430 | key: "scrollTo", 431 | value: function scrollTo(offset) { 432 | if (this.vars["native"]) { 433 | this.vars.direction == 'vertical' ? window.scrollTo(0, offset) : window.scrollTo(offset, 0); 434 | } else { 435 | this.vars.target = offset; 436 | this.clampTarget(); 437 | } 438 | } 439 | }, { 440 | key: "resize", 441 | value: function resize() { 442 | var prop = this.vars.direction === 'vertical' ? 'height' : 'width'; 443 | this.vars.height = window.innerHeight; 444 | this.vars.width = window.innerWidth; 445 | 446 | if (!this["extends"]) { 447 | var bounding = this.dom.section.getBoundingClientRect(); 448 | this.vars.bounding = this.vars.direction === 'vertical' ? bounding.height - (this.vars["native"] ? 0 : this.vars.height) : bounding.right - (this.vars["native"] ? 0 : this.vars.width); 449 | } 450 | 451 | if (!this.vars["native"] && !this.options.noscrollbar) { 452 | this.dom.scrollbar.drag.height = this.vars.height * (this.vars.height / (this.vars.bounding + this.vars.height)); 453 | this.dom.scrollbar.drag.el.style[prop] = "".concat(this.dom.scrollbar.drag.height, "px"); 454 | } else if (this.vars["native"]) { 455 | this.dom.scroll.style[prop] = "".concat(this.vars.bounding, "px"); 456 | } 457 | 458 | !this.vars["native"] && this.clampTarget(); 459 | } 460 | }, { 461 | key: "clampTarget", 462 | value: function clampTarget() { 463 | this.vars.target = Math.round(Math.max(0, Math.min(this.vars.target, this.vars.bounding))); 464 | } 465 | }, { 466 | key: "destroy", 467 | value: function destroy() { 468 | if (this.vars["native"]) { 469 | _domClasses["default"].remove(this.dom.listener, 'is-native-scroll'); 470 | 471 | this.removeFakeScrollHeight(); 472 | } else { 473 | _domClasses["default"].remove(this.dom.listener, 'is-virtual-scroll'); 474 | 475 | !this.options.noscrollbar && this.removeFakeScrollBar(); 476 | } 477 | 478 | this.vars.direction === 'vertical' ? _domClasses["default"].remove(this.dom.listener, 'y-scroll') : _domClasses["default"].remove(this.dom.listener, 'x-scroll'); 479 | this.vars.current = 0; 480 | this.vs && (this.vs.destroy(), this.vs = null); 481 | this.removeEvents(); 482 | } 483 | }]); 484 | 485 | return Smooth; 486 | }(); 487 | 488 | exports["default"] = Smooth; 489 | window.Smooth = Smooth; 490 | 491 | },{"dom-classes":5,"dom-create-element":6,"dom-events":7,"prefix":11,"virtual-scroll":17}],4:[function(require,module,exports){ 492 | 'use strict'; 493 | 494 | var toString = Object.prototype.toString, 495 | hasOwnProperty = Object.prototype.hasOwnProperty; 496 | 497 | module.exports = function(object) { 498 | if(!object) return console.warn('bindAll requires at least one argument.'); 499 | 500 | var functions = Array.prototype.slice.call(arguments, 1); 501 | 502 | if (functions.length === 0) { 503 | 504 | for (var method in object) { 505 | if(hasOwnProperty.call(object, method)) { 506 | if(typeof object[method] == 'function' && toString.call(object[method]) == "[object Function]") { 507 | functions.push(method); 508 | } 509 | } 510 | } 511 | } 512 | 513 | for(var i = 0; i < functions.length; i++) { 514 | var f = functions[i]; 515 | object[f] = bind(object[f], object); 516 | } 517 | }; 518 | 519 | /* 520 | Faster bind without specific-case checking. (see https://coderwall.com/p/oi3j3w). 521 | bindAll is only needed for events binding so no need to make slow fixes for constructor 522 | or partial application. 523 | */ 524 | function bind(func, context) { 525 | return function() { 526 | return func.apply(context, arguments); 527 | }; 528 | } 529 | },{}],5:[function(require,module,exports){ 530 | /** 531 | * Module dependencies. 532 | */ 533 | 534 | var index = require('indexof'); 535 | 536 | /** 537 | * Whitespace regexp. 538 | */ 539 | 540 | var whitespaceRe = /\s+/; 541 | 542 | /** 543 | * toString reference. 544 | */ 545 | 546 | var toString = Object.prototype.toString; 547 | 548 | module.exports = classes; 549 | module.exports.add = add; 550 | module.exports.contains = has; 551 | module.exports.has = has; 552 | module.exports.toggle = toggle; 553 | module.exports.remove = remove; 554 | module.exports.removeMatching = removeMatching; 555 | 556 | function classes (el) { 557 | if (el.classList) { 558 | return el.classList; 559 | } 560 | 561 | var str = el.className.replace(/^\s+|\s+$/g, ''); 562 | var arr = str.split(whitespaceRe); 563 | if ('' === arr[0]) arr.shift(); 564 | return arr; 565 | } 566 | 567 | function add (el, name) { 568 | // classList 569 | if (el.classList) { 570 | el.classList.add(name); 571 | return; 572 | } 573 | 574 | // fallback 575 | var arr = classes(el); 576 | var i = index(arr, name); 577 | if (!~i) arr.push(name); 578 | el.className = arr.join(' '); 579 | } 580 | 581 | function has (el, name) { 582 | return el.classList 583 | ? el.classList.contains(name) 584 | : !! ~index(classes(el), name); 585 | } 586 | 587 | function remove (el, name) { 588 | if ('[object RegExp]' == toString.call(name)) { 589 | return removeMatching(el, name); 590 | } 591 | 592 | // classList 593 | if (el.classList) { 594 | el.classList.remove(name); 595 | return; 596 | } 597 | 598 | // fallback 599 | var arr = classes(el); 600 | var i = index(arr, name); 601 | if (~i) arr.splice(i, 1); 602 | el.className = arr.join(' '); 603 | } 604 | 605 | function removeMatching (el, re, ref) { 606 | var arr = Array.prototype.slice.call(classes(el)); 607 | for (var i = 0; i < arr.length; i++) { 608 | if (re.test(arr[i])) { 609 | remove(el, arr[i]); 610 | } 611 | } 612 | } 613 | 614 | function toggle (el, name) { 615 | // classList 616 | if (el.classList) { 617 | return el.classList.toggle(name); 618 | } 619 | 620 | // fallback 621 | if (has(el, name)) { 622 | remove(el, name); 623 | } else { 624 | add(el, name); 625 | } 626 | } 627 | 628 | },{"indexof":8}],6:[function(require,module,exports){ 629 | /* 630 | `dom-create-element` 631 | 632 | var create = require('dom-create-element'); 633 | 634 | var el = create({ 635 | selector: 'div', 636 | styles: 'preloader', 637 | html: 'Text' 638 | }); 639 | */ 640 | 641 | module.exports = create; 642 | 643 | function create(opt) { 644 | 645 | opt = opt || {}; 646 | 647 | var el = document.createElement(opt.selector); 648 | 649 | if(opt.attr) for(var index in opt.attr) 650 | opt.attr.hasOwnProperty(index) && el.setAttribute(index, opt.attr[index]); 651 | 652 | "a" == opt.selector && opt.link && ( 653 | el.href = opt.link, 654 | opt.target && el.setAttribute("target", opt.target) 655 | ); 656 | 657 | "img" == opt.selector && opt.src && ( 658 | el.src = opt.src, 659 | opt.lazyload && ( 660 | el.style.opacity = 0, 661 | el.onload = function(){ 662 | el.style.opacity = 1; 663 | } 664 | ) 665 | ); 666 | 667 | opt.id && (el.id = opt.id); 668 | opt.styles && (el.className = opt.styles); 669 | 670 | opt.html && (el.innerHTML = opt.html); 671 | opt.children && (el.appendChild(opt.children)); 672 | 673 | return el; 674 | }; 675 | },{}],7:[function(require,module,exports){ 676 | 677 | var synth = require('synthetic-dom-events'); 678 | 679 | var on = function(element, name, fn, capture) { 680 | return element.addEventListener(name, fn, capture || false); 681 | }; 682 | 683 | var off = function(element, name, fn, capture) { 684 | return element.removeEventListener(name, fn, capture || false); 685 | }; 686 | 687 | var once = function (element, name, fn, capture) { 688 | function tmp (ev) { 689 | off(element, name, tmp, capture); 690 | fn(ev); 691 | } 692 | on(element, name, tmp, capture); 693 | }; 694 | 695 | var emit = function(element, name, opt) { 696 | var ev = synth(name, opt); 697 | element.dispatchEvent(ev); 698 | }; 699 | 700 | if (!document.addEventListener) { 701 | on = function(element, name, fn) { 702 | return element.attachEvent('on' + name, fn); 703 | }; 704 | } 705 | 706 | if (!document.removeEventListener) { 707 | off = function(element, name, fn) { 708 | return element.detachEvent('on' + name, fn); 709 | }; 710 | } 711 | 712 | if (!document.dispatchEvent) { 713 | emit = function(element, name, opt) { 714 | var ev = synth(name, opt); 715 | return element.fireEvent('on' + ev.type, ev); 716 | }; 717 | } 718 | 719 | module.exports = { 720 | on: on, 721 | off: off, 722 | once: once, 723 | emit: emit 724 | }; 725 | 726 | },{"synthetic-dom-events":12}],8:[function(require,module,exports){ 727 | 728 | var indexOf = [].indexOf; 729 | 730 | module.exports = function(arr, obj){ 731 | if (indexOf) return arr.indexOf(obj); 732 | for (var i = 0; i < arr.length; ++i) { 733 | if (arr[i] === obj) return i; 734 | } 735 | return -1; 736 | }; 737 | },{}],9:[function(require,module,exports){ 738 | // Generated by CoffeeScript 1.9.2 739 | (function() { 740 | var root; 741 | 742 | root = typeof exports !== "undefined" && exports !== null ? exports : this; 743 | 744 | root.Lethargy = (function() { 745 | function Lethargy(stability, sensitivity, tolerance, delay) { 746 | this.stability = stability != null ? Math.abs(stability) : 8; 747 | this.sensitivity = sensitivity != null ? 1 + Math.abs(sensitivity) : 100; 748 | this.tolerance = tolerance != null ? 1 + Math.abs(tolerance) : 1.1; 749 | this.delay = delay != null ? delay : 150; 750 | this.lastUpDeltas = (function() { 751 | var i, ref, results; 752 | results = []; 753 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 754 | results.push(null); 755 | } 756 | return results; 757 | }).call(this); 758 | this.lastDownDeltas = (function() { 759 | var i, ref, results; 760 | results = []; 761 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 762 | results.push(null); 763 | } 764 | return results; 765 | }).call(this); 766 | this.deltasTimestamp = (function() { 767 | var i, ref, results; 768 | results = []; 769 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 770 | results.push(null); 771 | } 772 | return results; 773 | }).call(this); 774 | } 775 | 776 | Lethargy.prototype.check = function(e) { 777 | var lastDelta; 778 | e = e.originalEvent || e; 779 | if (e.wheelDelta != null) { 780 | lastDelta = e.wheelDelta; 781 | } else if (e.deltaY != null) { 782 | lastDelta = e.deltaY * -40; 783 | } else if ((e.detail != null) || e.detail === 0) { 784 | lastDelta = e.detail * -40; 785 | } 786 | this.deltasTimestamp.push(Date.now()); 787 | this.deltasTimestamp.shift(); 788 | if (lastDelta > 0) { 789 | this.lastUpDeltas.push(lastDelta); 790 | this.lastUpDeltas.shift(); 791 | return this.isInertia(1); 792 | } else { 793 | this.lastDownDeltas.push(lastDelta); 794 | this.lastDownDeltas.shift(); 795 | return this.isInertia(-1); 796 | } 797 | return false; 798 | }; 799 | 800 | Lethargy.prototype.isInertia = function(direction) { 801 | var lastDeltas, lastDeltasNew, lastDeltasOld, newAverage, newSum, oldAverage, oldSum; 802 | lastDeltas = direction === -1 ? this.lastDownDeltas : this.lastUpDeltas; 803 | if (lastDeltas[0] === null) { 804 | return direction; 805 | } 806 | if (this.deltasTimestamp[(this.stability * 2) - 2] + this.delay > Date.now() && lastDeltas[0] === lastDeltas[(this.stability * 2) - 1]) { 807 | return false; 808 | } 809 | lastDeltasOld = lastDeltas.slice(0, this.stability); 810 | lastDeltasNew = lastDeltas.slice(this.stability, this.stability * 2); 811 | oldSum = lastDeltasOld.reduce(function(t, s) { 812 | return t + s; 813 | }); 814 | newSum = lastDeltasNew.reduce(function(t, s) { 815 | return t + s; 816 | }); 817 | oldAverage = oldSum / lastDeltasOld.length; 818 | newAverage = newSum / lastDeltasNew.length; 819 | if (Math.abs(oldAverage) < Math.abs(newAverage * this.tolerance) && (this.sensitivity < Math.abs(newAverage))) { 820 | return direction; 821 | } else { 822 | return false; 823 | } 824 | }; 825 | 826 | Lethargy.prototype.showLastUpDeltas = function() { 827 | return this.lastUpDeltas; 828 | }; 829 | 830 | Lethargy.prototype.showLastDownDeltas = function() { 831 | return this.lastDownDeltas; 832 | }; 833 | 834 | return Lethargy; 835 | 836 | })(); 837 | 838 | }).call(this); 839 | 840 | },{}],10:[function(require,module,exports){ 841 | /* 842 | object-assign 843 | (c) Sindre Sorhus 844 | @license MIT 845 | */ 846 | 847 | 'use strict'; 848 | /* eslint-disable no-unused-vars */ 849 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 850 | var hasOwnProperty = Object.prototype.hasOwnProperty; 851 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 852 | 853 | function toObject(val) { 854 | if (val === null || val === undefined) { 855 | throw new TypeError('Object.assign cannot be called with null or undefined'); 856 | } 857 | 858 | return Object(val); 859 | } 860 | 861 | function shouldUseNative() { 862 | try { 863 | if (!Object.assign) { 864 | return false; 865 | } 866 | 867 | // Detect buggy property enumeration order in older V8 versions. 868 | 869 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 870 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 871 | test1[5] = 'de'; 872 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 873 | return false; 874 | } 875 | 876 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 877 | var test2 = {}; 878 | for (var i = 0; i < 10; i++) { 879 | test2['_' + String.fromCharCode(i)] = i; 880 | } 881 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 882 | return test2[n]; 883 | }); 884 | if (order2.join('') !== '0123456789') { 885 | return false; 886 | } 887 | 888 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 889 | var test3 = {}; 890 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 891 | test3[letter] = letter; 892 | }); 893 | if (Object.keys(Object.assign({}, test3)).join('') !== 894 | 'abcdefghijklmnopqrst') { 895 | return false; 896 | } 897 | 898 | return true; 899 | } catch (err) { 900 | // We don't expect any of the above to throw, but better to be safe. 901 | return false; 902 | } 903 | } 904 | 905 | module.exports = shouldUseNative() ? Object.assign : function (target, source) { 906 | var from; 907 | var to = toObject(target); 908 | var symbols; 909 | 910 | for (var s = 1; s < arguments.length; s++) { 911 | from = Object(arguments[s]); 912 | 913 | for (var key in from) { 914 | if (hasOwnProperty.call(from, key)) { 915 | to[key] = from[key]; 916 | } 917 | } 918 | 919 | if (getOwnPropertySymbols) { 920 | symbols = getOwnPropertySymbols(from); 921 | for (var i = 0; i < symbols.length; i++) { 922 | if (propIsEnumerable.call(from, symbols[i])) { 923 | to[symbols[i]] = from[symbols[i]]; 924 | } 925 | } 926 | } 927 | } 928 | 929 | return to; 930 | }; 931 | 932 | },{}],11:[function(require,module,exports){ 933 | // check document first so it doesn't error in node.js 934 | var style = typeof document != 'undefined' 935 | ? document.createElement('p').style 936 | : {} 937 | 938 | var prefixes = ['O', 'ms', 'Moz', 'Webkit'] 939 | var upper = /([A-Z])/g 940 | var memo = {} 941 | 942 | /** 943 | * prefix `key` 944 | * 945 | * prefix('transform') // => WebkitTransform 946 | * 947 | * @param {String} key 948 | * @return {String} 949 | * @api public 950 | */ 951 | function prefix(key){ 952 | // Camel case 953 | key = key.replace(/-([a-z])/g, function(_, char){ 954 | return char.toUpperCase() 955 | }) 956 | 957 | // Without prefix 958 | if (style[key] !== undefined) return key 959 | 960 | // With prefix 961 | var Key = key.charAt(0).toUpperCase() + key.slice(1) 962 | var i = prefixes.length 963 | while (i--) { 964 | var name = prefixes[i] + Key 965 | if (style[name] !== undefined) return name 966 | } 967 | 968 | return key 969 | } 970 | 971 | /** 972 | * Memoized version of `prefix` 973 | * 974 | * @param {String} key 975 | * @return {String} 976 | * @api public 977 | */ 978 | function prefixMemozied(key){ 979 | return key in memo 980 | ? memo[key] 981 | : memo[key] = prefix(key) 982 | } 983 | 984 | /** 985 | * Create a dashed prefix 986 | * 987 | * @param {String} key 988 | * @return {String} 989 | * @api public 990 | */ 991 | function prefixDashed(key){ 992 | key = prefix(key) 993 | if (upper.test(key)) { 994 | key = '-' + key.replace(upper, '-$1') 995 | upper.lastIndex = 0 996 | } 997 | return key.toLowerCase() 998 | } 999 | 1000 | module.exports = prefixMemozied 1001 | module.exports.dash = prefixDashed 1002 | 1003 | },{}],12:[function(require,module,exports){ 1004 | 1005 | // for compression 1006 | var win = window; 1007 | var doc = document || {}; 1008 | var root = doc.documentElement || {}; 1009 | 1010 | // detect if we need to use firefox KeyEvents vs KeyboardEvents 1011 | var use_key_event = true; 1012 | try { 1013 | doc.createEvent('KeyEvents'); 1014 | } 1015 | catch (err) { 1016 | use_key_event = false; 1017 | } 1018 | 1019 | // Workaround for https://bugs.webkit.org/show_bug.cgi?id=16735 1020 | function check_kb(ev, opts) { 1021 | if (ev.ctrlKey != (opts.ctrlKey || false) || 1022 | ev.altKey != (opts.altKey || false) || 1023 | ev.shiftKey != (opts.shiftKey || false) || 1024 | ev.metaKey != (opts.metaKey || false) || 1025 | ev.keyCode != (opts.keyCode || 0) || 1026 | ev.charCode != (opts.charCode || 0)) { 1027 | 1028 | ev = document.createEvent('Event'); 1029 | ev.initEvent(opts.type, opts.bubbles, opts.cancelable); 1030 | ev.ctrlKey = opts.ctrlKey || false; 1031 | ev.altKey = opts.altKey || false; 1032 | ev.shiftKey = opts.shiftKey || false; 1033 | ev.metaKey = opts.metaKey || false; 1034 | ev.keyCode = opts.keyCode || 0; 1035 | ev.charCode = opts.charCode || 0; 1036 | } 1037 | 1038 | return ev; 1039 | } 1040 | 1041 | // modern browsers, do a proper dispatchEvent() 1042 | var modern = function(type, opts) { 1043 | opts = opts || {}; 1044 | 1045 | // which init fn do we use 1046 | var family = typeOf(type); 1047 | var init_fam = family; 1048 | if (family === 'KeyboardEvent' && use_key_event) { 1049 | family = 'KeyEvents'; 1050 | init_fam = 'KeyEvent'; 1051 | } 1052 | 1053 | var ev = doc.createEvent(family); 1054 | var init_fn = 'init' + init_fam; 1055 | var init = typeof ev[init_fn] === 'function' ? init_fn : 'initEvent'; 1056 | 1057 | var sig = initSignatures[init]; 1058 | var args = []; 1059 | var used = {}; 1060 | 1061 | opts.type = type; 1062 | for (var i = 0; i < sig.length; ++i) { 1063 | var key = sig[i]; 1064 | var val = opts[key]; 1065 | // if no user specified value, then use event default 1066 | if (val === undefined) { 1067 | val = ev[key]; 1068 | } 1069 | used[key] = true; 1070 | args.push(val); 1071 | } 1072 | ev[init].apply(ev, args); 1073 | 1074 | // webkit key event issue workaround 1075 | if (family === 'KeyboardEvent') { 1076 | ev = check_kb(ev, opts); 1077 | } 1078 | 1079 | // attach remaining unused options to the object 1080 | for (var key in opts) { 1081 | if (!used[key]) { 1082 | ev[key] = opts[key]; 1083 | } 1084 | } 1085 | 1086 | return ev; 1087 | }; 1088 | 1089 | var legacy = function (type, opts) { 1090 | opts = opts || {}; 1091 | var ev = doc.createEventObject(); 1092 | 1093 | ev.type = type; 1094 | for (var key in opts) { 1095 | if (opts[key] !== undefined) { 1096 | ev[key] = opts[key]; 1097 | } 1098 | } 1099 | 1100 | return ev; 1101 | }; 1102 | 1103 | // expose either the modern version of event generation or legacy 1104 | // depending on what we support 1105 | // avoids if statements in the code later 1106 | module.exports = doc.createEvent ? modern : legacy; 1107 | 1108 | var initSignatures = require('./init.json'); 1109 | var types = require('./types.json'); 1110 | var typeOf = (function () { 1111 | var typs = {}; 1112 | for (var key in types) { 1113 | var ts = types[key]; 1114 | for (var i = 0; i < ts.length; i++) { 1115 | typs[ts[i]] = key; 1116 | } 1117 | } 1118 | 1119 | return function (name) { 1120 | return typs[name] || 'Event'; 1121 | }; 1122 | })(); 1123 | 1124 | },{"./init.json":13,"./types.json":14}],13:[function(require,module,exports){ 1125 | module.exports={ 1126 | "initEvent" : [ 1127 | "type", 1128 | "bubbles", 1129 | "cancelable" 1130 | ], 1131 | "initUIEvent" : [ 1132 | "type", 1133 | "bubbles", 1134 | "cancelable", 1135 | "view", 1136 | "detail" 1137 | ], 1138 | "initMouseEvent" : [ 1139 | "type", 1140 | "bubbles", 1141 | "cancelable", 1142 | "view", 1143 | "detail", 1144 | "screenX", 1145 | "screenY", 1146 | "clientX", 1147 | "clientY", 1148 | "ctrlKey", 1149 | "altKey", 1150 | "shiftKey", 1151 | "metaKey", 1152 | "button", 1153 | "relatedTarget" 1154 | ], 1155 | "initMutationEvent" : [ 1156 | "type", 1157 | "bubbles", 1158 | "cancelable", 1159 | "relatedNode", 1160 | "prevValue", 1161 | "newValue", 1162 | "attrName", 1163 | "attrChange" 1164 | ], 1165 | "initKeyboardEvent" : [ 1166 | "type", 1167 | "bubbles", 1168 | "cancelable", 1169 | "view", 1170 | "ctrlKey", 1171 | "altKey", 1172 | "shiftKey", 1173 | "metaKey", 1174 | "keyCode", 1175 | "charCode" 1176 | ], 1177 | "initKeyEvent" : [ 1178 | "type", 1179 | "bubbles", 1180 | "cancelable", 1181 | "view", 1182 | "ctrlKey", 1183 | "altKey", 1184 | "shiftKey", 1185 | "metaKey", 1186 | "keyCode", 1187 | "charCode" 1188 | ] 1189 | } 1190 | 1191 | },{}],14:[function(require,module,exports){ 1192 | module.exports={ 1193 | "MouseEvent" : [ 1194 | "click", 1195 | "mousedown", 1196 | "mouseup", 1197 | "mouseover", 1198 | "mousemove", 1199 | "mouseout" 1200 | ], 1201 | "KeyboardEvent" : [ 1202 | "keydown", 1203 | "keyup", 1204 | "keypress" 1205 | ], 1206 | "MutationEvent" : [ 1207 | "DOMSubtreeModified", 1208 | "DOMNodeInserted", 1209 | "DOMNodeRemoved", 1210 | "DOMNodeRemovedFromDocument", 1211 | "DOMNodeInsertedIntoDocument", 1212 | "DOMAttrModified", 1213 | "DOMCharacterDataModified" 1214 | ], 1215 | "HTMLEvents" : [ 1216 | "load", 1217 | "unload", 1218 | "abort", 1219 | "error", 1220 | "select", 1221 | "change", 1222 | "submit", 1223 | "reset", 1224 | "focus", 1225 | "blur", 1226 | "resize", 1227 | "scroll" 1228 | ], 1229 | "UIEvent" : [ 1230 | "DOMFocusIn", 1231 | "DOMFocusOut", 1232 | "DOMActivate" 1233 | ] 1234 | } 1235 | 1236 | },{}],15:[function(require,module,exports){ 1237 | function E () { 1238 | // Keep this empty so it's easier to inherit from 1239 | // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 1240 | } 1241 | 1242 | E.prototype = { 1243 | on: function (name, callback, ctx) { 1244 | var e = this.e || (this.e = {}); 1245 | 1246 | (e[name] || (e[name] = [])).push({ 1247 | fn: callback, 1248 | ctx: ctx 1249 | }); 1250 | 1251 | return this; 1252 | }, 1253 | 1254 | once: function (name, callback, ctx) { 1255 | var self = this; 1256 | function listener () { 1257 | self.off(name, listener); 1258 | callback.apply(ctx, arguments); 1259 | }; 1260 | 1261 | listener._ = callback 1262 | return this.on(name, listener, ctx); 1263 | }, 1264 | 1265 | emit: function (name) { 1266 | var data = [].slice.call(arguments, 1); 1267 | var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 1268 | var i = 0; 1269 | var len = evtArr.length; 1270 | 1271 | for (i; i < len; i++) { 1272 | evtArr[i].fn.apply(evtArr[i].ctx, data); 1273 | } 1274 | 1275 | return this; 1276 | }, 1277 | 1278 | off: function (name, callback) { 1279 | var e = this.e || (this.e = {}); 1280 | var evts = e[name]; 1281 | var liveEvents = []; 1282 | 1283 | if (evts && callback) { 1284 | for (var i = 0, len = evts.length; i < len; i++) { 1285 | if (evts[i].fn !== callback && evts[i].fn._ !== callback) 1286 | liveEvents.push(evts[i]); 1287 | } 1288 | } 1289 | 1290 | // Remove event from queue to prevent memory leak 1291 | // Suggested by https://github.com/lazd 1292 | // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 1293 | 1294 | (liveEvents.length) 1295 | ? e[name] = liveEvents 1296 | : delete e[name]; 1297 | 1298 | return this; 1299 | } 1300 | }; 1301 | 1302 | module.exports = E; 1303 | 1304 | },{}],16:[function(require,module,exports){ 1305 | 'use strict'; 1306 | 1307 | module.exports = function(source) { 1308 | return JSON.parse(JSON.stringify(source)); 1309 | }; 1310 | },{}],17:[function(require,module,exports){ 1311 | 'use strict'; 1312 | 1313 | var objectAssign = require('object-assign'); 1314 | var Emitter = require('tiny-emitter'); 1315 | var Lethargy = require('lethargy').Lethargy; 1316 | var support = require('./support'); 1317 | var clone = require('./clone'); 1318 | var bindAll = require('bindall-standalone'); 1319 | var EVT_ID = 'virtualscroll'; 1320 | 1321 | module.exports = VirtualScroll; 1322 | 1323 | var keyCodes = { 1324 | LEFT: 37, 1325 | UP: 38, 1326 | RIGHT: 39, 1327 | DOWN: 40, 1328 | SPACE: 32 1329 | }; 1330 | 1331 | function VirtualScroll(options) { 1332 | bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown'); 1333 | 1334 | this.el = window; 1335 | if (options && options.el) { 1336 | this.el = options.el; 1337 | delete options.el; 1338 | } 1339 | this.options = objectAssign({ 1340 | mouseMultiplier: 1, 1341 | touchMultiplier: 2, 1342 | firefoxMultiplier: 15, 1343 | keyStep: 120, 1344 | preventTouch: false, 1345 | unpreventTouchClass: 'vs-touchmove-allowed', 1346 | limitInertia: false 1347 | }, options); 1348 | 1349 | if (this.options.limitInertia) this._lethargy = new Lethargy(); 1350 | 1351 | this._emitter = new Emitter(); 1352 | this._event = { 1353 | y: 0, 1354 | x: 0, 1355 | deltaX: 0, 1356 | deltaY: 0 1357 | }; 1358 | this.touchStartX = null; 1359 | this.touchStartY = null; 1360 | this.bodyTouchAction = null; 1361 | 1362 | if (this.options.passive !== undefined) { 1363 | this.listenerOptions = {passive: this.options.passive}; 1364 | } 1365 | } 1366 | 1367 | VirtualScroll.prototype._notify = function(e) { 1368 | var evt = this._event; 1369 | evt.x += evt.deltaX; 1370 | evt.y += evt.deltaY; 1371 | 1372 | this._emitter.emit(EVT_ID, { 1373 | x: evt.x, 1374 | y: evt.y, 1375 | deltaX: evt.deltaX, 1376 | deltaY: evt.deltaY, 1377 | originalEvent: e 1378 | }); 1379 | }; 1380 | 1381 | VirtualScroll.prototype._onWheel = function(e) { 1382 | var options = this.options; 1383 | if (this._lethargy && this._lethargy.check(e) === false) return; 1384 | var evt = this._event; 1385 | 1386 | // In Chrome and in Firefox (at least the new one) 1387 | evt.deltaX = e.wheelDeltaX || e.deltaX * -1; 1388 | evt.deltaY = e.wheelDeltaY || e.deltaY * -1; 1389 | 1390 | // for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad 1391 | // real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes 1392 | if(support.isFirefox && e.deltaMode == 1) { 1393 | evt.deltaX *= options.firefoxMultiplier; 1394 | evt.deltaY *= options.firefoxMultiplier; 1395 | } 1396 | 1397 | evt.deltaX *= options.mouseMultiplier; 1398 | evt.deltaY *= options.mouseMultiplier; 1399 | 1400 | this._notify(e); 1401 | }; 1402 | 1403 | VirtualScroll.prototype._onMouseWheel = function(e) { 1404 | if (this.options.limitInertia && this._lethargy.check(e) === false) return; 1405 | 1406 | var evt = this._event; 1407 | 1408 | // In Safari, IE and in Chrome if 'wheel' isn't defined 1409 | evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0; 1410 | evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta; 1411 | 1412 | this._notify(e); 1413 | }; 1414 | 1415 | VirtualScroll.prototype._onTouchStart = function(e) { 1416 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1417 | this.touchStartX = t.pageX; 1418 | this.touchStartY = t.pageY; 1419 | }; 1420 | 1421 | VirtualScroll.prototype._onTouchMove = function(e) { 1422 | var options = this.options; 1423 | if(options.preventTouch 1424 | && !e.target.classList.contains(options.unpreventTouchClass)) { 1425 | e.preventDefault(); 1426 | } 1427 | 1428 | var evt = this._event; 1429 | 1430 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 1431 | 1432 | evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier; 1433 | evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier; 1434 | 1435 | this.touchStartX = t.pageX; 1436 | this.touchStartY = t.pageY; 1437 | 1438 | this._notify(e); 1439 | }; 1440 | 1441 | VirtualScroll.prototype._onKeyDown = function(e) { 1442 | var evt = this._event; 1443 | evt.deltaX = evt.deltaY = 0; 1444 | var windowHeight = window.innerHeight - 40 1445 | 1446 | switch(e.keyCode) { 1447 | case keyCodes.LEFT: 1448 | case keyCodes.UP: 1449 | evt.deltaY = this.options.keyStep; 1450 | break; 1451 | 1452 | case keyCodes.RIGHT: 1453 | case keyCodes.DOWN: 1454 | evt.deltaY = - this.options.keyStep; 1455 | break; 1456 | case keyCodes.SPACE && e.shiftKey: 1457 | evt.deltaY = windowHeight; 1458 | break; 1459 | case keyCodes.SPACE: 1460 | evt.deltaY = - windowHeight; 1461 | break; 1462 | default: 1463 | return; 1464 | } 1465 | 1466 | this._notify(e); 1467 | }; 1468 | 1469 | VirtualScroll.prototype._bind = function() { 1470 | if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel, this.listenerOptions); 1471 | if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel, this.listenerOptions); 1472 | 1473 | if(support.hasTouch) { 1474 | this.el.addEventListener('touchstart', this._onTouchStart, this.listenerOptions); 1475 | this.el.addEventListener('touchmove', this._onTouchMove, this.listenerOptions); 1476 | } 1477 | 1478 | if(support.hasPointer && support.hasTouchWin) { 1479 | this.bodyTouchAction = document.body.style.msTouchAction; 1480 | document.body.style.msTouchAction = 'none'; 1481 | this.el.addEventListener('MSPointerDown', this._onTouchStart, true); 1482 | this.el.addEventListener('MSPointerMove', this._onTouchMove, true); 1483 | } 1484 | 1485 | if(support.hasKeyDown) document.addEventListener('keydown', this._onKeyDown); 1486 | }; 1487 | 1488 | VirtualScroll.prototype._unbind = function() { 1489 | if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel); 1490 | if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel); 1491 | 1492 | if(support.hasTouch) { 1493 | this.el.removeEventListener('touchstart', this._onTouchStart); 1494 | this.el.removeEventListener('touchmove', this._onTouchMove); 1495 | } 1496 | 1497 | if(support.hasPointer && support.hasTouchWin) { 1498 | document.body.style.msTouchAction = this.bodyTouchAction; 1499 | this.el.removeEventListener('MSPointerDown', this._onTouchStart, true); 1500 | this.el.removeEventListener('MSPointerMove', this._onTouchMove, true); 1501 | } 1502 | 1503 | if(support.hasKeyDown) document.removeEventListener('keydown', this._onKeyDown); 1504 | }; 1505 | 1506 | VirtualScroll.prototype.on = function(cb, ctx) { 1507 | this._emitter.on(EVT_ID, cb, ctx); 1508 | 1509 | var events = this._emitter.e; 1510 | if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind(); 1511 | }; 1512 | 1513 | VirtualScroll.prototype.off = function(cb, ctx) { 1514 | this._emitter.off(EVT_ID, cb, ctx); 1515 | 1516 | var events = this._emitter.e; 1517 | if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind(); 1518 | }; 1519 | 1520 | VirtualScroll.prototype.reset = function() { 1521 | var evt = this._event; 1522 | evt.x = 0; 1523 | evt.y = 0; 1524 | }; 1525 | 1526 | VirtualScroll.prototype.destroy = function() { 1527 | this._emitter.off(); 1528 | this._unbind(); 1529 | }; 1530 | 1531 | },{"./clone":16,"./support":18,"bindall-standalone":4,"lethargy":9,"object-assign":10,"tiny-emitter":15}],18:[function(require,module,exports){ 1532 | 'use strict'; 1533 | 1534 | module.exports = (function getSupport() { 1535 | return { 1536 | hasWheelEvent: 'onwheel' in document, 1537 | hasMouseWheelEvent: 'onmousewheel' in document, 1538 | hasTouch: 'ontouchstart' in document, 1539 | hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1, 1540 | hasPointer: !!window.navigator.msPointerEnabled, 1541 | hasKeyDown: 'onkeydown' in document, 1542 | isFirefox: navigator.userAgent.indexOf('Firefox') > -1 1543 | }; 1544 | })(); 1545 | 1546 | },{}]},{},[2]); 1547 | --------------------------------------------------------------------------------