UNPKG

overscroll

Version:
1,900 lines (1,578 loc) 158 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.overscroll = factory()); }(this, (function () { 'use strict'; function compose() { for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } if (funcs.length === 0) { return function (arg) { return arg; }; } if (funcs.length === 1) { return funcs[0]; } var last = funcs[funcs.length - 1]; var rest = funcs.slice(0, -1); return function () { return rest.reduceRight(function (composed, f) { return f(composed); }, last.apply(undefined, arguments)); }; } /*! * isobject <https://github.com/jonschlinkert/isobject> * * Copyright (c) 2014-2017, Jon Schlinkert. * Released under the MIT License. */ 'use strict'; var isobject = function isObject(val) { return val != null && typeof val === 'object' && Array.isArray(val) === false; }; /*! * is-plain-object <https://github.com/jonschlinkert/is-plain-object> * * Copyright (c) 2014-2017, Jon Schlinkert. * Released under the MIT License. */ 'use strict'; function isObjectObject(o) { return isobject(o) === true && Object.prototype.toString.call(o) === '[object Object]'; } var isPlainObject = function isPlainObject(o) { var ctor,prot; if (isObjectObject(o) === false) return false; // If has modified constructor ctor = o.constructor; if (typeof ctor !== 'function') return false; // If has modified prototype prot = ctor.prototype; if (isObjectObject(prot) === false) return false; // If constructor does not have an Object-specific method if (prot.hasOwnProperty('isPrototypeOf') === false) { return false; } // Most likely a plain Object return true; }; var toString = Object.prototype.toString; var kindOf = function kindOf(val) { if (val === void 0) return 'undefined'; if (val === null) return 'null'; var type = typeof val; if (type === 'boolean') return 'boolean'; if (type === 'string') return 'string'; if (type === 'number') return 'number'; if (type === 'symbol') return 'symbol'; if (type === 'function') { return isGeneratorFn(val) ? 'generatorfunction' : 'function'; } if (isArray(val)) return 'array'; if (isBuffer(val)) return 'buffer'; if (isArguments(val)) return 'arguments'; if (isDate(val)) return 'date'; if (isError(val)) return 'error'; if (isRegexp(val)) return 'regexp'; switch (ctorName(val)) { case 'Symbol': return 'symbol'; case 'Promise': return 'promise'; // Set, Map, WeakSet, WeakMap case 'WeakMap': return 'weakmap'; case 'WeakSet': return 'weakset'; case 'Map': return 'map'; case 'Set': return 'set'; // 8-bit typed arrays case 'Int8Array': return 'int8array'; case 'Uint8Array': return 'uint8array'; case 'Uint8ClampedArray': return 'uint8clampedarray'; // 16-bit typed arrays case 'Int16Array': return 'int16array'; case 'Uint16Array': return 'uint16array'; // 32-bit typed arrays case 'Int32Array': return 'int32array'; case 'Uint32Array': return 'uint32array'; case 'Float32Array': return 'float32array'; case 'Float64Array': return 'float64array'; } if (isGeneratorObj(val)) { return 'generator'; } // Non-plain objects type = toString.call(val); switch (type) { case '[object Object]': return 'object'; // iterators case '[object Map Iterator]': return 'mapiterator'; case '[object Set Iterator]': return 'setiterator'; case '[object String Iterator]': return 'stringiterator'; case '[object Array Iterator]': return 'arrayiterator'; } // other return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); }; function ctorName(val) { return val.constructor ? val.constructor.name : null; } function isArray(val) { if (Array.isArray) return Array.isArray(val); return val instanceof Array; } function isError(val) { return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); } function isDate(val) { if (val instanceof Date) return true; return typeof val.toDateString === 'function' && typeof val.getDate === 'function' && typeof val.setDate === 'function'; } function isRegexp(val) { if (val instanceof RegExp) return true; return typeof val.flags === 'string' && typeof val.ignoreCase === 'boolean' && typeof val.multiline === 'boolean' && typeof val.global === 'boolean'; } function isGeneratorFn(name, val) { return ctorName(name) === 'GeneratorFunction'; } function isGeneratorObj(val) { return typeof val.throw === 'function' && typeof val.return === 'function' && typeof val.next === 'function'; } function isArguments(val) { try { if (typeof val.length === 'number' && typeof val.callee === 'function') { return true; } } catch (err) { if (err.message.indexOf('callee') !== -1) { return true; } } return false; } /** * If you need to support Safari 5-7 (8-10 yr-old browser), * take a look at https://github.com/feross/is-buffer */ function isBuffer(val) { if (val.constructor && typeof val.constructor.isBuffer === 'function') { return val.constructor.isBuffer(val); } return false; } var itis = {}; ['Array', 'Number', 'Function', 'RegExp', 'Boolean', 'Date', 'Error', 'Arguments', 'Null', 'String'].forEach(function (name) { itis[name] = function (v) { return kindOf(v) === name.toLowerCase(); }; }); /** * 2017-08-12 rainx * If a variable is not equal to null or undefined, I think it is defined. */ var isDefined = (function (variable) { return variable !== null && variable !== undefined; }); /** * 2017-08-12 rainx * Be contrary to isDefined. */ var isUndefined = (function (variable) { return variable === null || variable === undefined; }); function isItClass(Cls) { return function (obj) { return obj instanceof Cls; }; } var isWindow = function (win) { return win != null && win === win.window; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * nodeType 说明 http://www.w3school.com.cn/jsref/prop_node_nodetype.asp * * 1 Element 代表元素 * Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference * 2 Attr 代表属性 * Text, EntityReference * 3 Text 代表元素或属性中的文本内容 * None * 4 CDATASection 代表文档中的 CDATA 部分(不会由解析器解析的文本) * None * 5 EntityReference 代表实体引用 * Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * 6 Entity 代表实体 * Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * 7 ProcessingInstruction 代表处理指令 * None * 8 Comment 代表注释 * None * 9 Document 代表整个文档(DOM 树的根节点) * Element, ProcessingInstruction, Comment, DocumentType * 10 DocumentType 向为文档定义的实体提供接口 * None * 11 DocumentFragment 代表轻量级的 Document 对象,能够容纳文档的某个部分 * Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * 12 Notation 代表 DTD 中声明的符号 * None */ var isElement = (function (element) { return (typeof element === 'undefined' ? 'undefined' : _typeof(element)) === 'object' && element.nodeType === 1; }); var _typeof$1 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var isDocument = (function (element) { return (typeof element === 'undefined' ? 'undefined' : _typeof$1(element)) === 'object' && element.nodeType === 9; }); var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; function getLength(obj) { return isobject(obj) ? obj.length : undefined; } var isArrayLike = function (collection) { var length = getLength(collection); return itis.Number(length) && length >= 0 && length <= MAX_ARRAY_INDEX; }; // Number, Function, RegExp, Boolean, Date, Error, Arguments, // PlainObject, Object, Array, ArrayLike, Element var is = Object.assign(itis, { Undefined: isUndefined, Defined: isDefined, Element: isElement, Window: isWindow, Document: isDocument, PlainObject: isPlainObject, Object: isobject, ArrayLike: isArrayLike, isItClass: isItClass }); var actions = (function (scope) { var target = scope.target, win = scope.win, overscroll = scope.overscroll, isPageScroll = scope.isPageScroll, resetState = scope.resetState, getScroll = scope.getScroll, getFromRange = scope.getFromRange, getScrollSize = scope.getScrollSize, getClientSize = scope.getClientSize; function setScroll() { var scrollLeft = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll.scrollLeft; var scrollTop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : overscroll.scrollTop; // 取出滚动区域大小和可视范围计算滚动位置 var scrollTopRange = overscroll.scrollTopRange, scrollLeftRange = overscroll.scrollLeftRange; var newScrollTop = scrollTopRange(scrollTop); var newScrollLeft = scrollLeftRange(scrollLeft); // 重置dom状态 resetState(newScrollTop, newScrollLeft); // 缓存滚动条位置 overscroll.scrollLeft = newScrollLeft; overscroll.scrollTop = newScrollTop; if (isPageScroll) { win.scrollTo(overscroll.scrollLeft, overscroll.scrollTop); } else { target.scrollTop = overscroll.scrollTop; target.scrollLeft = overscroll.scrollLeft; } } function resetCache() { var _getScroll = getScroll(), top = _getScroll.top, left = _getScroll.left; var _getScrollSize = getScrollSize(), scrollWidth = _getScrollSize.width, scrollHeight = _getScrollSize.height; var _getClientSize = getClientSize(), clientWidth = _getClientSize.width, clientHeight = _getClientSize.height; overscroll.scrollTop = top; overscroll.scrollLeft = left; overscroll.scrollWidth = scrollWidth; overscroll.scrollHeight = scrollHeight; overscroll.clientWidth = clientWidth; overscroll.clientHeight = clientHeight; overscroll.scrollTopRange = getFromRange(0, scrollHeight - clientHeight); overscroll.scrollLeftRange = getFromRange(0, scrollWidth - clientWidth); } return { setScroll: setScroll, resetCache: resetCache }; }); var getOffset = function getOffset(_ref) { var win = _ref.win, body = _ref.body, html = _ref.html; return function (node) { var box = node.getBoundingClientRect(); // < ie8 不支持 win.pageXOffset, 则使用 html.scrollLeft return { left: box.left + (win.pageXOffset || html.scrollLeft) - (html.clientLeft || body.clientLeft || 0), top: box.top + (win.pageYOffset || html.scrollTop) - (html.clientTop || body.clientTop || 0) }; }; }; var getPosition = function getPosition(scope) { return function (node) { // if ( node.parentNode === scope.body ) { // return getOffset( scope )( node ); // } var parent = node.parentNode; var parentOffset = getOffset(scope)(parent); var offset = getOffset(scope)(node); return { left: offset.left - parentOffset.left, top: offset.top - parentOffset.top }; }; }; var getFromRange = (function (min, max) { return function (number) { return Math.min(max, Math.max(min, number)); }; }); var getClientSize = (function (_ref) { var html = _ref.html, target = _ref.target, isPageScroll = _ref.isPageScroll; return function () { var width = void 0; var height = void 0; if (isPageScroll) { width = html.clientWidth; height = html.clientHeight; // width = win.innerWidth || html.clientWidth; // height = win.innerHeight || html.clientHeight; } else { width = target.clientWidth; height = target.clientHeight; } return { width: width, height: height }; }; }); var getScrollSize = (function (_ref) { var body = _ref.body, html = _ref.html, target = _ref.target, isPageScroll = _ref.isPageScroll; return function () { var width = void 0; var height = void 0; if (isPageScroll) { width = Math.max(html.scrollWidth, body.scrollWidth); height = Math.max(html.scrollHeight, body.scrollHeight); } else { width = target.scrollWidth; height = target.scrollHeight; } return { width: width, height: height }; }; }); var asyncGenerator = function () { function AwaitValue(value) { this.value = value; } function AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } } if (typeof Symbol === "function" && Symbol.asyncIterator) { AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; } AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; return { wrap: function (fn) { return function () { return new AsyncGenerator(fn.apply(this, arguments)); }; }, await: function (value) { return new AwaitValue(value); } }; }(); var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var get$1 = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; var PIXEL_PATTERN = /margin|padding|width|height|max|min|offset/; var removePixel = { left: true, top: true }; var floatMap = { cssFloat: 1, styleFloat: 1, float: 1 }; function getComputedStyle(node) { return node.nodeType === 1 ? node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; } function getStyleValue(node, type, value) { type = type.toLowerCase(); if (value === 'auto') { if (type === 'height') { return node.offsetHeight; } if (type === 'width') { return node.offsetWidth; } } if (!(type in removePixel)) { removePixel[type] = PIXEL_PATTERN.test(type); } return removePixel[type] ? parseFloat(value) || 0 : value; } function get(node, name) { var length = arguments.length; var style = getComputedStyle(node); name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name; return length === 1 ? style : getStyleValue(node, name, style[name] || node.style[name]); } function set(node, name, value) { var length = arguments.length; name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name; if (length === 3) { if (typeof value === 'number' && PIXEL_PATTERN.test(name)) { value = value + 'px'; } node.style[name] = value; // Number return value; } if (is.PlainObject(name)) { Object.entries(name).forEach(function (_ref) { var _ref2 = slicedToArray(_ref, 2), key = _ref2[0], value = _ref2[1]; set(node, key, value); }); } // for ( const x in name ) { // if ( name.hasOwnProperty( x )) { // set( node, x, name[x]); // } // } return getComputedStyle(node); } var hyphenateRE = /([a-z\d])([A-Z])/g; function hyphenate(str) { return str.replace(hyphenateRE, '$1-$2').toLowerCase(); } // import camelCase from '../camelCase'; function firstCharToLowerCase(str) { var firstChar = str.charAt(0); if (/^[A-Z]$/.test(str.charAt(0))) { return firstChar.toLowerCase() + str.slice(1); } return str; } var setData = function setData(dom, name) { var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; if (dom.dataset) { dom.dataset[firstCharToLowerCase(name)] = value; } else { dom.setAttribute('data-' + hyphenate(name), value); } }; var getData = function getData(dom, name) { if (dom.dataset) { return dom.dataset[firstCharToLowerCase(name)]; } return dom.getAttribute('data-' + hyphenate(name)); }; var hasData = function hasData(dom, name) { if (dom.dataset) { return Object.keys(dom.dataset).includes(firstCharToLowerCase(name)); } return dom.hasAttribute('data-' + hyphenate(name)); }; var removeData = function removeData(dom, name) { if (dom.dataset) { delete dom.dataset[firstCharToLowerCase(name)]; } else { dom.removeAttribute('data-' + hyphenate(name)); } }; var domData = { setData: setData, getData: getData, hasData: hasData, removeData: removeData }; // 实际情况下 body documentElement 不会都设置 overflow: scroll // 推荐 html, body { height: 100% } body { overflow: auto; } function compose$1(funcA, funcB) { return function () { return funcA(funcB.apply(undefined, arguments)); }; } var scrollable = function scrollable(attr) { return function (dom) { return ['overlay', 'scroll', 'auto'].includes(get(dom, attr)); }; }; var hasScrollBar = (function (_ref) { var body = _ref.body, html = _ref.html, target = _ref.target, OVERSCROLLX = _ref.OVERSCROLLX, OVERSCROLLY = _ref.OVERSCROLLY; function hasScrollX(dom) { return dom.scrollWidth > dom.clientWidth && (hasData(dom, OVERSCROLLX) || scrollable('overflow-x')(dom)); } function hasScrollY(dom) { return dom.scrollHeight > dom.clientHeight && (hasData(dom, OVERSCROLLY) || scrollable('overflow-y')(dom)); } function scrollingElement() { var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : target; return dom === body ? html : dom; } function hasScroll(dom) { return { x: hasScrollX(dom), y: hasScrollY(dom) }; } return { hasScroll: compose$1(hasScroll, scrollingElement), hasScrollX: compose$1(hasScrollX, scrollingElement), hasScrollY: compose$1(hasScrollY, scrollingElement) }; }); // 检测 parentNode 元素包含 childNode 元素 function contains(parentNode, childNode) { // 标准浏览器支持compareDocumentPosition if (parentNode.compareDocumentPosition) { return !!(parentNode.compareDocumentPosition(childNode) & 16); } // IE支持contains else if (parentNode.contains) { return parentNode !== childNode && parentNode.contains(childNode); } return false; } function getDocument(dom) { return is.Element(dom) ? dom.ownerDocument : document; } function getWindow(dom) { var doc = is.Document(dom) ? dom : getDocument(dom); return doc.defaultView || window; } function parentNode(dom, condition) { var parent = dom.parentNode; if (is.Function(condition)) { return parent && condition(parent) === true ? parent : parent ? parentNode(parent, condition) : null; } return parent || getWindow(dom); } var domUtils = (function (scope) { var target = scope.target, NOBUBBLE = scope.NOBUBBLE; var hasScrollBarFuncs = hasScrollBar(scope); var hasScrollY = hasScrollBarFuncs.hasScrollY, hasScrollX = hasScrollBarFuncs.hasScrollX; function getNearestScrollable(dom) { return target === dom || hasData(dom, NOBUBBLE) || hasScrollY(dom) || hasScrollX(dom) ? dom : parentNode(dom, function (dom) { return target === dom || hasData(dom, NOBUBBLE) || hasScrollY(dom) || hasScrollX(dom); }); } return _extends({ domData: domData, contains: contains, getFromRange: getFromRange, getWindow: getWindow, getParent: parentNode, getDocument: getDocument, getNearestScrollable: getNearestScrollable, getOffset: getOffset(scope), getPosition: getPosition(scope), getClientSize: getClientSize(scope), getScrollSize: getScrollSize(scope) }, hasScrollBarFuncs); }); var domStates = (function (scope) { var target = scope.target, overscroll = scope.overscroll, _scope$domData = scope.domData, setData = _scope$domData.setData, hasData = _scope$domData.hasData, removeData = _scope$domData.removeData, hasScrollY = scope.hasScrollY, hasScrollX = scope.hasScrollX, getNearestScrollable = scope.getNearestScrollable, getParent = scope.getParent, NOBUBBLE = scope.NOBUBBLE; function handleState(states) { return function (target) { var state = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; if (!hasData(target, state)) { states.filter(function (elemState) { return state !== elemState && hasData(target, elemState); }).forEach(function (state) { return removeData(target, state); }); if (state && states.includes(state)) { setData(target, state); } } }; } // 滚动位置 var setPositionX = handleState(['scrollLeft', 'scrollRight', 'scrollX']); var setPositionY = handleState(['scrollTop', 'scrollBottom', 'scrollY']); // 动画状态 var setAnimationX = handleState(['scrollingX']); var setAnimationY = handleState(['scrollingY']); // 方向 var setDirectionX = handleState(['scrollingLeft', 'scrollingRight']); var setDirectionY = handleState(['scrollingUp', 'scrollingDown']); function animatingX() { setAnimationX(target, 'scrollingX'); } function animatingY() { setAnimationY(target, 'scrollingY'); } function scrollingUp() { setDirectionY(target, 'scrollingUp'); } function scrollingDown() { setDirectionY(target, 'scrollingDown'); } function scrollingLeft() { setDirectionX(target, 'scrollingLeft'); } function scrollingRight() { setDirectionX(target, 'scrollingRight'); } function scrollingStopX() { setDirectionX(target); setAnimationX(target); } function scrollingStopY() { setDirectionY(target); setAnimationY(target); } function isTop() { var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll; return dom.scrollTop < 1; } function isBottom() { var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll; return dom.scrollTop === dom.scrollHeight - dom.clientHeight; } function isLeft() { var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll; return dom.scrollLeft < 1; } function isRight() { var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll; return dom.scrollLeft === dom.scrollWidth - dom.clientWidth; } function scrollableTo(dom) { return { top: hasScrollY(dom) && !isTop(dom), left: hasScrollX(dom) && !isLeft(dom), right: hasScrollX(dom) && !isRight(dom), bottom: hasScrollY(dom) && !isBottom(dom) }; } function scrollable(dom) { var childrenState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { top: false, left: false, right: false, bottom: false }; var scrollableDom = getNearestScrollable(dom); var state = scrollableTo(scrollableDom); if (target === scrollableDom) { return { top: state.top && !childrenState.top, left: state.left && !childrenState.left, right: state.right && !childrenState.right, bottom: state.bottom && !childrenState.bottom }; } return scrollable(getParent(scrollableDom), { top: hasData(scrollableDom, NOBUBBLE) || state.top || childrenState.top, left: hasData(scrollableDom, NOBUBBLE) || state.left || childrenState.left, right: hasData(scrollableDom, NOBUBBLE) || state.right || childrenState.right, bottom: hasData(scrollableDom, NOBUBBLE) || state.bottom || childrenState.bottom }); } // 滚动状态设置完成后运行该函数 function resetState() { var scrollTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var scrollLeft = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; // 位置判断 ----------------- // 纵向状态设置 if (isTop()) { setPositionY(target, 'scrollTop'); } else if (isBottom()) { setPositionY(target, 'scrollBottom'); } else { setPositionY(target, 'scrollY'); } // 横向状态设置 if (isLeft()) { setPositionX(target, 'scrollLeft'); } else if (isRight()) { setPositionX(target, 'scrollRight'); } else { setPositionX(target, 'scrollX'); } // 滚动状态判断 ---------------- if (scrollTop === 0 || overscroll.scrollTop === scrollTop) { scrollingStopY(); } else if (overscroll.scrollTop > scrollTop) { animatingY(); scrollingDown(); } else if (overscroll.scrollTop < scrollTop) { animatingY(); scrollingUp(); } if (scrollLeft === 0 || overscroll.scrollLeft === scrollLeft) { scrollingStopX(); } else if (overscroll.scrollLeft > scrollLeft) { animatingX(); scrollingRight(); } else if (overscroll.scrollLeft < scrollLeft) { animatingX(); scrollingLeft(); } } return { isTop: isTop, isBottom: isBottom, isLeft: isLeft, isRight: isRight, animatingX: animatingX, animatingY: animatingY, scrollingUp: scrollingUp, scrollingDown: scrollingDown, scrollingLeft: scrollingLeft, scrollingRight: scrollingRight, scrollingStopX: scrollingStopX, scrollingStopY: scrollingStopY, resetState: resetState, scrollable: scrollable }; }); var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var hammer = createCommonjsModule(function (module) { /*! Hammer.JS - v2.0.7 - 2016-04-22 * http://hammerjs.github.io/ * * Copyright (c) 2016 Jorik Tangelder; * Licensed under the MIT license */ (function(window, document, exportName, undefined) { 'use strict'; var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; var TEST_ELEMENT = document.createElement('div'); var TYPE_FUNCTION = 'function'; var round = Math.round; var abs = Math.abs; var now = Date.now; /** * set a timeout with a given scope * @param {Function} fn * @param {Number} timeout * @param {Object} context * @returns {number} */ function setTimeoutContext(fn, timeout, context) { return setTimeout(bindFn(fn, context), timeout); } /** * if the argument is an array, we want to execute the fn on each entry * if it aint an array we don't want to do a thing. * this is used by all the methods that accept a single and array argument. * @param {*|Array} arg * @param {String} fn * @param {Object} [context] * @returns {Boolean} */ function invokeArrayArg(arg, fn, context) { if (Array.isArray(arg)) { each(arg, context[fn], context); return true; } return false; } /** * walk objects and arrays * @param {Object} obj * @param {Function} iterator * @param {Object} context */ function each(obj, iterator, context) { var i; if (!obj) { return; } if (obj.forEach) { obj.forEach(iterator, context); } else if (obj.length !== undefined) { i = 0; while (i < obj.length) { iterator.call(context, obj[i], i, obj); i++; } } else { for (i in obj) { obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); } } } /** * wrap a method with a deprecation warning and stack trace * @param {Function} method * @param {String} name * @param {String} message * @returns {Function} A new function wrapping the supplied method. */ function deprecate(method, name, message) { var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; return function() { var e = new Error('get-stack-trace'); var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') .replace(/^\s+at\s+/gm, '') .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; var log = window.console && (window.console.warn || window.console.log); if (log) { log.call(window.console, deprecationMessage, stack); } return method.apply(this, arguments); }; } /** * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} target * @param {...Object} objects_to_assign * @returns {Object} target */ var assign; if (typeof Object.assign !== 'function') { assign = function assign(target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; } else { assign = Object.assign; } /** * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} dest * @param {Object} src * @param {Boolean} [merge=false] * @returns {Object} dest */ var extend = deprecate(function extend(dest, src, merge) { var keys = Object.keys(src); var i = 0; while (i < keys.length) { if (!merge || (merge && dest[keys[i]] === undefined)) { dest[keys[i]] = src[keys[i]]; } i++; } return dest; }, 'extend', 'Use `assign`.'); /** * merge the values from src in the dest. * means that properties that exist in dest will not be overwritten by src * @param {Object} dest * @param {Object} src * @returns {Object} dest */ var merge = deprecate(function merge(dest, src) { return extend(dest, src, true); }, 'merge', 'Use `assign`.'); /** * simple class inheritance * @param {Function} child * @param {Function} base * @param {Object} [properties] */ function inherit(child, base, properties) { var baseP = base.prototype, childP; childP = child.prototype = Object.create(baseP); childP.constructor = child; childP._super = baseP; if (properties) { assign(childP, properties); } } /** * simple function bind * @param {Function} fn * @param {Object} context * @returns {Function} */ function bindFn(fn, context) { return function boundFn() { return fn.apply(context, arguments); }; } /** * let a boolean value also be a function that must return a boolean * this first item in args will be used as the context * @param {Boolean|Function} val * @param {Array} [args] * @returns {Boolean} */ function boolOrFn(val, args) { if (typeof val == TYPE_FUNCTION) { return val.apply(args ? args[0] || undefined : undefined, args); } return val; } /** * use the val2 when val1 is undefined * @param {*} val1 * @param {*} val2 * @returns {*} */ function ifUndefined(val1, val2) { return (val1 === undefined) ? val2 : val1; } /** * addEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function addEventListeners(target, types, handler) { each(splitStr(types), function(type) { target.addEventListener(type, handler, false); }); } /** * removeEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function removeEventListeners(target, types, handler) { each(splitStr(types), function(type) { target.removeEventListener(type, handler, false); }); } /** * find if a node is in the given parent * @method hasParent * @param {HTMLElement} node * @param {HTMLElement} parent * @return {Boolean} found */ function hasParent(node, parent) { while (node) { if (node == parent) { return true; } node = node.parentNode; } return false; } /** * small indexOf wrapper * @param {String} str * @param {String} find * @returns {Boolean} found */ function inStr(str, find) { return str.indexOf(find) > -1; } /** * split string on whitespace * @param {String} str * @returns {Array} words */ function splitStr(str) { return str.trim().split(/\s+/g); } /** * find if a array contains the object using indexOf or a simple polyFill * @param {Array} src * @param {String} find * @param {String} [findByKey] * @return {Boolean|Number} false when not found, or the index */ function inArray(src, find, findByKey) { if (src.indexOf && !findByKey) { return src.indexOf(find); } else { var i = 0; while (i < src.length) { if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { return i; } i++; } return -1; } } /** * convert array-like objects to real arrays * @param {Object} obj * @returns {Array} */ function toArray(obj) { return Array.prototype.slice.call(obj, 0); } /** * unique array with objects based on a key (like 'id') or just by the array's value * @param {Array} src [{id:1},{id:2},{id:1}] * @param {String} [key] * @param {Boolean} [sort=False] * @returns {Array} [{id:1},{id:2}] */ function uniqueArray(src, key, sort) { var results = []; var values = []; var i = 0; while (i < src.length) { var val = key ? src[i][key] : src[i]; if (inArray(values, val) < 0) { results.push(src[i]); } values[i] = val; i++; } if (sort) { if (!key) { results = results.sort(); } else { results = results.sort(function sortUniqueArray(a, b) { return a[key] > b[key]; }); } } return results; } /** * get the prefixed property * @param {Object} obj * @param {String} property * @returns {String|Undefined} prefixed */ function prefixed(obj, property) { var prefix, prop; var camelProp = property[0].toUpperCase() + property.slice(1); var i = 0; while (i < VENDOR_PREFIXES.length) { prefix = VENDOR_PREFIXES[i]; prop = (prefix) ? prefix + camelProp : property; if (prop in obj) { return prop; } i++; } return undefined; } /** * get a unique id * @returns {number} uniqueId */ var _uniqueId = 1; function uniqueId() { return _uniqueId++; } /** * get the window object of an element * @param {HTMLElement} element * @returns {DocumentView|Window} */ function getWindowForElement(element) { var doc = element.ownerDocument || element; return (doc.defaultView || doc.parentWindow || window); } var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; var SUPPORT_TOUCH = ('ontouchstart' in window); var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); var INPUT_TYPE_TOUCH = 'touch'; var INPUT_TYPE_PEN = 'pen'; var INPUT_TYPE_MOUSE = 'mouse'; var INPUT_TYPE_KINECT = 'kinect'; var COMPUTE_INTERVAL = 25; var INPUT_START = 1; var INPUT_MOVE = 2; var INPUT_END = 4; var INPUT_CANCEL = 8; var DIRECTION_NONE = 1; var DIRECTION_LEFT = 2; var DIRECTION_RIGHT = 4; var DIRECTION_UP = 8; var DIRECTION_DOWN = 16; var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; var PROPS_XY = ['x', 'y']; var PROPS_CLIENT_XY = ['clientX', 'clientY']; /** * create new input type manager * @param {Manager} manager * @param {Function} callback * @returns {Input} * @constructor */ function Input(manager, callback) { var self = this; this.manager = manager; this.callback = callback; this.element = manager.element; this.target = manager.options.inputTarget; // smaller wrapper around the handler, for the scope and the enabled state of the manager, // so when disabled the input events are completely bypassed. this.domHandler = function(ev) { if (boolOrFn(manager.options.enable, [manager])) { self.handler(ev); } }; this.init(); } Input.prototype = { /** * should handle the inputEvent data and trigger the callback * @virtual */ handler: function() { }, /** * bind the events */ init: function() { this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }, /** * unbind the events */ destroy: function() { this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); } }; /** * create new input type manager * called by the Manager constructor * @param {Hammer} manager * @returns {Input} */ function createInputInstance(manager) { var Type; var inputClass = manager.options.inputClass; if (inputClass) { Type = inputClass; } else if (SUPPORT_POINTER_EVENTS) { Type = PointerEventInput; } else if (SUPPORT_ONLY_TOUCH) { Type = TouchInput; } else if (!SUPPORT_TOUCH) { Type = MouseInput; } else { Type = TouchMouseInput; } return new (Type)(manager, inputHandler); } /** * handle input events * @param {Manager} manager * @param {String} eventType * @param {Object} input */ function inputHandler(manager, eventType, input) { var pointersLen = input.pointers.length; var changedPointersLen = input.changedPointers.length; var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); input.isFirst = !!isFirst; input.isFinal = !!isFinal; if (isFirst) { manager.session = {}; } // source event is the normalized value of the domEvents // like 'touchstart, mouseup, pointerdown' input.eventType = eventType; // compute scale, rotation etc computeInputData(manager, input); // emit secret event manager.emit('hammer.input', input); manager.recognize(input); manager.session.prevInput = input; } /** * extend the data with some usable properties like scale, rotate, velocity etc * @param {Object} manager * @param {Object} input */ function computeInputData(manager, input) { var session = manager.session; var pointers = input.pointers; var pointersLength = pointers.length; // store the first input to calculate the distance and direction if (!session.firstInput) { session.firstInput = simpleCloneInputData(input); } // to compute scale and rotation we need to store the multiple touches if (pointersLength > 1 && !session.firstMultiple) { session.firstMultiple = simpleCloneInputData(input); } else if (pointersLength === 1) { session.firstMultiple = false; } var firstInput = session.firstInput; var firstMultiple = session.firstMultiple; var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; var center = input.center = getCenter(pointers); input.timeStamp = now(); input.deltaTime = input.timeStamp - firstInput.timeStamp; input.angle = getAngle(offsetCenter, center); input.distance = getDistance(offsetCenter, center); computeDeltaXY(session, input); input.offsetDirection = getDirection(input.deltaX, input.deltaY); var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); input.overallVelocityX = overallVelocity.x; input.overallVelocityY = overallVelocity.y; input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); computeIntervalInputData(session, input); // find the correct target var target = manager.element; if (hasParent(input.srcEvent.target, target)) { target = input.srcEvent.target; } input.target = target; } function computeDeltaXY(session, input) { var center = input.center; var offset = session.offsetDelta || {}; var prevDelta = session.prevDelta || {}; var prevInput = session.prevInput || {}; if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { prevDelta = session.prevDelta = { x: prevInput.deltaX || 0, y: prevInput.deltaY || 0 }; offset = session.offsetDelta = { x: center.x, y: center.y }; } input.deltaX = prevDelta.x + (center.x - offset.x); input.deltaY = prevDelta.y + (center.y - offset.y); } /** * velocity is calculated every x ms * @param {Object} session * @param {Object} input */ function computeIntervalInputData(session, input) { var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction; if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { var deltaX = input.deltaX - last.deltaX; var deltaY = input.deltaY - last.deltaY; var v = getVelocity(deltaTime, deltaX, deltaY); velocityX = v.x; velocityY = v.y; velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; direction = getDirection(deltaX, deltaY); session.lastInterval = input; } else { // use latest velocity info if it doesn't overtake a minimum period velocity = last.velocity; velocityX = last.velocityX; velocityY = last.velocityY; direction = last.direction; } input.velocity = velocity; input.velocityX = velocityX; input.velocityY = velocityY; input.direction = direction; } /** * create a simple clone from the input used for storage of firstInput and firstMultiple * @param {Object} input * @returns {Object} clonedInputData */ function simpleCloneInputData(input) { // make a simple copy of the pointers because we will get a reference if we don't // we only need clientXY for the calculations var pointers = []; var i = 0; while (i < input.pointers.length) { pointers[i] = { clientX: round(input.pointers[i].clientX), clientY: round(input.pointers[i].clientY) }; i++; } return { timeStamp: now(), pointers: pointers, center: getCenter(pointers), deltaX: input.deltaX, deltaY: input.deltaY }; } /** * get the center of all the pointers * @param {Array} pointers * @return {Object} center contains `x` and `y` properties */ function getCenter(pointers) { var pointersLength = pointers.length; // no need to loop when only one touch if (pointersLength === 1) { return { x: round(pointers[0].clientX), y: round(pointers[0].clientY) }; } var x = 0, y = 0, i = 0; while (i < pointersLength) { x += pointers[i].clientX; y += pointers[i].clientY; i++; } return { x: round(x / pointersLength), y: round(y / pointersLength) }; } /** * calculate the velocity between two points. unit is in px per ms. * @param {Number} deltaTime * @param {Number} x * @param {Number} y * @return {Object} velocity `x` and `y` */ function getVelocity(deltaTime, x, y) { return { x: x / deltaTime || 0, y: y / deltaTime || 0 }; } /** * get the direction between two points * @param {Number} x * @param {Number} y * @return {Number} direction */ function getDirection(x, y) { if (x === y) { return DIRECTION_NONE; } if (abs(x) >= abs(y)) { return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; } return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; } /** * calculate the absolute distance between two points * @param {Object} p1 {x, y} * @param {Object} p2 {x, y} * @param {Array} [props] containing x and y key