UNPKG

scroll-swipe

Version:

a lightweight Events API for detecting scroll and touch events based on custom sensitivity

459 lines (351 loc) 10.6 kB
;(function(root, factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.ScrollSwipe = factory(); } }(this, function() { 'use strict'; var VERTICAL = 'VERTICAL'; var HORIZONTAL = 'HORIZONTAL'; var acceptedParams = new Set(['target', 'scrollSensitivity', 'touchSensitivity', 'scrollCb', 'touchCb', 'scrollPreventDefault', 'touchPreventDefault', 'addEventListenerOptions']); var noop = function noop() {}; if (typeof module !== 'undefined') { module.exports = ScrollSwipe; } function ScrollSwipe(opts) { var _this = this; Object.keys(opts).forEach(function (key) { if (acceptedParams.has(key)) { _this[key] = opts[key]; return; } throw new Error('unknown options for ScrollSwipe: ' + key); }); if (!opts.target) { throw new Error('must provide DOM target element to ScrollSwipe'); } // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters this.addEventListenerOptions = this.addEventListenerOptions || {}; if (!this.scrollSensitivity || this.scrollSensitivity < 0) { this.scrollSensitivity = 0; } if (!this.touchSensitivity || this.touchSensitivity < 0) { this.touchSensitivity = 0; } if (this.target.style || this.target.style.touchAction === '') { this.target.style.touchAction += 'manipulation'; } this.scrollPending = false; this.startTouchEvent = null; this.latestTouchEvent = null; this.latestTouch = null; this.startScrollEvent = null; this.latestScrollEvent = null; this.latestScroll = null; this.intent = 0; this.currentDirection = VERTICAL; this.touchArr = []; this.xArr = []; this.yArr = []; this.touchArrX = []; this.touchArrY = []; this.intentMap = { 'VERTICAL': { 0: 'UP', 1: 'DOWN' }, 'HORIZONTAL': { 0: 'LEFT', 1: 'RIGHT' } }; this.init(); return this; } ScrollSwipe.prototype.init = function init() { // only init if true if (this.scrollCb) { this.initScroll(); } if (this.touchCb) { this.initTouch(); } return this; }; ScrollSwipe.prototype.listen = function listen() { this.flush(); this.scrollPending = false; return this; }; ScrollSwipe.prototype.onWheel = function onWheel(e) { var _this2 = this; if (this.scrollPreventDefault && !this.addEventListenerOptions.passive) { e.preventDefault(); } if (this.scrollPending) { return; } this.startScrollEvent = e; var x = e.deltaX; var y = e.deltaY; this.addXScroll(x); this.addYScroll(y); this.scrollFulfilled(function (fulfilled, direction, intent) { if (!fulfilled) { return; } _this2.lockout(); _this2.latestScrollEvent = e; var result = _this2.buildResult({ startEvent: _this2.latestScrollEvent, lastEvent: _this2.latestScrollEvent, direction: direction, intent: intent }); _this2.scrollCb(result, _this2); _this2.undoLockout(); }); }; ScrollSwipe.prototype.initScroll = function initScroll() { this.newOnWheel = this.onWheel.bind(this); if (this.target && this.target.addEventListener) { this.target.addEventListener('wheel', this.newOnWheel, this.addEventListenerOptions); } return this; }; ScrollSwipe.prototype.touchMove = function touchMove(e) { if (this.touch && !this.addEventListenerOptions.passive) { e.preventDefault(); } var changedTouches = e.changedTouches[0]; var x = changedTouches.clientX; var y = changedTouches.clientY; this.startTouchEvent = e; this.addXTouch(x); this.addYTouch(y); }; ScrollSwipe.prototype.buildResult = function buildResult(_ref) { var startEvent = _ref.startEvent, lastEvent = _ref.lastEvent, direction = _ref.direction, intent = _ref.intent; return { startEvent: startEvent, lastEvent: lastEvent, direction: direction, intent: intent, scrollPending: this.scrollPending, mappedIntent: this.intentMap[direction][intent] }; }; ScrollSwipe.prototype.touchEnd = function touchEnd(e) { var _this3 = this; this.touchFulfilled(e, function (fulfilled, direction, intent) { if (!fulfilled) { return; } var result = _this3.buildResult({ startEvent: _this3.startTouchEvent, lastEvent: _this3.latestTouchEvent, direction: direction, intent: intent }); _this3.touchCb(result, _this3); }); }; ScrollSwipe.prototype.initTouch = function initTouch() { this.newTouchMove = this.touchMove.bind(this); this.newTouchEnd = this.touchEnd.bind(this); this.target.addEventListener('touchmove', this.newTouchMove, this.addEventListenerOptions); this.target.addEventListener('touchend', this.newTouchEnd, this.addEventListenerOptions); return this; }; //touch events ScrollSwipe.prototype.touchFulfilled = function touchFulfilled(e, cb) { if (!e) { throw new Error('must provide event to touchFulfilled'); } if (!cb) { throw new Error('must provide callback to touchFulfilled'); } var touchSensitivity = this.touchSensitivity, touchArrX = this.touchArrX, touchArrY = this.touchArrY; var bool = touchArrX.length > touchSensitivity && touchArrY.length > touchSensitivity; if (!bool) { return cb(false, null); } var changedTouches = e.changedTouches[0]; var xStart = touchArrX[0]; var yStart = touchArrY[0]; var xEnd = changedTouches.clientX; var yEnd = changedTouches.clientY; var xIntent = xStart < xEnd ? 0 : 1; var yIntent = yStart < yEnd ? 0 : 1; var direction = VERTICAL; //determine vertical or horizontal based on the greatest difference if (Math.abs(xStart - xEnd) > Math.abs(yStart - yEnd)) { direction = HORIZONTAL; } var intent = direction === VERTICAL ? yIntent : xIntent; swap.call(this, intent, direction); this.resetTouches(); this.scrollPending = true; this.latestTouchEvent = e; cb(this.scrollPending, this.currentDirection, this.currentIntent); return this; }; ScrollSwipe.prototype.getTouch = function getTouch(idx) { return this.touchArr[idx]; }; ScrollSwipe.prototype.addXTouch = function addTouch(touch) { if (this.pending()) { return this; } this.latestTouch = touch; this.touchArrX.push(touch); return this; }; ScrollSwipe.prototype.addYTouch = function addTouch(touch) { if (this.pending()) { return this; } this.latestTouch = touch; this.touchArrY.push(touch); return this; }; ScrollSwipe.prototype.resetTouches = function resetTouches() { this.touchArrX = []; this.touchArrY = []; return this; }; //wheel events ScrollSwipe.prototype.addXScroll = function addXScroll(s) { if (this.pending()) { return this; } this.latestScroll = s; this.xArr.push(s); return this; }; ScrollSwipe.prototype.addYScroll = function addYScroll(s) { if (this.pending()) { return this; } this.latestScroll = s; this.yArr.push(s); return this; }; ScrollSwipe.prototype.getDirection = function getDirection() { return this.currentDirection; }; ScrollSwipe.prototype.resetScroll = function resetScroll() { this.xArr = []; this.yArr = []; return this; }; ScrollSwipe.prototype.flush = function flush() { this.resetScroll(); this.resetTouches(); return this; }; ScrollSwipe.prototype.lockout = function lockout() { this.originalAddXTouch = this.addXTouch; this.originalAddYTouch = this.addYTouch; this.originalAddXScroll = this.addXScroll; this.originalAddYScroll = this.addYScroll; this.addXScroll = noop; this.addYScroll = noop; this.addXTouch = noop; this.addYTouch = noop; return this; }; ScrollSwipe.prototype.undoLockout = function undoLockout() { this.addXScroll = this.originalAddXScroll; this.addYScroll = this.originalAddYScroll; this.addXTouch = this.originalAddXTouch; this.addYTouch = this.originalAddYTouch; return this; }; ScrollSwipe.prototype.scrollFulfilled = function scrollFulfilled(cb) { if (!cb) { throw new Error('must provide callback to scrollFulfilled'); } var xArr = this.xArr, yArr = this.yArr, scrollSensitivity = this.scrollSensitivity; var bool = xArr.length > scrollSensitivity && yArr.length > scrollSensitivity; var _evalScrollDirection = this.evalScrollDirection(), direction = _evalScrollDirection.direction, intent = _evalScrollDirection.intent; if (bool) { swap.call(this, intent, direction); this.resetScroll(); this.scrollPending = true; } cb(this.scrollPending, this.currentDirection, this.currentIntent); return this; }; ScrollSwipe.prototype.evalScrollDirection = function evalScrollDirection() { var _getSums = this.getSums(), x = _getSums.x, y = _getSums.y, xIntent = _getSums.xIntent, yIntent = _getSums.yIntent; var direction = x > y ? HORIZONTAL : VERTICAL; var base = direction === VERTICAL ? yIntent : xIntent; var intent = 0; if (base > 0) { intent = 1; } return { direction: direction, intent: intent }; }; ScrollSwipe.prototype.getSums = function getSums() { var xArr = this.xArr, yArr = this.yArr; var xIntent = 0; var yIntent = 0; var x = xArr.reduce(function (result, curr) { xIntent = xIntent + curr; return result += Math.abs(curr); }, 0); var y = yArr.reduce(function (result, curr) { yIntent = yIntent + curr; return result += Math.abs(curr); }, 0); return { x: x, y: y, xIntent: xIntent, yIntent: yIntent }; }; ScrollSwipe.prototype.getScrollDirection = function getScrollDirection() { return this.currentDirection; }; ScrollSwipe.prototype.pending = function pending() { return this.scrollPending; }; ScrollSwipe.prototype.killScroll = function killScroll() { if (this.target && this.target.removeEventListener) { this.target.removeEventListener('wheel', this.newOnWheel, false); } return this; }; ScrollSwipe.prototype.killTouch = function killTouch() { if (this.target && this.target.removeEventListener) { this.target.removeEventListener('touchmove', this.newTouchMove, false); this.target.removeEventListener('touchend', this.newTouchEnd, false); } return this; }; ScrollSwipe.prototype.killAll = function teardown() { this.killScroll().killTouch().flush(); return this; }; function swap(intent, direction) { this.previousIntent = this.currentIntent; this.currentIntent = intent; this.previousDirection = this.currentDirection; this.currentDirection = direction; } return ScrollSwipe; }));