├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── build ├── manager.js └── manager.min.js ├── demo ├── index.html ├── index.js └── style.css ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Baptiste Briel 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slider-manager 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | ## Usage 6 | 7 | [![NPM](https://nodei.co/npm/slider-manager.png)](https://www.npmjs.com/package/slider-manager) 8 | 9 | `npm install slider-manager --save` 10 | 11 | ```javascript 12 | import Manager from 'slider-manager' 13 | import gsap from 'gsap' 14 | 15 | const slides = [].slice.call(document.querySelectorAll('.slides')) 16 | 17 | const slider = new Manager({ 18 | length: slides.length, 19 | loop: true, 20 | callback: (event) => { 21 | 22 | const index = event.current 23 | const previous = event.previous 24 | const down = event.direction === 'downwards' 25 | 26 | slider.animating = true 27 | 28 | this.videos[index].play() 29 | 30 | const windowheight = window.innerHeight 31 | const tl = new TimelineMax({ paused: true, onComplete: () => { 32 | 33 | this.videos[previous].pause() 34 | slider.animating = false 35 | }}) 36 | 37 | tl.staggerTo(slides, 1, { cycle: { 38 | y: (loop) => index === loop ? 0 : loop < index ? -windowheight : windowheight 39 | }, ease: Expo.easeInOut}, 0, 0) 40 | 41 | tl.restart() 42 | } 43 | }) 44 | 45 | slider.init() 46 | ``` 47 | 48 | ### options 49 | 50 | - `el`: HTMLElement to listen for scroll/touch events - defaults to document.body 51 | - `direction`: 'x' or 'y' - defaults to 'y' 52 | - `loop`: true of false - defaults to false 53 | - `delta`: delta limiter for scroll/touch events - defaults to 1 54 | - `limitInertia`: if set to true, leverage the limitInertia option of [virtual-scroll](https://github.com/ayamflow/virtual-scroll) to distinguish between user-initiated and inertial scrolling. Prevents callback from firing multiple times per scroll - defaults to false. 55 | - `callback`: function called when user has swiped or scrolled 56 | 57 | ### methods 58 | 59 | - `init`: add event listeners 60 | - `goTo(index)`: goes to the slide index 61 | - `destroy`: remove event listeners 62 | 63 | ### changelog 64 | 65 | - `1.0.6`: added direction to options, so it listens to either deltaX or deltaY depending on the desired option 66 | - `1.1.6`: added limitInertia to options, that when set to true fixes a behavior where multiple callbacks are fired for a single user-initiated scroll due to inertial scrolling. 67 | 68 | ## License 69 | 70 | MIT, see [LICENSE.md](http://github.com/BaptisteBriel/slider-manager/blob/master/LICENSE.md) for details. 71 | -------------------------------------------------------------------------------- /build/manager.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.buildmanager = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && arguments[0] !== undefined ? arguments[0] : {}; 27 | 28 | _classCallCheck(this, Manager); 29 | 30 | if (!opt.callback) console.error('You need to provide a callback function in the options'); 31 | 32 | this.el = opt.el || document.body; 33 | this.animating = false; 34 | 35 | this.index = 0; 36 | this.length = opt.length; 37 | 38 | this.options = { 39 | direction: opt.direction || 'y', 40 | loop: opt.loop || false, 41 | delta: opt.delta || 1, 42 | callback: opt.callback, 43 | limitInertia: opt.limitInertia || false, 44 | passive: opt.passive || undefined 45 | }; 46 | 47 | this.vs = null; 48 | 49 | this.onScroll = this.onScroll.bind(this); 50 | this.onKeyDown = this.onKeyDown.bind(this); 51 | } 52 | 53 | _createClass(Manager, [{ 54 | key: 'init', 55 | value: function init() { 56 | 57 | this.vs = new _virtualScroll2.default({ 58 | passive: this.options.passive, 59 | limitInertia: this.options.limitInertia 60 | }); 61 | 62 | this.vs.on(this.onScroll); 63 | 64 | if (_sniffer2.default.isDesktop) { 65 | (0, _domEvent.on)(document, 'keydown', this.onKeyDown); 66 | } 67 | } 68 | }, { 69 | key: 'destroy', 70 | value: function destroy() { 71 | 72 | this.vs.off(this.onScroll); 73 | this.vs.destroy(); 74 | this.vs = null; 75 | 76 | if (_sniffer2.default.isDesktop) { 77 | (0, _domEvent.off)(document, 'keydown', this.onKeyDown); 78 | } 79 | } 80 | }, { 81 | key: 'getNext', 82 | value: function getNext(delta) { 83 | 84 | var next = delta >= this.options.delta ? this.index + 1 : this.index - 1; 85 | 86 | return this.checkLoop(next); 87 | } 88 | }, { 89 | key: 'checkLoop', 90 | value: function checkLoop(next) { 91 | 92 | return next < 0 ? this.options.loop ? this.length : 0 : next > this.length ? this.options.loop ? 0 : this.length : next; 93 | } 94 | }, { 95 | key: 'getEvent', 96 | value: function getEvent(index) { 97 | 98 | var prev = this.options.direction == 'y' ? 'up' : 'left'; 99 | var next = this.options.direction == 'y' ? 'down' : 'right'; 100 | 101 | var direction = index > this.index ? next : prev; 102 | if (this.options.loop) { 103 | if (this.index == 0 && index == this.length) direction = prev; 104 | if (this.index == this.length && index == 0) direction = next; 105 | } 106 | 107 | return { 108 | current: index, 109 | previous: this.index, 110 | direction: direction 111 | }; 112 | } 113 | }, { 114 | key: 'onScroll', 115 | value: function onScroll(event) { 116 | var deltaX = event.deltaX, 117 | deltaY = event.deltaY; 118 | 119 | var norm = this.options.direction == 'y' ? deltaY - deltaY * 2 : deltaX - deltaX * 2; 120 | 121 | if (this.animating || norm > -this.options.delta && norm < this.options.delta) return; 122 | this.animating = true; 123 | 124 | this.callback(norm); 125 | } 126 | }, { 127 | key: 'onKeyDown', 128 | value: function onKeyDown(e) { 129 | 130 | var prev = this.options.direction == 'y' ? '38' : '37'; 131 | var next = this.options.direction == 'y' ? '40' : '39'; 132 | 133 | if (this.animating || e.keyCode != prev && e.keyCode != next) return; 134 | this.animating = true; 135 | 136 | this.callback(e.keyCode == next ? this.options.delta + 1 : -(this.options.delta + 1)); 137 | } 138 | }, { 139 | key: 'goTo', 140 | value: function goTo(index) { 141 | 142 | var check = this.checkLoop(index); 143 | var event = this.getEvent(check); 144 | 145 | this.index = check; 146 | this.options.callback(event); 147 | } 148 | }, { 149 | key: 'callback', 150 | value: function callback(delta) { 151 | 152 | var index = this.getNext(delta); 153 | var event = this.getEvent(index); 154 | 155 | this.index = index; 156 | this.options.callback(event, delta); 157 | } 158 | }]); 159 | 160 | return Manager; 161 | }(); 162 | 163 | exports.default = Manager; 164 | 165 | },{"dom-event":4,"sniffer":7,"virtual-scroll":10}],2:[function(require,module,exports){ 166 | 'use strict'; 167 | 168 | var toString = Object.prototype.toString, 169 | hasOwnProperty = Object.prototype.hasOwnProperty; 170 | 171 | module.exports = function(object) { 172 | if(!object) return console.warn('bindAll requires at least one argument.'); 173 | 174 | var functions = Array.prototype.slice.call(arguments, 1); 175 | 176 | if (functions.length === 0) { 177 | 178 | for (var method in object) { 179 | if(hasOwnProperty.call(object, method)) { 180 | if(typeof object[method] == 'function' && toString.call(object[method]) == "[object Function]") { 181 | functions.push(method); 182 | } 183 | } 184 | } 185 | } 186 | 187 | for(var i = 0; i < functions.length; i++) { 188 | var f = functions[i]; 189 | object[f] = bind(object[f], object); 190 | } 191 | }; 192 | 193 | /* 194 | Faster bind without specific-case checking. (see https://coderwall.com/p/oi3j3w). 195 | bindAll is only needed for events binding so no need to make slow fixes for constructor 196 | or partial application. 197 | */ 198 | function bind(func, context) { 199 | return function() { 200 | return func.apply(context, arguments); 201 | }; 202 | } 203 | },{}],3:[function(require,module,exports){ 204 | /*! 205 | * dashify 206 | * 207 | * Copyright (c) 2015 Jon Schlinkert. 208 | * Licensed under the MIT license. 209 | */ 210 | 211 | 'use strict'; 212 | 213 | module.exports = function dashify(str) { 214 | if (typeof str !== 'string') { 215 | throw new TypeError('expected a string'); 216 | } 217 | str = str.replace(/([a-z])([A-Z])/g, '$1-$2'); 218 | str = str.replace(/[ \t\W]/g, '-'); 219 | str = str.replace(/^-+|-+$/g, ''); 220 | return str.toLowerCase(); 221 | }; 222 | 223 | },{}],4:[function(require,module,exports){ 224 | module.exports = on; 225 | module.exports.on = on; 226 | module.exports.off = off; 227 | 228 | function on (element, event, callback, capture) { 229 | !element.addEventListener && (event = 'on' + event); 230 | (element.addEventListener || element.attachEvent).call(element, event, callback, capture); 231 | return callback; 232 | } 233 | 234 | function off (element, event, callback, capture) { 235 | !element.removeEventListener && (event = 'on' + event); 236 | (element.removeEventListener || element.detachEvent).call(element, event, callback, capture); 237 | return callback; 238 | } 239 | 240 | },{}],5:[function(require,module,exports){ 241 | // Generated by CoffeeScript 1.9.2 242 | (function() { 243 | var root; 244 | 245 | root = typeof exports !== "undefined" && exports !== null ? exports : this; 246 | 247 | root.Lethargy = (function() { 248 | function Lethargy(stability, sensitivity, tolerance, delay) { 249 | this.stability = stability != null ? Math.abs(stability) : 8; 250 | this.sensitivity = sensitivity != null ? 1 + Math.abs(sensitivity) : 100; 251 | this.tolerance = tolerance != null ? 1 + Math.abs(tolerance) : 1.1; 252 | this.delay = delay != null ? delay : 150; 253 | this.lastUpDeltas = (function() { 254 | var i, ref, results; 255 | results = []; 256 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 257 | results.push(null); 258 | } 259 | return results; 260 | }).call(this); 261 | this.lastDownDeltas = (function() { 262 | var i, ref, results; 263 | results = []; 264 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 265 | results.push(null); 266 | } 267 | return results; 268 | }).call(this); 269 | this.deltasTimestamp = (function() { 270 | var i, ref, results; 271 | results = []; 272 | for (i = 1, ref = this.stability * 2; 1 <= ref ? i <= ref : i >= ref; 1 <= ref ? i++ : i--) { 273 | results.push(null); 274 | } 275 | return results; 276 | }).call(this); 277 | } 278 | 279 | Lethargy.prototype.check = function(e) { 280 | var lastDelta; 281 | e = e.originalEvent || e; 282 | if (e.wheelDelta != null) { 283 | lastDelta = e.wheelDelta; 284 | } else if (e.deltaY != null) { 285 | lastDelta = e.deltaY * -40; 286 | } else if ((e.detail != null) || e.detail === 0) { 287 | lastDelta = e.detail * -40; 288 | } 289 | this.deltasTimestamp.push(Date.now()); 290 | this.deltasTimestamp.shift(); 291 | if (lastDelta > 0) { 292 | this.lastUpDeltas.push(lastDelta); 293 | this.lastUpDeltas.shift(); 294 | return this.isInertia(1); 295 | } else { 296 | this.lastDownDeltas.push(lastDelta); 297 | this.lastDownDeltas.shift(); 298 | return this.isInertia(-1); 299 | } 300 | return false; 301 | }; 302 | 303 | Lethargy.prototype.isInertia = function(direction) { 304 | var lastDeltas, lastDeltasNew, lastDeltasOld, newAverage, newSum, oldAverage, oldSum; 305 | lastDeltas = direction === -1 ? this.lastDownDeltas : this.lastUpDeltas; 306 | if (lastDeltas[0] === null) { 307 | return direction; 308 | } 309 | if (this.deltasTimestamp[(this.stability * 2) - 2] + this.delay > Date.now() && lastDeltas[0] === lastDeltas[(this.stability * 2) - 1]) { 310 | return false; 311 | } 312 | lastDeltasOld = lastDeltas.slice(0, this.stability); 313 | lastDeltasNew = lastDeltas.slice(this.stability, this.stability * 2); 314 | oldSum = lastDeltasOld.reduce(function(t, s) { 315 | return t + s; 316 | }); 317 | newSum = lastDeltasNew.reduce(function(t, s) { 318 | return t + s; 319 | }); 320 | oldAverage = oldSum / lastDeltasOld.length; 321 | newAverage = newSum / lastDeltasNew.length; 322 | if (Math.abs(oldAverage) < Math.abs(newAverage * this.tolerance) && (this.sensitivity < Math.abs(newAverage))) { 323 | return direction; 324 | } else { 325 | return false; 326 | } 327 | }; 328 | 329 | Lethargy.prototype.showLastUpDeltas = function() { 330 | return this.lastUpDeltas; 331 | }; 332 | 333 | Lethargy.prototype.showLastDownDeltas = function() { 334 | return this.lastDownDeltas; 335 | }; 336 | 337 | return Lethargy; 338 | 339 | })(); 340 | 341 | }).call(this); 342 | 343 | },{}],6:[function(require,module,exports){ 344 | /* 345 | object-assign 346 | (c) Sindre Sorhus 347 | @license MIT 348 | */ 349 | 350 | 'use strict'; 351 | /* eslint-disable no-unused-vars */ 352 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 353 | var hasOwnProperty = Object.prototype.hasOwnProperty; 354 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 355 | 356 | function toObject(val) { 357 | if (val === null || val === undefined) { 358 | throw new TypeError('Object.assign cannot be called with null or undefined'); 359 | } 360 | 361 | return Object(val); 362 | } 363 | 364 | function shouldUseNative() { 365 | try { 366 | if (!Object.assign) { 367 | return false; 368 | } 369 | 370 | // Detect buggy property enumeration order in older V8 versions. 371 | 372 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 373 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 374 | test1[5] = 'de'; 375 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 376 | return false; 377 | } 378 | 379 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 380 | var test2 = {}; 381 | for (var i = 0; i < 10; i++) { 382 | test2['_' + String.fromCharCode(i)] = i; 383 | } 384 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 385 | return test2[n]; 386 | }); 387 | if (order2.join('') !== '0123456789') { 388 | return false; 389 | } 390 | 391 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 392 | var test3 = {}; 393 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 394 | test3[letter] = letter; 395 | }); 396 | if (Object.keys(Object.assign({}, test3)).join('') !== 397 | 'abcdefghijklmnopqrst') { 398 | return false; 399 | } 400 | 401 | return true; 402 | } catch (err) { 403 | // We don't expect any of the above to throw, but better to be safe. 404 | return false; 405 | } 406 | } 407 | 408 | module.exports = shouldUseNative() ? Object.assign : function (target, source) { 409 | var from; 410 | var to = toObject(target); 411 | var symbols; 412 | 413 | for (var s = 1; s < arguments.length; s++) { 414 | from = Object(arguments[s]); 415 | 416 | for (var key in from) { 417 | if (hasOwnProperty.call(from, key)) { 418 | to[key] = from[key]; 419 | } 420 | } 421 | 422 | if (getOwnPropertySymbols) { 423 | symbols = getOwnPropertySymbols(from); 424 | for (var i = 0; i < symbols.length; i++) { 425 | if (propIsEnumerable.call(from, symbols[i])) { 426 | to[symbols[i]] = from[symbols[i]]; 427 | } 428 | } 429 | } 430 | } 431 | 432 | return to; 433 | }; 434 | 435 | },{}],7:[function(require,module,exports){ 436 | 'use strict'; 437 | 438 | var dashify = require('dashify'); 439 | 440 | module.exports = new Sniffer(); 441 | 442 | function Sniffer() { 443 | var ua = navigator.userAgent.toLowerCase(); 444 | var av = navigator.appVersion.toLowerCase(); 445 | 446 | var isDroidPhone = /android.*mobile/.test(ua); 447 | var isDroidTablet = !isDroidPhone && (/android/i).test(ua); 448 | var isDroid = isDroidPhone || isDroidTablet; 449 | 450 | var isIos = (/ip(hone|od|ad)/i).test(ua) && !window.MSStream; 451 | var isIpad = (/ipad/i).test(ua) && isIos; 452 | 453 | var isTablet = isDroidTablet || isIpad; 454 | var isPhone = isDroidPhone || (isIos && !isIpad); 455 | var isDevice = isPhone || isTablet; 456 | 457 | var isFirefox = ua.indexOf('firefox') > -1; 458 | var isSafari = !!ua.match(/version\/[\d\.]+.*safari/); 459 | var isOpera = ua.indexOf('opr') > -1; 460 | var isIE11 = !(window.ActiveXObject) && "ActiveXObject" in window; 461 | var isIE = av.indexOf('msie') > -1 || isIE11 || av.indexOf('edge') > -1; 462 | var isEdge = ua.indexOf('edge') > -1; 463 | var isChrome = window.chrome !== null && window.chrome !== undefined && navigator.vendor.toLowerCase() == 'google inc.' && !isOpera && !isEdge; 464 | 465 | this.infos = { 466 | isDroid: isDroid, 467 | isDroidPhone: isDroidPhone, 468 | isDroidTablet: isDroidTablet, 469 | isIos: isIos, 470 | isIpad: isIpad, 471 | isDevice: isDevice, 472 | isEdge: isEdge, 473 | isIE: isIE, 474 | isIE11: isIE11, 475 | isPhone: isPhone, 476 | isTablet: isTablet, 477 | isFirefox: isFirefox, 478 | isSafari: isSafari, 479 | isOpera: isOpera, 480 | isChrome: isChrome, 481 | isDesktop: !isPhone && !isTablet 482 | }; 483 | 484 | Object.keys(this.infos).forEach(function(info) { 485 | Object.defineProperty(this, info, { 486 | get: function () { 487 | return this.infos[info]; 488 | } 489 | }); 490 | }, this); 491 | 492 | Object.freeze(this); 493 | 494 | // TODO: add getVersion() to get IE/Safari/... version 495 | } 496 | 497 | Sniffer.prototype.addClasses = function(el) { 498 | Object.keys(this.infos).forEach(function(info) { 499 | if (this.infos[info]) addClass(el, dashify(info)); 500 | }, this); 501 | }; 502 | 503 | Sniffer.prototype.getInfos = function() { 504 | return clone(this.infos); 505 | }; 506 | 507 | function addClass(el, className) { 508 | if (el.addClass) el.addClass(className); 509 | else if (el.classList) el.classList.add(className); 510 | else el.className += ' ' + className; 511 | } 512 | 513 | function clone(source) { 514 | return JSON.parse(JSON.stringify(source)); 515 | } 516 | },{"dashify":3}],8:[function(require,module,exports){ 517 | function E () { 518 | // Keep this empty so it's easier to inherit from 519 | // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 520 | } 521 | 522 | E.prototype = { 523 | on: function (name, callback, ctx) { 524 | var e = this.e || (this.e = {}); 525 | 526 | (e[name] || (e[name] = [])).push({ 527 | fn: callback, 528 | ctx: ctx 529 | }); 530 | 531 | return this; 532 | }, 533 | 534 | once: function (name, callback, ctx) { 535 | var self = this; 536 | function listener () { 537 | self.off(name, listener); 538 | callback.apply(ctx, arguments); 539 | }; 540 | 541 | listener._ = callback 542 | return this.on(name, listener, ctx); 543 | }, 544 | 545 | emit: function (name) { 546 | var data = [].slice.call(arguments, 1); 547 | var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 548 | var i = 0; 549 | var len = evtArr.length; 550 | 551 | for (i; i < len; i++) { 552 | evtArr[i].fn.apply(evtArr[i].ctx, data); 553 | } 554 | 555 | return this; 556 | }, 557 | 558 | off: function (name, callback) { 559 | var e = this.e || (this.e = {}); 560 | var evts = e[name]; 561 | var liveEvents = []; 562 | 563 | if (evts && callback) { 564 | for (var i = 0, len = evts.length; i < len; i++) { 565 | if (evts[i].fn !== callback && evts[i].fn._ !== callback) 566 | liveEvents.push(evts[i]); 567 | } 568 | } 569 | 570 | // Remove event from queue to prevent memory leak 571 | // Suggested by https://github.com/lazd 572 | // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 573 | 574 | (liveEvents.length) 575 | ? e[name] = liveEvents 576 | : delete e[name]; 577 | 578 | return this; 579 | } 580 | }; 581 | 582 | module.exports = E; 583 | 584 | },{}],9:[function(require,module,exports){ 585 | 'use strict'; 586 | 587 | module.exports = function(source) { 588 | return JSON.parse(JSON.stringify(source)); 589 | }; 590 | },{}],10:[function(require,module,exports){ 591 | 'use strict'; 592 | 593 | var objectAssign = require('object-assign'); 594 | var Emitter = require('tiny-emitter'); 595 | var Lethargy = require('lethargy').Lethargy; 596 | var support = require('./support'); 597 | var clone = require('./clone'); 598 | var bindAll = require('bindall-standalone'); 599 | var EVT_ID = 'virtualscroll'; 600 | 601 | module.exports = VirtualScroll; 602 | 603 | var keyCodes = { 604 | LEFT: 37, 605 | UP: 38, 606 | RIGHT: 39, 607 | DOWN: 40 608 | }; 609 | 610 | function VirtualScroll(options) { 611 | bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown'); 612 | 613 | this.el = window; 614 | if (options && options.el) { 615 | this.el = options.el; 616 | delete options.el; 617 | } 618 | this.options = objectAssign({ 619 | mouseMultiplier: 1, 620 | touchMultiplier: 2, 621 | firefoxMultiplier: 15, 622 | keyStep: 120, 623 | preventTouch: false, 624 | unpreventTouchClass: 'vs-touchmove-allowed', 625 | limitInertia: false 626 | }, options); 627 | 628 | if (this.options.limitInertia) this._lethargy = new Lethargy(); 629 | 630 | this._emitter = new Emitter(); 631 | this._event = { 632 | y: 0, 633 | x: 0, 634 | deltaX: 0, 635 | deltaY: 0 636 | }; 637 | 638 | this.touchStartX = null; 639 | this.touchStartY = null; 640 | this.bodyTouchAction = null; 641 | } 642 | 643 | VirtualScroll.prototype._notify = function(e) { 644 | var evt = this._event; 645 | evt.x += evt.deltaX; 646 | evt.y += evt.deltaY; 647 | 648 | this._emitter.emit(EVT_ID, { 649 | x: evt.x, 650 | y: evt.y, 651 | deltaX: evt.deltaX, 652 | deltaY: evt.deltaY, 653 | originalEvent: e 654 | }); 655 | }; 656 | 657 | VirtualScroll.prototype._onWheel = function(e) { 658 | var options = this.options; 659 | if (this._lethargy && this._lethargy.check(e) === false) return; 660 | 661 | var evt = this._event; 662 | 663 | // In Chrome and in Firefox (at least the new one) 664 | evt.deltaX = e.wheelDeltaX || e.deltaX * -1; 665 | evt.deltaY = e.wheelDeltaY || e.deltaY * -1; 666 | 667 | // for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad 668 | // real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes 669 | if(support.isFirefox && e.deltaMode == 1) { 670 | evt.deltaX *= options.firefoxMultiplier; 671 | evt.deltaY *= options.firefoxMultiplier; 672 | } 673 | 674 | evt.deltaX *= options.mouseMultiplier; 675 | evt.deltaY *= options.mouseMultiplier; 676 | 677 | this._notify(e); 678 | }; 679 | 680 | VirtualScroll.prototype._onMouseWheel = function(e) { 681 | if (this.options.limitInertia && this._lethargy.check(e) === false) return; 682 | 683 | var evt = this._event; 684 | 685 | // In Safari, IE and in Chrome if 'wheel' isn't defined 686 | evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0; 687 | evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta; 688 | 689 | this._notify(e); 690 | }; 691 | 692 | VirtualScroll.prototype._onTouchStart = function(e) { 693 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 694 | this.touchStartX = t.pageX; 695 | this.touchStartY = t.pageY; 696 | }; 697 | 698 | VirtualScroll.prototype._onTouchMove = function(e) { 699 | var options = this.options; 700 | if(options.preventTouch 701 | && !e.target.classList.contains(options.unpreventTouchClass)) { 702 | e.preventDefault(); 703 | } 704 | 705 | var evt = this._event; 706 | 707 | var t = (e.targetTouches) ? e.targetTouches[0] : e; 708 | 709 | evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier; 710 | evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier; 711 | 712 | this.touchStartX = t.pageX; 713 | this.touchStartY = t.pageY; 714 | 715 | this._notify(e); 716 | }; 717 | 718 | VirtualScroll.prototype._onKeyDown = function(e) { 719 | var evt = this._event; 720 | evt.deltaX = evt.deltaY = 0; 721 | 722 | switch(e.keyCode) { 723 | case keyCodes.LEFT: 724 | case keyCodes.UP: 725 | evt.deltaY = this.options.keyStep; 726 | break; 727 | 728 | case keyCodes.RIGHT: 729 | case keyCodes.DOWN: 730 | evt.deltaY = - this.options.keyStep; 731 | break; 732 | 733 | default: 734 | return; 735 | } 736 | 737 | this._notify(e); 738 | }; 739 | 740 | VirtualScroll.prototype._bind = function() { 741 | if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel); 742 | if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel); 743 | 744 | if(support.hasTouch) { 745 | this.el.addEventListener('touchstart', this._onTouchStart); 746 | this.el.addEventListener('touchmove', this._onTouchMove); 747 | } 748 | 749 | if(support.hasPointer && support.hasTouchWin) { 750 | this.bodyTouchAction = document.body.style.msTouchAction; 751 | document.body.style.msTouchAction = 'none'; 752 | this.el.addEventListener('MSPointerDown', this._onTouchStart, true); 753 | this.el.addEventListener('MSPointerMove', this._onTouchMove, true); 754 | } 755 | 756 | if(support.hasKeyDown) document.addEventListener('keydown', this._onKeyDown); 757 | }; 758 | 759 | VirtualScroll.prototype._unbind = function() { 760 | if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel); 761 | if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel); 762 | 763 | if(support.hasTouch) { 764 | this.el.removeEventListener('touchstart', this._onTouchStart); 765 | this.el.removeEventListener('touchmove', this._onTouchMove); 766 | } 767 | 768 | if(support.hasPointer && support.hasTouchWin) { 769 | document.body.style.msTouchAction = this.bodyTouchAction; 770 | this.el.removeEventListener('MSPointerDown', this._onTouchStart, true); 771 | this.el.removeEventListener('MSPointerMove', this._onTouchMove, true); 772 | } 773 | 774 | if(support.hasKeyDown) document.removeEventListener('keydown', this._onKeyDown); 775 | }; 776 | 777 | VirtualScroll.prototype.on = function(cb, ctx) { 778 | this._emitter.on(EVT_ID, cb, ctx); 779 | 780 | var events = this._emitter.e; 781 | if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind(); 782 | }; 783 | 784 | VirtualScroll.prototype.off = function(cb, ctx) { 785 | this._emitter.off(EVT_ID, cb, ctx); 786 | 787 | var events = this._emitter.e; 788 | if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind(); 789 | }; 790 | 791 | VirtualScroll.prototype.reset = function() { 792 | var evt = this._event; 793 | evt.x = 0; 794 | evt.y = 0; 795 | }; 796 | 797 | VirtualScroll.prototype.destroy = function() { 798 | this._emitter.off(); 799 | this._unbind(); 800 | }; 801 | 802 | },{"./clone":9,"./support":11,"bindall-standalone":2,"lethargy":5,"object-assign":6,"tiny-emitter":8}],11:[function(require,module,exports){ 803 | 'use strict'; 804 | 805 | module.exports = (function getSupport() { 806 | return { 807 | hasWheelEvent: 'onwheel' in document, 808 | hasMouseWheelEvent: 'onmousewheel' in document, 809 | hasTouch: 'ontouchstart' in document, 810 | hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1, 811 | hasPointer: !!window.navigator.msPointerEnabled, 812 | hasKeyDown: 'onkeydown' in document, 813 | isFirefox: navigator.userAgent.indexOf('Firefox') > -1 814 | }; 815 | })(); 816 | },{}]},{},[1])(1) 817 | }); -------------------------------------------------------------------------------- /build/manager.min.js: -------------------------------------------------------------------------------- 1 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.buildmanager=t()}}(function(){return function t(e,i,n){function o(r,a){if(!i[r]){if(!e[r]){var l="function"==typeof require&&require;if(!a&&l)return l(r,!0);if(s)return s(r,!0);var h=new Error("Cannot find module '"+r+"'");throw h.code="MODULE_NOT_FOUND",h}var u=i[r]={exports:{}};e[r][0].call(u.exports,function(t){var i=e[r][1][t];return o(i?i:t)},u,u.exports,t,e,i,n)}return i[r].exports}for(var s="function"==typeof require&&require,r=0;r0&&void 0!==arguments[0]?arguments[0]:{};o(this,t),e.callback||console.error("You need to provide a callback function in the options"),this.el=e.el||document.body,this.animating=!1,this.index=0,this.length=e.length,this.options={direction:e.direction||"y",loop:e.loop||!1,delta:e.delta||1,callback:e.callback,limitInertia:e.limitInertia||!1,passive:e.passive||void 0},this.vs=null,this.onScroll=this.onScroll.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}return s(t,[{key:"init",value:function(){this.vs=new a["default"]({passive:this.options.passive,limitInertia:this.options.limitInertia}),this.vs.on(this.onScroll),h["default"].isDesktop&&u.on(document,"keydown",this.onKeyDown)}},{key:"destroy",value:function(){this.vs.off(this.onScroll),this.vs.destroy(),this.vs=null,h["default"].isDesktop&&u.off(document,"keydown",this.onKeyDown)}},{key:"getNext",value:function(t){var e=t>=this.options.delta?this.index+1:this.index-1;return this.checkLoop(e)}},{key:"checkLoop",value:function(t){return 0>t?this.options.loop?this.length:0:t>this.length?this.options.loop?0:this.length:t}},{key:"getEvent",value:function(t){var e="y"==this.options.direction?"up":"left",i="y"==this.options.direction?"down":"right",n=t>this.index?i:e;return this.options.loop&&(0==this.index&&t==this.length&&(n=e),this.index==this.length&&0==t&&(n=i)),{current:t,previous:this.index,direction:n}}},{key:"onScroll",value:function(t){var e=t.deltaX,i=t.deltaY,n="y"==this.options.direction?i-2*i:e-2*e;this.animating||n>-this.options.delta&&n=1?e>=t:t>=e;e>=1?t++:t--)i.push(null);return i}.call(this),this.lastDownDeltas=function(){var t,e,i;for(i=[],t=1,e=2*this.stability;e>=1?e>=t:t>=e;e>=1?t++:t--)i.push(null);return i}.call(this),this.deltasTimestamp=function(){var t,e,i;for(i=[],t=1,e=2*this.stability;e>=1?e>=t:t>=e;e>=1?t++:t--)i.push(null);return i}.call(this)}return t.prototype.check=function(t){var e;return t=t.originalEvent||t,null!=t.wheelDelta?e=t.wheelDelta:null!=t.deltaY?e=-40*t.deltaY:(null!=t.detail||0===t.detail)&&(e=-40*t.detail),this.deltasTimestamp.push(Date.now()),this.deltasTimestamp.shift(),e>0?(this.lastUpDeltas.push(e),this.lastUpDeltas.shift(),this.isInertia(1)):(this.lastDownDeltas.push(e),this.lastDownDeltas.shift(),this.isInertia(-1))},t.prototype.isInertia=function(t){var e,i,n,o,s,r,a;return e=-1===t?this.lastDownDeltas:this.lastUpDeltas,null===e[0]?t:this.deltasTimestamp[2*this.stability-2]+this.delay>Date.now()&&e[0]===e[2*this.stability-1]?!1:(n=e.slice(0,this.stability),i=e.slice(this.stability,2*this.stability),a=n.reduce(function(t,e){return t+e}),s=i.reduce(function(t,e){return t+e}),r=a/n.length,o=s/i.length,Math.abs(r)i;i++)e["_"+String.fromCharCode(i)]=i;var n=Object.getOwnPropertyNames(e).map(function(t){return e[t]});if("0123456789"!==n.join(""))return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach(function(t){o[t]=t}),"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},o)).join("")?!1:!0}catch(s){return!1}}var o=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=n()?Object.assign:function(t){for(var e,n,a=i(t),l=1;l-1,c=!!t.match(/version\/[\d\.]+.*safari/),f=t.indexOf("opr")>-1,d=!window.ActiveXObject&&"ActiveXObject"in window,p=e.indexOf("msie")>-1||d||e.indexOf("edge")>-1,v=t.indexOf("edge")>-1,y=null!==window.chrome&&void 0!==window.chrome&&"google inc."==navigator.vendor.toLowerCase()&&!f&&!v;this.infos={isDroid:o,isDroidPhone:i,isDroidTablet:n,isIos:s,isIpad:r,isDevice:h,isEdge:v,isIE:p,isIE11:d,isPhone:l,isTablet:a,isFirefox:u,isSafari:c,isOpera:f,isChrome:y,isDesktop:!l&&!a},Object.keys(this.infos).forEach(function(t){Object.defineProperty(this,t,{get:function(){return this.infos[t]}})},this),Object.freeze(this)}function n(t,e){t.addClass?t.addClass(e):t.classList?t.classList.add(e):t.className+=" "+e}function o(t){return JSON.parse(JSON.stringify(t))}var s=t("dashify");e.exports=new i,i.prototype.addClasses=function(t){Object.keys(this.infos).forEach(function(e){this.infos[e]&&n(t,s(e))},this)},i.prototype.getInfos=function(){return o(this.infos)}},{dashify:3}],8:[function(t,e){function i(){}i.prototype={on:function(t,e,i){var n=this.e||(this.e={});return(n[t]||(n[t]=[])).push({fn:e,ctx:i}),this},once:function(t,e,i){function n(){o.off(t,n),e.apply(i,arguments)}var o=this;return n._=e,this.on(t,n,i)},emit:function(t){var e=[].slice.call(arguments,1),i=((this.e||(this.e={}))[t]||[]).slice(),n=0,o=i.length;for(n;o>n;n++)i[n].fn.apply(i[n].ctx,e);return this},off:function(t,e){var i=this.e||(this.e={}),n=i[t],o=[];if(n&&e)for(var s=0,r=n.length;r>s;s++)n[s].fn!==e&&n[s].fn._!==e&&o.push(n[s]);return o.length?i[t]=o:delete i[t],this}},e.exports=i},{}],9:[function(t,e){"use strict";e.exports=function(t){return JSON.parse(JSON.stringify(t))}},{}],10:[function(t,e){"use strict";function i(t){a(this,"_onWheel","_onMouseWheel","_onTouchStart","_onTouchMove","_onKeyDown"),this.el=window,t&&t.el&&(this.el=t.el,delete t.el),this.options=n({mouseMultiplier:1,touchMultiplier:2,firefoxMultiplier:15,keyStep:120,preventTouch:!1,unpreventTouchClass:"vs-touchmove-allowed",limitInertia:!1},t),this.options.limitInertia&&(this._lethargy=new s),this._emitter=new o,this._event={y:0,x:0,deltaX:0,deltaY:0},this.touchStartX=null,this.touchStartY=null,this.bodyTouchAction=null}var n=t("object-assign"),o=t("tiny-emitter"),s=t("lethargy").Lethargy,r=t("./support"),a=(t("./clone"),t("bindall-standalone")),l="virtualscroll";e.exports=i;var h={LEFT:37,UP:38,RIGHT:39,DOWN:40};i.prototype._notify=function(t){var e=this._event;e.x+=e.deltaX,e.y+=e.deltaY,this._emitter.emit(l,{x:e.x,y:e.y,deltaX:e.deltaX,deltaY:e.deltaY,originalEvent:t})},i.prototype._onWheel=function(t){var e=this.options;if(!this._lethargy||this._lethargy.check(t)!==!1){var i=this._event;i.deltaX=t.wheelDeltaX||-1*t.deltaX,i.deltaY=t.wheelDeltaY||-1*t.deltaY,r.isFirefox&&1==t.deltaMode&&(i.deltaX*=e.firefoxMultiplier,i.deltaY*=e.firefoxMultiplier),i.deltaX*=e.mouseMultiplier,i.deltaY*=e.mouseMultiplier,this._notify(t)}},i.prototype._onMouseWheel=function(t){if(!this.options.limitInertia||this._lethargy.check(t)!==!1){var e=this._event;e.deltaX=t.wheelDeltaX?t.wheelDeltaX:0,e.deltaY=t.wheelDeltaY?t.wheelDeltaY:t.wheelDelta,this._notify(t)}},i.prototype._onTouchStart=function(t){var e=t.targetTouches?t.targetTouches[0]:t;this.touchStartX=e.pageX,this.touchStartY=e.pageY},i.prototype._onTouchMove=function(t){var e=this.options;e.preventTouch&&!t.target.classList.contains(e.unpreventTouchClass)&&t.preventDefault();var i=this._event,n=t.targetTouches?t.targetTouches[0]:t;i.deltaX=(n.pageX-this.touchStartX)*e.touchMultiplier,i.deltaY=(n.pageY-this.touchStartY)*e.touchMultiplier,this.touchStartX=n.pageX,this.touchStartY=n.pageY,this._notify(t)},i.prototype._onKeyDown=function(t){var e=this._event;switch(e.deltaX=e.deltaY=0,t.keyCode){case h.LEFT:case h.UP:e.deltaY=this.options.keyStep;break;case h.RIGHT:case h.DOWN:e.deltaY=-this.options.keyStep;break;default:return}this._notify(t)},i.prototype._bind=function(){r.hasWheelEvent&&this.el.addEventListener("wheel",this._onWheel),r.hasMouseWheelEvent&&this.el.addEventListener("mousewheel",this._onMouseWheel),r.hasTouch&&(this.el.addEventListener("touchstart",this._onTouchStart),this.el.addEventListener("touchmove",this._onTouchMove)),r.hasPointer&&r.hasTouchWin&&(this.bodyTouchAction=document.body.style.msTouchAction,document.body.style.msTouchAction="none",this.el.addEventListener("MSPointerDown",this._onTouchStart,!0),this.el.addEventListener("MSPointerMove",this._onTouchMove,!0)),r.hasKeyDown&&document.addEventListener("keydown",this._onKeyDown)},i.prototype._unbind=function(){r.hasWheelEvent&&this.el.removeEventListener("wheel",this._onWheel),r.hasMouseWheelEvent&&this.el.removeEventListener("mousewheel",this._onMouseWheel),r.hasTouch&&(this.el.removeEventListener("touchstart",this._onTouchStart),this.el.removeEventListener("touchmove",this._onTouchMove)),r.hasPointer&&r.hasTouchWin&&(document.body.style.msTouchAction=this.bodyTouchAction,this.el.removeEventListener("MSPointerDown",this._onTouchStart,!0),this.el.removeEventListener("MSPointerMove",this._onTouchMove,!0)),r.hasKeyDown&&document.removeEventListener("keydown",this._onKeyDown)},i.prototype.on=function(t,e){this._emitter.on(l,t,e);var i=this._emitter.e;i&&i[l]&&1===i[l].length&&this._bind()},i.prototype.off=function(t,e){this._emitter.off(l,t,e);var i=this._emitter.e;(!i[l]||i[l].length<=0)&&this._unbind()},i.prototype.reset=function(){var t=this._event;t.x=0,t.y=0},i.prototype.destroy=function(){this._emitter.off(),this._unbind()}},{"./clone":9,"./support":11,"bindall-standalone":2,lethargy:5,"object-assign":6,"tiny-emitter":8}],11:[function(t,e){"use strict";e.exports=function(){return{hasWheelEvent:"onwheel"in document,hasMouseWheelEvent:"onmousewheel"in document,hasTouch:"ontouchstart"in document,hasTouchWin:navigator.msMaxTouchPoints&&navigator.msMaxTouchPoints>1,hasPointer:!!window.navigator.msPointerEnabled,hasKeyDown:"onkeydown"in document,isFirefox:navigator.userAgent.indexOf("Firefox")>-1}}()},{}]},{},[1])(1)}); -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | slider manager 7 | 8 | 9 | 10 | 11 | 12 |

Slide 1

13 |

Slide 2

14 |

Slide 3

15 |

Slide 4

16 |

Slide 5

17 |

Slide 6

18 |

Slide 7

19 |

Slide 8

20 |

Slide 9

21 |

Slide 10

22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | import Manager from '../index.js' 2 | 3 | const slides = document.querySelectorAll('div') 4 | 5 | const slider = new Manager({ 6 | length: slides.length - 1, 7 | loop: true, 8 | direction: 'y', 9 | limitInertia: true, 10 | callback: (e) => { 11 | 12 | console.log(e) 13 | slider.animating = true 14 | 15 | slides.forEach((slide, i) => { 16 | 17 | slide.addEventListener('transitionend', () => { 18 | slider.animating = false 19 | }) 20 | 21 | slide.style.transform = i === e.current ? 'none' : i > e.current ? 'translateY(100%)' : 'translateY(-100%)' 22 | }) 23 | } 24 | }) 25 | 26 | slider.init() 27 | -------------------------------------------------------------------------------- /demo/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, 6 | body { 7 | height: 100%; 8 | margin: 0; 9 | } 10 | 11 | body { 12 | font-family: sans-serif; 13 | overflow: hidden; 14 | } 15 | 16 | div { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | right: 0; 21 | bottom: 0; 22 | color: white; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | will-change: transform; 27 | transition: transform 1s cubic-bezier(0.86, 0, 0.07, 1); 28 | } 29 | 30 | div:not(:first-of-type) { 31 | transform: translateY(100%); 32 | } 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import vs from 'virtual-scroll' 2 | import sniffer from 'sniffer' 3 | import { on, off } from 'dom-event' 4 | 5 | export default class Manager { 6 | 7 | constructor(opt = {}) { 8 | 9 | if(!opt.callback) 10 | console.error('You need to provide a callback function in the options') 11 | 12 | this.el = opt.el || document.body 13 | this.animating = false 14 | 15 | this.index = 0 16 | this.length = opt.length 17 | 18 | this.options = { 19 | direction: opt.direction || 'y', 20 | loop: opt.loop || false, 21 | delta: opt.delta || 1, 22 | callback: opt.callback, 23 | limitInertia: opt.limitInertia || false, 24 | passive: opt.passive || undefined 25 | } 26 | 27 | this.vs = null 28 | 29 | this.onScroll = this.onScroll.bind(this) 30 | this.onKeyDown = this.onKeyDown.bind(this) 31 | } 32 | 33 | init() { 34 | 35 | this.vs = new vs({ 36 | passive: this.options.passive, 37 | limitInertia: this.options.limitInertia 38 | }) 39 | 40 | this.vs.on(this.onScroll) 41 | 42 | if(sniffer.isDesktop) { 43 | on(document, 'keydown', this.onKeyDown) 44 | } 45 | } 46 | 47 | destroy() { 48 | 49 | this.vs.off(this.onScroll) 50 | this.vs.destroy() 51 | this.vs = null 52 | 53 | if(sniffer.isDesktop) { 54 | off(document, 'keydown', this.onKeyDown) 55 | } 56 | } 57 | 58 | getNext(delta) { 59 | 60 | const next = delta >= this.options.delta ? this.index + 1 : this.index - 1 61 | 62 | return this.checkLoop(next) 63 | } 64 | 65 | checkLoop(next) { 66 | 67 | return next < 0 ? this.options.loop ? this.length : 0 : next > this.length ? this.options.loop ? 0 : this.length : next 68 | } 69 | 70 | getEvent(index) { 71 | 72 | const prev = this.options.direction == 'y' ? 'up' : 'left' 73 | const next = this.options.direction == 'y' ? 'down' : 'right' 74 | 75 | let direction = index > this.index ? next : prev 76 | if (this.options.loop) { 77 | if (this.index == 0 && index == this.length) direction = prev 78 | if (this.index == this.length && index == 0) direction = next 79 | } 80 | 81 | return { 82 | current: index, 83 | previous: this.index, 84 | direction: direction 85 | } 86 | } 87 | 88 | onScroll(event) { 89 | 90 | const { deltaX, deltaY } = event 91 | const norm = this.options.direction == 'y' ? deltaY - (deltaY * 2) : deltaX - (deltaX * 2) 92 | 93 | if(this.animating || norm > -this.options.delta && norm < this.options.delta) return 94 | this.animating = true 95 | 96 | this.callback(norm) 97 | } 98 | 99 | onKeyDown(e) { 100 | 101 | const prev = this.options.direction == 'y' ? '38' : '37' 102 | const next = this.options.direction == 'y' ? '40' : '39' 103 | 104 | if(this.animating || e.keyCode != prev && e.keyCode != next) return 105 | this.animating = true 106 | 107 | this.callback(e.keyCode == next ? this.options.delta+1 : -(this.options.delta+1)) 108 | } 109 | 110 | goTo(index) { 111 | 112 | const check = this.checkLoop(index) 113 | const event = this.getEvent(check) 114 | 115 | this.index = check 116 | this.options.callback(event) 117 | } 118 | 119 | callback(delta) { 120 | 121 | const index = this.getNext(delta) 122 | const event = this.getEvent(index) 123 | 124 | this.index = index 125 | this.options.callback(event, delta) 126 | } 127 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slider-manager", 3 | "version": "2.0.3", 4 | "description": "", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Baptiste Briel", 9 | "email": "baptiste@sa-studio.fr", 10 | "url": "https://github.com/BaptisteBriel" 11 | }, 12 | "dependencies": { 13 | "dom-event": "npm-dom/dom-event", 14 | "sniffer": "github:watsondg/sniffer", 15 | "virtual-scroll": "^1.3.1" 16 | }, 17 | "devDependencies": { 18 | "babel-preset-es2015": "^6.6.0", 19 | "babelify": "^7.2.0", 20 | "budo": "^9.4.7", 21 | "uglifyjs": "^2.4.10", 22 | "watchify": "^3.7.0" 23 | }, 24 | "scripts": { 25 | "bundle": "browserify index.js -s build/manager -o build/manager.js", 26 | "uglify": "uglifyjs build/manager.js -cm > build/manager.min.js", 27 | "build": "npm run bundle && npm run uglify", 28 | "demo": "budo demo/index.js --dir demo --open" 29 | }, 30 | "browserify": { 31 | "transform": [ 32 | [ 33 | "babelify", 34 | { 35 | "presets": [ 36 | "es2015" 37 | ] 38 | } 39 | ] 40 | ] 41 | }, 42 | "keywords": [], 43 | "repository": { 44 | "type": "git", 45 | "url": "git://github.com/BaptisteBriel/slider-manager.git" 46 | }, 47 | "homepage": "https://github.com/BaptisteBriel/slider-manager", 48 | "bugs": { 49 | "url": "https://github.com/BaptisteBriel/slider-manager/issues" 50 | } 51 | } 52 | --------------------------------------------------------------------------------