/*! * better-normal-scroll v1.13.2 * (c) 2016-2018 ustbhuangyi * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.BScroll = factory()); }(this, (function () { 'use strict'; 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); } }; function eventMixin(BScroll) { BScroll.prototype.on = function (type, fn) { var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this; if (!this._events[type]) { this._events[type] = []; } this._events[type].push([fn, context]); }; BScroll.prototype.once = function (type, fn) { var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this; function magic() { this.off(type, magic); fn.apply(context, arguments); } // To expose the corresponding function method in order to execute the off method magic.fn = fn; this.on(type, magic); }; BScroll.prototype.off = function (type, fn) { var _events = this._events[type]; if (!_events) { return; } var count = _events.length; while (count--) { if (_events[count][0] === fn || _events[count][0] && _events[count][0].fn === fn) { _events[count][0] = undefined; } } }; BScroll.prototype.trigger = function (type) { var events = this._events[type]; if (!events) { return; } var len = events.length; var eventsCopy = [].concat(toConsumableArray(events)); for (var i = 0; i < len; i++) { var event = eventsCopy[i]; var _event = slicedToArray(event, 2), fn = _event[0], context = _event[1]; if (fn) { fn.apply(context, [].slice.call(arguments, 1)); } } }; } // ssr support var inBrowser = typeof window !== 'undefined'; var ua = inBrowser && navigator.userAgent.toLowerCase(); var isWeChatDevTools = ua && /wechatdevtools/.test(ua); var isAndroid = ua && ua.indexOf('android') > 0; function getNow() { return window.performance && window.performance.now ? window.performance.now() + window.performance.timing.navigationStart : +new Date(); } function extend(target) { for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { rest[_key - 1] = arguments[_key]; } for (var i = 0; i < rest.length; i++) { var source = rest[i]; for (var key in source) { target[key] = source[key]; } } return target; } function isUndef(v) { return v === undefined || v === null; } function getDistance(x, y) { return Math.sqrt(x * x + y * y); } var elementStyle = inBrowser && document.createElement('div').style; var vendor = function () { if (!inBrowser) { return false; } var transformNames = { webkit: 'webkitTransform', Moz: 'MozTransform', O: 'OTransform', ms: 'msTransform', standard: 'transform' }; for (var key in transformNames) { if (elementStyle[transformNames[key]] !== undefined) { return key; } } return false; }(); function prefixStyle(style) { if (vendor === false) { return false; } if (vendor === 'standard') { if (style === 'transitionEnd') { return 'transitionend'; } return style; } return vendor + style.charAt(0).toUpperCase() + style.substr(1); } function addEvent(el, type, fn, capture) { el.addEventListener(type, fn, { passive: false, capture: !!capture }); } function removeEvent(el, type, fn, capture) { el.removeEventListener(type, fn, { passive: false, capture: !!capture }); } function offset(el) { var left = 0; var top = 0; while (el) { left -= el.offsetLeft; top -= el.offsetTop; el = el.offsetParent; } return { left: left, top: top }; } function offsetToBody(el) { var rect = el.getBoundingClientRect(); return { left: -(rect.left + window.pageXOffset), top: -(rect.top + window.pageYOffset) }; } var cssVendor = vendor && vendor !== 'standard' ? '-' + vendor.toLowerCase() + '-' : ''; var transform = prefixStyle('transform'); var transition = prefixStyle('transition'); var hasPerspective = inBrowser && prefixStyle('perspective') in elementStyle; // fix issue #361 var hasTouch = inBrowser && ('ontouchstart' in window || isWeChatDevTools); var hasTransform = transform !== false; var hasTransition = inBrowser && transition in elementStyle; var style = { transform: transform, transition: transition, transitionTimingFunction: prefixStyle('transitionTimingFunction'), transitionDuration: prefixStyle('transitionDuration'), transitionDelay: prefixStyle('transitionDelay'), transformOrigin: prefixStyle('transformOrigin'), transitionEnd: prefixStyle('transitionEnd') }; var TOUCH_EVENT = 1; var MOUSE_EVENT = 2; var eventType = { touchstart: TOUCH_EVENT, touchmove: TOUCH_EVENT, touchend: TOUCH_EVENT, mousedown: MOUSE_EVENT, mousemove: MOUSE_EVENT, mouseup: MOUSE_EVENT }; function getRect(el) { if (el instanceof window.SVGElement) { var rect = el.getBoundingClientRect(); return { top: rect.top, left: rect.left, width: rect.width, height: rect.height }; } else { return { top: el.offsetTop, left: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight }; } } function preventDefaultException(el, exceptions) { for (var i in exceptions) { if (exceptions[i].test(el[i])) { return true; } } return false; } function tap(e, eventName) { var ev = document.createEvent('Event'); ev.initEvent(eventName, true, true); ev.pageX = e.pageX; ev.pageY = e.pageY; e.target.dispatchEvent(ev); } function click(e) { var event = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'click'; var eventSource = void 0; if (e.type === 'mouseup' || e.type === 'mousecancel') { eventSource = e; } else if (e.type === 'touchend' || e.type === 'touchcancel') { eventSource = e.changedTouches[0]; } var posSrc = {}; if (eventSource) { posSrc.screenX = eventSource.screenX || 0; posSrc.screenY = eventSource.screenY || 0; posSrc.clientX = eventSource.clientX || 0; posSrc.clientY = eventSource.clientY || 0; } var ev = void 0; var bubbles = true; var cancelable = true; if (typeof MouseEvent !== 'undefined') { try { ev = new MouseEvent(event, extend({ bubbles: bubbles, cancelable: cancelable }, posSrc)); } catch (e) { createEvent(); } } else { createEvent(); } function createEvent() { ev = document.createEvent('Event'); ev.initEvent(event, bubbles, cancelable); extend(ev, posSrc); } // forwardedTouchEvent set to true in case of the conflict with fastclick ev.forwardedTouchEvent = true; ev._constructed = true; e.target.dispatchEvent(ev); } function dblclick(e) { click(e, 'dblclick'); } function prepend(el, target) { if (target.firstChild) { before(el, target.firstChild); } else { target.appendChild(el); } } function before(el, target) { target.parentNode.insertBefore(el, target); } function removeChild(el, child) { el.removeChild(child); } var DEFAULT_OPTIONS = { startX: 0, startY: 0, scrollX: false, scrollY: true, freeScroll: false, directionLockThreshold: 5, eventPassthrough: '', click: false, tap: false, /** * support any side * bounce: { * top: true, * bottom: true, * left: true, * right: true * } */ bounce: true, bounceTime: 800, momentum: true, momentumLimitTime: 300, momentumLimitDistance: 15, swipeTime: 2500, swipeBounceTime: 500, deceleration: 0.0015, flickLimitTime: 200, flickLimitDistance: 100, resizePolling: 60, probeType: 0, preventDefault: true, preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ }, HWCompositing: true, useTransition: true, useTransform: true, bindToWrapper: false, disableMouse: hasTouch, disableTouch: !hasTouch, observeDOM: true, autoBlur: true, /** * for picker * wheel: { * selectedIndex: 0, * rotate: 25, * adjustTime: 400 * wheelWrapperClass: 'wheel-scroll', * wheelItemClass: 'wheel-item' * } */ wheel: false, /** * for slide * snap: { * loop: false, * el: domEl, * threshold: 0.1, * stepX: 100, * stepY: 100, * speed: 400, * easing: { * style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', * fn: function (t) { * return t * (2 - t) * } * } * listenFlick: true * } */ snap: false, /** * for scrollbar * scrollbar: { * fade: true, * interactive: false * } */ scrollbar: false, /** * for pull down and refresh * pullDownRefresh: { * threshold: 50, * stop: 20 * } */ pullDownRefresh: false, /** * for pull up and load * pullUpLoad: { * threshold: 50 * } */ pullUpLoad: false, /** * for mouse wheel * mouseWheel: { * speed: 20, * invert: false, * easeTime: 300 * } */ mouseWheel: false, stopPropagation: false, /** * for zoom * zoom: { * start: 1, * min: 1, * max: 4 * } */ zoom: false, /** * for infinity * infinity: { * render(item, div) { * }, * createTombstone() { * }, * fetch(count) { * } * } */ infinity: false, /** * for double click * dblclick: { * delay: 300 * } */ dblclick: false }; function initMixin(BScroll) { BScroll.prototype._init = function (el, options) { this._handleOptions(options); // init private custom events this._events = {}; this.x = 0; this.y = 0; this.directionX = 0; this.directionY = 0; this.setScale(1); this._addDOMEvents(); this._initExtFeatures(); this._watchTransition(); if (this.options.observeDOM) { this._initDOMObserver(); } if (this.options.autoBlur) { this._handleAutoBlur(); } this.refresh(); if (!this.options.snap) { this.scrollTo(this.options.startX, this.options.startY); } this.enable(); }; BScroll.prototype.setScale = function (scale) { this.lastScale = isUndef(this.scale) ? scale : this.scale; this.scale = scale; }; BScroll.prototype._handleOptions = function (options) { this.options = extend({}, DEFAULT_OPTIONS, options); this.translateZ = this.options.HWCompositing && hasPerspective ? ' translateZ(0)' : ''; this.options.useTransition = this.options.useTransition && hasTransition; this.options.useTransform = this.options.useTransform && hasTransform; this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault; // If you want eventPassthrough I have to lock one of the axes this.options.scrollX = this.options.eventPassthrough === 'horizontal' ? false : this.options.scrollX; this.options.scrollY = this.options.eventPassthrough === 'vertical' ? false : this.options.scrollY; // With eventPassthrough we also need lockDirection mechanism this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough; this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold; if (this.options.tap === true) { this.options.tap = 'tap'; } }; BScroll.prototype._addDOMEvents = function () { var eventOperation = addEvent; this._handleDOMEvents(eventOperation); }; BScroll.prototype._removeDOMEvents = function () { var eventOperation = removeEvent; this._handleDOMEvents(eventOperation); }; BScroll.prototype._handleDOMEvents = function (eventOperation) { var target = this.options.bindToWrapper ? this.wrapper : window; eventOperation(window, 'orientationchange', this); eventOperation(window, 'resize', this); if (this.options.click) { eventOperation(this.wrapper, 'click', this, true); } if (!this.options.disableMouse) { eventOperation(this.wrapper, 'mousedown', this); eventOperation(target, 'mousemove', this); eventOperation(target, 'mousecancel', this); eventOperation(target, 'mouseup', this); } if (hasTouch && !this.options.disableTouch) { eventOperation(this.wrapper, 'touchstart', this); eventOperation(target, 'touchmove', this); eventOperation(target, 'touchcancel', this); eventOperation(target, 'touchend', this); } eventOperation(this.scroller, style.transitionEnd, this); }; BScroll.prototype._initExtFeatures = function () { if (this.options.snap) { this._initSnap(); } if (this.options.scrollbar) { this._initScrollbar(); } if (this.options.pullUpLoad) { this._initPullUp(); } if (this.options.pullDownRefresh) { this._initPullDown(); } if (this.options.wheel) { this._initWheel(); } if (this.options.mouseWheel) { this._initMouseWheel(); } if (this.options.zoom) { this._initZoom(); } if (this.options.infinity) { this._initInfinite(); } }; BScroll.prototype._watchTransition = function () { if (typeof Object.defineProperty !== 'function') { return; } var me = this; var isInTransition = false; var key = this.useTransition ? 'isInTransition' : 'isAnimating'; Object.defineProperty(this, key, { get: function get() { return isInTransition; }, set: function set(newVal) { isInTransition = newVal; // fix issue #359 var el = me.scroller.children.length ? me.scroller.children : [me.scroller]; var pointerEvents = isInTransition && !me.pulling ? 'none' : 'auto'; for (var i = 0; i < el.length; i++) { el[i].style.pointerEvents = pointerEvents; } } }); }; BScroll.prototype._handleAutoBlur = function () { this.on('scrollStart', function () { var activeElement = document.activeElement; if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')) { activeElement.blur(); } }); }; BScroll.prototype._initDOMObserver = function () { var _this = this; if (typeof MutationObserver !== 'undefined') { var timer = void 0; var observer = new MutationObserver(function (mutations) { // don't do any refresh during the transition, or outside of the boundaries if (_this._shouldNotRefresh()) { return; } var immediateRefresh = false; var deferredRefresh = false; for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; if (mutation.type !== 'attributes') { immediateRefresh = true; break; } else { if (mutation.target !== _this.scroller) { deferredRefresh = true; break; } } } if (immediateRefresh) { _this.refresh(); } else if (deferredRefresh) { // attributes changes too often clearTimeout(timer); timer = setTimeout(function () { if (!_this._shouldNotRefresh()) { _this.refresh(); } }, 60); } }); var config = { attributes: true, childList: true, subtree: true }; observer.observe(this.scroller, config); this.on('destroy', function () { observer.disconnect(); }); } else { this._checkDOMUpdate(); } }; BScroll.prototype._shouldNotRefresh = function () { var outsideBoundaries = this.x > this.minScrollX || this.x < this.maxScrollX || this.y > this.minScrollY || this.y < this.maxScrollY; return this.isInTransition || this.stopFromTransition || outsideBoundaries; }; BScroll.prototype._checkDOMUpdate = function () { var scrollerRect = getRect(this.scroller); var oldWidth = scrollerRect.width; var oldHeight = scrollerRect.height; function check() { if (this.destroyed) { return; } scrollerRect = getRect(this.scroller); var newWidth = scrollerRect.width; var newHeight = scrollerRect.height; if (oldWidth !== newWidth || oldHeight !== newHeight) { this.refresh(); } oldWidth = newWidth; oldHeight = newHeight; next.call(this); } function next() { var _this2 = this; setTimeout(function () { check.call(_this2); }, 1000); } next.call(this); }; BScroll.prototype.handleEvent = function (e) { switch (e.type) { case 'touchstart': case 'mousedown': this._start(e); if (this.options.zoom && e.touches && e.touches.length > 1) { this._zoomStart(e); } break; case 'touchmove': case 'mousemove': if (this.options.zoom && e.touches && e.touches.length > 1) { this._zoom(e); } else { this._move(e); } break; case 'touchend': case 'mouseup': case 'touchcancel': case 'mousecancel': if (this.scaled) { this._zoomEnd(e); } else { this._end(e); } break; case 'orientationchange': case 'resize': this._resize(); break; case 'transitionend': case 'webkitTransitionEnd': case 'oTransitionEnd': case 'MSTransitionEnd': this._transitionEnd(e); break; case 'click': if (this.enabled && !e._constructed) { if (!preventDefaultException(e.target, this.options.preventDefaultException)) { e.preventDefault(); e.stopPropagation(); } } break; case 'wheel': case 'DOMMouseScroll': case 'mousewheel': this._onMouseWheel(e); break; } }; BScroll.prototype.refresh = function () { var isWrapperStatic = window.getComputedStyle(this.wrapper, null).position === 'static'; var wrapperRect = getRect(this.wrapper); this.wrapperWidth = wrapperRect.width; this.wrapperHeight = wrapperRect.height; var scrollerRect = getRect(this.scroller); this.scrollerWidth = Math.round(scrollerRect.width * this.scale); this.scrollerHeight = Math.round(scrollerRect.height * this.scale); this.relativeX = scrollerRect.left; this.relativeY = scrollerRect.top; if (isWrapperStatic) { this.relativeX -= wrapperRect.left; this.relativeY -= wrapperRect.top; } this.minScrollX = 0; this.minScrollY = 0; var wheel = this.options.wheel; if (wheel) { this.items = this.scroller.children; this.options.itemHeight = this.itemHeight = this.items.length ? this.scrollerHeight / this.items.length : 0; if (this.selectedIndex === undefined) { this.selectedIndex = wheel.selectedIndex || 0; } this.options.startY = -this.selectedIndex * this.itemHeight; this.maxScrollX = 0; this.maxScrollY = -this.itemHeight * (this.items.length - 1); } else { this.maxScrollX = this.wrapperWidth - this.scrollerWidth; if (!this.options.infinity) { this.maxScrollY = this.wrapperHeight - this.scrollerHeight; } if (this.maxScrollX < 0) { this.maxScrollX -= this.relativeX; this.minScrollX = -this.relativeX; } else if (this.scale > 1) { this.maxScrollX = this.maxScrollX / 2 - this.relativeX; this.minScrollX = this.maxScrollX; } if (this.maxScrollY < 0) { this.maxScrollY -= this.relativeY; this.minScrollY = -this.relativeY; } else if (this.scale > 1) { this.maxScrollY = this.maxScrollY / 2 - this.relativeY; this.minScrollY = this.maxScrollY; } } this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < this.minScrollX; this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < this.minScrollY; if (!this.hasHorizontalScroll) { this.maxScrollX = this.minScrollX; this.scrollerWidth = this.wrapperWidth; } if (!this.hasVerticalScroll) { this.maxScrollY = this.minScrollY; this.scrollerHeight = this.wrapperHeight; } this.endTime = 0; this.directionX = 0; this.directionY = 0; this.wrapperOffset = offset(this.wrapper); this.trigger('refresh'); !this.scaled && this.resetPosition(); }; BScroll.prototype.enable = function () { this.enabled = true; }; BScroll.prototype.disable = function () { this.enabled = false; }; } var ease = { // easeOutQuint swipe: { style: 'cubic-bezier(0.23, 1, 0.32, 1)', fn: function fn(t) { return 1 + --t * t * t * t * t; } }, // easeOutQuard swipeBounce: { style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', fn: function fn(t) { return t * (2 - t); } }, // easeOutQuart bounce: { style: 'cubic-bezier(0.165, 0.84, 0.44, 1)', fn: function fn(t) { return 1 - --t * t * t * t; } } }; function momentum(current, start, time, lowerMargin, upperMargin, wrapperSize, options) { var distance = current - start; var speed = Math.abs(distance) / time; var deceleration = options.deceleration, itemHeight = options.itemHeight, swipeBounceTime = options.swipeBounceTime, wheel = options.wheel, swipeTime = options.swipeTime; var duration = swipeTime; var rate = wheel ? 4 : 15; var destination = current + speed / deceleration * (distance < 0 ? -1 : 1); if (wheel && itemHeight) { destination = Math.round(destination / itemHeight) * itemHeight; } if (destination < lowerMargin) { destination = wrapperSize ? Math.max(lowerMargin - wrapperSize / 4, lowerMargin - wrapperSize / rate * speed) : lowerMargin; duration = swipeBounceTime; } else if (destination > upperMargin) { destination = wrapperSize ? Math.min(upperMargin + wrapperSize / 4, upperMargin + wrapperSize / rate * speed) : upperMargin; duration = swipeBounceTime; } return { destination: Math.round(destination), duration: duration }; } var DEFAULT_INTERVAL = 100 / 60; function noop() {} var requestAnimationFrame = function () { if (!inBrowser) { /* istanbul ignore if */ return noop; } return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || // if all else fails, use setTimeout function (callback) { return window.setTimeout(callback, (callback.interval || DEFAULT_INTERVAL) / 2); // make interval as precise as possible. }; }(); var cancelAnimationFrame = function () { if (!inBrowser) { /* istanbul ignore if */ return noop; } return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || function (id) { window.clearTimeout(id); }; }(); var DIRECTION_UP = 1; var DIRECTION_DOWN = -1; var DIRECTION_LEFT = 1; var DIRECTION_RIGHT = -1; var PROBE_DEBOUNCE = 1; var PROBE_REALTIME = 3; function warn(msg) { console.error('[BScroll warn]: ' + msg); } function assert(condition, msg) { if (!condition) { throw new Error('[BScroll] ' + msg); } } function coreMixin(BScroll) { BScroll.prototype._start = function (e) { var _eventType = eventType[e.type]; if (_eventType !== TOUCH_EVENT) { if (e.button !== 0) { return; } } if (!this.enabled || this.destroyed || this.initiated && this.initiated !== _eventType) { return; } this.initiated = _eventType; if (this.options.preventDefault && !preventDefaultException(e.target, this.options.preventDefaultException)) { e.preventDefault(); } if (this.options.stopPropagation) { e.stopPropagation(); } this.moved = false; this.distX = 0; this.distY = 0; this.directionX = 0; this.directionY = 0; this.movingDirectionX = 0; this.movingDirectionY = 0; this.directionLocked = 0; this._transitionTime(); this.startTime = getNow(); if (this.options.wheel) { this.target = e.target; } this.stop(); var point = e.touches ? e.touches[0] : e; this.startX = this.x; this.startY = this.y; this.absStartX = this.x; this.absStartY = this.y; this.pointX = point.pageX; this.pointY = point.pageY; this.trigger('beforeScrollStart'); }; BScroll.prototype._move = function (e) { if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) { return; } if (this.options.preventDefault) { e.preventDefault(); } if (this.options.stopPropagation) { e.stopPropagation(); } var point = e.touches ? e.touches[0] : e; var deltaX = point.pageX - this.pointX; var deltaY = point.pageY - this.pointY; this.pointX = point.pageX; this.pointY = point.pageY; this.distX += deltaX; this.distY += deltaY; var absDistX = Math.abs(this.distX); var absDistY = Math.abs(this.distY); var timestamp = getNow(); // We need to move at least momentumLimitDistance pixels for the scrolling to initiate if (timestamp - this.endTime > this.options.momentumLimitTime && absDistY < this.options.momentumLimitDistance && absDistX < this.options.momentumLimitDistance) { return; } // If you are scrolling in one direction lock the other if (!this.directionLocked && !this.options.freeScroll) { if (absDistX > absDistY + this.options.directionLockThreshold) { this.directionLocked = 'h'; // lock horizontally } else if (absDistY >= absDistX + this.options.directionLockThreshold) { this.directionLocked = 'v'; // lock vertically } else { this.directionLocked = 'n'; // no lock } } if (this.directionLocked === 'h') { if (this.options.eventPassthrough === 'vertical') { e.preventDefault(); } else if (this.options.eventPassthrough === 'horizontal') { this.initiated = false; return; } deltaY = 0; } else if (this.directionLocked === 'v') { if (this.options.eventPassthrough === 'horizontal') { e.preventDefault(); } else if (this.options.eventPassthrough === 'vertical') { this.initiated = false; return; } deltaX = 0; } deltaX = this.hasHorizontalScroll ? deltaX : 0; deltaY = this.hasVerticalScroll ? deltaY : 0; this.movingDirectionX = deltaX > 0 ? DIRECTION_RIGHT : deltaX < 0 ? DIRECTION_LEFT : 0; this.movingDirectionY = deltaY > 0 ? DIRECTION_DOWN : deltaY < 0 ? DIRECTION_UP : 0; var newX = this.x + deltaX; var newY = this.y + deltaY; var top = false; var bottom = false; var left = false; var right = false; // Slow down or stop if outside of the boundaries var bounce = this.options.bounce; if (bounce !== false) { top = bounce.top === undefined ? true : bounce.top; bottom = bounce.bottom === undefined ? true : bounce.bottom; left = bounce.left === undefined ? true : bounce.left; right = bounce.right === undefined ? true : bounce.right; } if (newX > this.minScrollX || newX < this.maxScrollX) { if (newX > this.minScrollX && left || newX < this.maxScrollX && right) { newX = this.x + deltaX / 3; } else { newX = newX > this.minScrollX ? this.minScrollX : this.maxScrollX; } } if (newY > this.minScrollY || newY < this.maxScrollY) { if (newY > this.minScrollY && top || newY < this.maxScrollY && bottom) { newY = this.y + deltaY / 3; } else { newY = newY > this.minScrollY ? this.minScrollY : this.maxScrollY; } } if (!this.moved) { this.moved = true; this.trigger('scrollStart'); } this._translate(newX, newY); if (timestamp - this.startTime > this.options.momentumLimitTime) { this.startTime = timestamp; this.startX = this.x; this.startY = this.y; if (this.options.probeType === PROBE_DEBOUNCE) { this.trigger('scroll', { x: this.x, y: this.y }); } } if (this.options.probeType > PROBE_DEBOUNCE) { this.trigger('scroll', { x: this.x, y: this.y }); } var scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft; var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop; var pX = this.pointX - scrollLeft; var pY = this.pointY - scrollTop; if (pX > document.documentElement.clientWidth - this.options.momentumLimitDistance || pX < this.options.momentumLimitDistance || pY < this.options.momentumLimitDistance || pY > document.documentElement.clientHeight - this.options.momentumLimitDistance) { this._end(e); } }; BScroll.prototype._end = function (e) { if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) { return; } this.initiated = false; if (this.options.preventDefault && !preventDefaultException(e.target, this.options.preventDefaultException)) { e.preventDefault(); } if (this.options.stopPropagation) { e.stopPropagation(); } this.trigger('touchEnd', { x: this.x, y: this.y }); this.isInTransition = false; // ensures that the last position is rounded var newX = Math.round(this.x); var newY = Math.round(this.y); var deltaX = newX - this.absStartX; var deltaY = newY - this.absStartY; this.directionX = deltaX > 0 ? DIRECTION_RIGHT : deltaX < 0 ? DIRECTION_LEFT : 0; this.directionY = deltaY > 0 ? DIRECTION_DOWN : deltaY < 0 ? DIRECTION_UP : 0; // if configure pull down refresh, check it first if (this.options.pullDownRefresh && this._checkPullDown()) { return; } // check if it is a click operation if (this._checkClick(e)) { this.trigger('scrollCancel'); return; } // reset if we are outside of the boundaries if (this.resetPosition(this.options.bounceTime, ease.bounce)) { return; } this._translate(newX, newY); this.endTime = getNow(); var duration = this.endTime - this.startTime; var absDistX = Math.abs(newX - this.startX); var absDistY = Math.abs(newY - this.startY); // flick if (this._events.flick && duration < this.options.flickLimitTime && absDistX < this.options.flickLimitDistance && absDistY < this.options.flickLimitDistance) { this.trigger('flick'); return; } var time = 0; // start momentum animation if needed if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) { var top = false; var bottom = false; var left = false; var right = false; var bounce = this.options.bounce; if (bounce !== false) { top = bounce.top === undefined ? true : bounce.top; bottom = bounce.bottom === undefined ? true : bounce.bottom; left = bounce.left === undefined ? true : bounce.left; right = bounce.right === undefined ? true : bounce.right; } var wrapperWidth = this.directionX === DIRECTION_RIGHT && left || this.directionX === DIRECTION_LEFT && right ? this.wrapperWidth : 0; var wrapperHeight = this.directionY === DIRECTION_DOWN && top || this.directionY === DIRECTION_UP && bottom ? this.wrapperHeight : 0; var momentumX = this.hasHorizontalScroll ? momentum(this.x, this.startX, duration, this.maxScrollX, this.minScrollX, wrapperWidth, this.options) : { destination: newX, duration: 0 }; var momentumY = this.hasVerticalScroll ? momentum(this.y, this.startY, duration, this.maxScrollY, this.minScrollY, wrapperHeight, this.options) : { destination: newY, duration: 0 }; newX = momentumX.destination; newY = momentumY.destination; time = Math.max(momentumX.duration, momentumY.duration); this.isInTransition = true; } else { if (this.options.wheel) { newY = Math.round(newY / this.itemHeight) * this.itemHeight; time = this.options.wheel.adjustTime || 400; } } var easing = ease.swipe; if (this.options.snap) { var snap = this._nearestSnap(newX, newY); this.currentPage = snap; time = this.options.snapSpeed || Math.max(Math.max(Math.min(Math.abs(newX - snap.x), 1000), Math.min(Math.abs(newY - snap.y), 1000)), 300); newX = snap.x; newY = snap.y; this.directionX = 0; this.directionY = 0; easing = this.options.snap.easing || ease.bounce; } if (newX !== this.x || newY !== this.y) { // change easing function when scroller goes out of the boundaries if (newX > this.minScrollX || newX < this.maxScrollX || newY > this.minScrollY || newY < this.maxScrollY) { easing = ease.swipeBounce; } this.scrollTo(newX, newY, time, easing); return; } if (this.options.wheel) { this.selectedIndex = Math.round(Math.abs(this.y / this.itemHeight)); } this.trigger('scrollEnd', { x: this.x, y: this.y }); }; BScroll.prototype._checkClick = function (e) { // when in the process of pulling down, it should not prevent click var preventClick = this.stopFromTransition && !this.pulling; this.stopFromTransition = false; // we scrolled less than 15 pixels if (!this.moved) { if (this.options.wheel) { if (this.target && this.target.classList.contains(this.options.wheel.wheelWrapperClass)) { var index = Math.abs(Math.round(this.y / this.itemHeight)); var _offset = Math.round((this.pointY + offsetToBody(this.wrapper).top - this.wrapperHeight / 2) / this.itemHeight); this.target = this.items[index + _offset]; } this.scrollToElement(this.target, this.options.wheel.adjustTime || 400, true, true, ease.swipe); return true; } else { if (!preventClick) { var _dblclick = this.options.dblclick; var dblclickTrigged = false; if (_dblclick && this.lastClickTime) { var _dblclick$delay = _dblclick.delay, delay = _dblclick$delay === undefined ? 300 : _dblclick$delay; if (getNow() - this.lastClickTime < delay) { dblclickTrigged = true; dblclick(e); } } if (this.options.tap) { tap(e, this.options.tap); } if (this.options.click && !preventDefaultException(e.target, this.options.preventDefaultException)) { click(e); } this.lastClickTime = dblclickTrigged ? null : getNow(); return true; } return false; } } return false; }; BScroll.prototype._resize = function () { var _this = this; if (!this.enabled) { return; } // fix a scroll problem under Android condition if (isAndroid) { this.wrapper.scrollTop = 0; } clearTimeout(this.resizeTimeout); this.resizeTimeout = setTimeout(function () { _this.refresh(); }, this.options.resizePolling); }; BScroll.prototype._startProbe = function () { cancelAnimationFrame(this.probeTimer); this.probeTimer = requestAnimationFrame(probe); var me = this; function probe() { var pos = me.getComputedPosition(); me.trigger('scroll', pos); if (!me.isInTransition) { me.trigger('scrollEnd', pos); return; } me.probeTimer = requestAnimationFrame(probe); } }; BScroll.prototype._transitionTime = function () { var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; this.scrollerStyle[style.transitionDuration] = time + 'ms'; if (this.options.wheel) { for (var i = 0; i < this.items.length; i++) { this.items[i].style[style.transitionDuration] = time + 'ms'; } } if (this.indicators) { for (var _i = 0; _i < this.indicators.length; _i++) { this.indicators[_i].transitionTime(time); } } }; BScroll.prototype._transitionTimingFunction = function (easing) { this.scrollerStyle[style.transitionTimingFunction] = easing; if (this.options.wheel) { for (var i = 0; i < this.items.length; i++) { this.items[i].style[style.transitionTimingFunction] = easing; } } if (this.indicators) { for (var _i2 = 0; _i2 < this.indicators.length; _i2++) { this.indicators[_i2].transitionTimingFunction(easing); } } }; BScroll.prototype._transitionEnd = function (e) { if (e.target !== this.scroller || !this.isInTransition) { return; } this._transitionTime(); var needReset = !this.pulling || this.movingDirectionY === DIRECTION_UP; if (needReset && !this.resetPosition(this.options.bounceTime, ease.bounce)) { this.isInTransition = false; if (this.options.probeType !== PROBE_REALTIME) { this.trigger('scrollEnd', { x: this.x, y: this.y }); } } }; BScroll.prototype._translate = function (x, y, scale) { assert(!isUndef(x) && !isUndef(y), 'Translate x or y is null or undefined.'); if (isUndef(scale)) { scale = this.scale; } if (this.options.useTransform) { this.scrollerStyle[style.transform] = 'translate(' + x + 'px,' + y + 'px) scale(' + scale + ')' + this.translateZ; } else { x = Math.round(x); y = Math.round(y); this.scrollerStyle.left = x + 'px'; this.scrollerStyle.top = y + 'px'; } if (this.options.wheel) { var _options$wheel$rotate = this.options.wheel.rotate, rotate = _options$wheel$rotate === undefined ? 25 : _options$wheel$rotate; for (var i = 0; i < this.items.length; i++) { var deg = rotate * (y / this.itemHeight + i); this.items[i].style[style.transform] = 'rotateX(' + deg + 'deg)'; } } this.x = x; this.y = y; this.setScale(scale); if (this.indicators) { for (var _i3 = 0; _i3 < this.indicators.length; _i3++) { this.indicators[_i3].updatePosition(); } } }; BScroll.prototype._animate = function (destX, destY, duration, easingFn) { var me = this; var startX = this.x; var startY = this.y; var startScale = this.lastScale; var destScale = this.scale; var startTime = getNow(); var destTime = startTime + duration; function step() { var now = getNow(); if (now >= destTime) { me.isAnimating = false; me._translate(destX, destY, destScale); me.trigger('scroll', { x: me.x, y: me.y }); if (!me.pulling && !me.resetPosition(me.options.bounceTime)) { me.trigger('scrollEnd', { x: me.x, y: me.y }); } return; } now = (now - startTime) / duration; var easing = easingFn(now); var newX = (destX - startX) * easing + startX; var newY = (destY - startY) * easing + startY; var newScale = (destScale - startScale) * easing + startScale; me._translate(newX, newY, newScale); if (me.isAnimating) { me.animateTimer = requestAnimationFrame(step); } if (me.options.probeType === PROBE_REALTIME) { me.trigger('scroll', { x: me.x, y: me.y }); } } this.isAnimating = true; cancelAnimationFrame(this.animateTimer); step(); }; BScroll.prototype.scrollBy = function (x, y) { var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ease.bounce; x = this.x + x; y = this.y + y; this.scrollTo(x, y, time, easing); }; BScroll.prototype.scrollTo = function (x, y) { var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ease.bounce; this.isInTransition = this.options.useTransition && time > 0 && (x !== this.x || y !== this.y); if (!time || this.options.useTransition) { this._transitionTimingFunction(easing.style); this._transitionTime(time); this._translate(x, y); if (time && this.options.probeType === PROBE_REALTIME) { this._startProbe(); } if (!time && (x !== this.x || y !== this.y)) { this.trigger('scroll', { x: x, y: y }); // force reflow to put everything in position this._reflow = document.body.offsetHeight; if (!this.resetPosition(this.options.bounceTime, ease.bounce)) { this.trigger('scrollEnd', { x: x, y: y }); } } if (this.options.wheel) { if (y > this.minScrollY) { this.selectedIndex = 0; } else if (y < this.maxScrollY) { this.selectedIndex = this.items.length - 1; } else { this.selectedIndex = Math.round(Math.abs(y / this.itemHeight)); } } } else { this._animate(x, y, time, easing.fn); } }; BScroll.prototype.scrollToElement = function (el, time, offsetX, offsetY, easing) { if (!el) { return; } el = el.nodeType ? el : this.scroller.querySelector(el); if (this.options.wheel && !el.classList.contains(this.options.wheel.wheelItemClass)) { return; } var pos = offset(el); pos.left -= this.wrapperOffset.left; pos.top -= this.wrapperOffset.top; // if offsetX/Y are true we center the element to the screen if (offsetX === true) { offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2); } if (offsetY === true) { offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2); } pos.left -= offsetX || 0; pos.top -= offsetY || 0; pos.left = pos.left > this.minScrollX ? this.minScrollX : pos.left < this.maxScrollX ? this.maxScrollX : pos.left; pos.top = pos.top > this.minScrollY ? this.minScrollY : pos.top < this.maxScrollY ? this.maxScrollY : pos.top; if (this.options.wheel) { pos.top = Math.round(pos.top / this.itemHeight) * this.itemHeight; } this.scrollTo(pos.left, pos.top, time, easing); }; BScroll.prototype.resetPosition = function () { var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var easeing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ease.bounce; var x = this.x; var roundX = Math.round(x); if (!this.hasHorizontalScroll || roundX > this.minScrollX) { x = this.minScrollX; } else if (roundX < this.maxScrollX) { x = this.maxScrollX; } var y = this.y; var roundY = Math.round(y); if (!this.hasVerticalScroll || roundY > this.minScrollY) { y = this.minScrollY; } else if (roundY < this.maxScrollY) { y = this.maxScrollY; } if (x === this.x && y === this.y) { return false; } this.scrollTo(x, y, time, easeing); return true; }; BScroll.prototype.getComputedPosition = function () { var matrix = window.getComputedStyle(this.scroller, null); var x = void 0; var y = void 0; if (this.options.useTransform) { matrix = matrix[style.transform].split(')')[0].split(', '); x = +(matrix[12] || matrix[4]); y = +(matrix[13] || matrix[5]); } else { x = +matrix.left.replace(/[^-\d.]/g, ''); y = +matrix.top.replace(/[^-\d.]/g, ''); } return { x: x, y: y }; }; BScroll.prototype.stop = function () { if (this.options.useTransition && this.isInTransition) { this.isInTransition = false; cancelAnimationFrame(this.probeTimer); var pos = this.getComputedPosition(); this._translate(pos.x, pos.y); if (this.options.wheel) { this.target = this.items[Math.round(-pos.y / this.itemHeight)]; } else { this.trigger('scrollEnd', { x: this.x, y: this.y }); } this.stopFromTransition = true; } else if (!this.options.useTransition && this.isAnimating) { this.isAnimating = false; cancelAnimationFrame(this.animateTimer); this.trigger('scrollEnd', { x: this.x, y: this.y }); this.stopFromTransition = true; } }; BScroll.prototype.destroy = function () { this.destroyed = true; this.trigger('destroy'); if (this.options.useTransition) { cancelAnimationFrame(this.probeTimer); } else { cancelAnimationFrame(this.animateTimer); } this._removeDOMEvents(); // remove custom events this._events = {}; }; } function snapMixin(BScroll) { BScroll.prototype._initSnap = function () { var _this = this; this.currentPage = {}; var snap = this.options.snap; if (snap.loop) { var children = this.scroller.children; if (children.length > 1) { prepend(children[children.length - 1].cloneNode(true), this.scroller); this.scroller.appendChild(children[1].cloneNode(true)); } else { // Loop does not make any sense if there is only one child. snap.loop = false; } } var el = snap.el; if (typeof el === 'string') { el = this.scroller.querySelectorAll(el); } this.on('refresh', function () { _this.pages = []; if (!_this.wrapperWidth || !_this.wrapperHeight || !_this.scrollerWidth || !_this.scrollerHeight) { return; } var stepX = snap.stepX || _this.wrapperWidth; var stepY = snap.stepY || _this.wrapperHeight; var x = 0; var y = void 0; var cx = void 0; var cy = void 0; var i = 0; var l = void 0; var m = 0; var n = void 0; var rect = void 0; if (!el) { cx = Math.round(stepX / 2); cy = Math.round(stepY / 2); while (x > -_this.scrollerWidth) { _this.pages[i] = []; l = 0; y = 0; while (y > -_this.scrollerHeight) { _this.pages[i][l] = { x: Math.max(x, _this.maxScrollX), y: Math.max(y, _this.maxScrollY), width: stepX, height: stepY, cx: x - cx, cy: y - cy }; y -= stepY; l++; } x -= stepX; i++; } } else { l = el.length; n = -1; for (; i < l; i++) { rect = getRect(el[i]); if (i === 0 || rect.left <= getRect(el[i - 1]).left) { m = 0; n++; } if (!_this.pages[m]) { _this.pages[m] = []; } x = Math.max(-rect.left, _this.maxScrollX); y = Math.max(-rect.top, _this.maxScrollY); cx = x - Math.round(rect.width / 2); cy = y - Math.round(rect.height / 2); _this.pages[m][n] = { x: x, y: y, width: rect.width, height: rect.height, cx: cx, cy: cy }; if (x > _this.maxScrollX) { m++; } } } _this._checkSnapLoop(); var initPageX = snap._loopX ? 1 : 0; var initPageY = snap._loopY ? 1 : 0; _this._goToPage(_this.currentPage.pageX || initPageX, _this.currentPage.pageY || initPageY, 0); // Update snap threshold if needed. var snapThreshold = snap.threshold; if (snapThreshold % 1 === 0) { _this.snapThresholdX = snapThreshold; _this.snapThresholdY = snapThreshold; } else { _this.snapThresholdX = Math.round(_this.pages[_this.currentPage.pageX][_this.currentPage.pageY].width * snapThreshold); _this.snapThresholdY = Math.round(_this.pages[_this.currentPage.pageX][_this.currentPage.pageY].height * snapThreshold); } }); this.on('scrollEnd', function () { if (snap.loop) { if (snap._loopX) { if (_this.currentPage.pageX === 0) { _this._goToPage(_this.pages.length - 2, _this.currentPage.pageY, 0); } if (_this.currentPage.pageX === _this.pages.length - 1) { _this._goToPage(1, _this.currentPage.pageY, 0); } } else { if (_this.currentPage.pageY === 0) { _this._goToPage(_this.currentPage.pageX, _this.pages[0].length - 2, 0); } if (_this.currentPage.pageY === _this.pages[0].length - 1) { _this._goToPage(_this.currentPage.pageX, 1, 0); } } } }); if (snap.listenFlick !== false) { this.on('flick', function () { var time = snap.speed || Math.max(Math.max(Math.min(Math.abs(_this.x - _this.startX), 1000), Math.min(Math.abs(_this.y - _this.startY), 1000)), 300); _this._goToPage(_this.currentPage.pageX + _this.directionX, _this.currentPage.pageY + _this.directionY, time); }); } this.on('destroy', function () { if (snap.loop) { var _children = _this.scroller.children; if (_children.length > 2) { removeChild(_this.scroller, _children[_children.length - 1]); removeChild(_this.scroller, _children[0]); } } }); }; BScroll.prototype._checkSnapLoop = function () { var snap = this.options.snap; if (!snap.loop || !this.pages || !this.pages.length) { return; } if (this.pages.length > 1) { snap._loopX = true; } if (this.pages[0] && this.pages[0].length > 1) { snap._loopY = true; } if (snap._loopX && snap._loopY) { warn('Loop does not support two direction at the same time.'); } }; BScroll.prototype._nearestSnap = function (x, y) { if (!this.pages.length) { return { x: 0, y: 0, pageX: 0, pageY: 0 }; } var i = 0; // Check if we exceeded the snap threshold if (Math.abs(x - this.absStartX) <= this.snapThresholdX && Math.abs(y - this.absStartY) <= this.snapThresholdY) { return this.currentPage; } if (x > this.minScrollX) { x = this.minScrollX; } else if (x < this.maxScrollX) { x = this.maxScrollX; } if (y > this.minScrollY) { y = this.minScrollY; } else if (y < this.maxScrollY) { y = this.maxScrollY; } var l = this.pages.length; for (; i < l; i++) { if (x >= this.pages[i][0].cx) { x = this.pages[i][0].x; break; } } l = this.pages[i].length; var m = 0; for (; m < l; m++) { if (y >= this.pages[0][m].cy) { y = this.pages[0][m].y; break; } } if (i === this.currentPage.pageX) { i += this.directionX; if (i < 0) { i = 0; } else if (i >= this.pages.length) { i = this.pages.length - 1; } x = this.pages[i][0].x; } if (m === this.currentPage.pageY) { m += this.directionY; if (m < 0) { m = 0; } else if (m >= this.pages[0].length) { m = this.pages[0].length - 1; } y = this.pages[0][m].y; } return { x: x, y: y, pageX: i, pageY: m }; }; BScroll.prototype._goToPage = function (x) { var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var time = arguments[2]; var easing = arguments[3]; var snap = this.options.snap; if (!snap || !this.pages || !this.pages.length) { return; } easing = easing || snap.easing || ease.bounce; if (x >= this.pages.length) { x = this.pages.length - 1; } else if (x < 0) { x = 0; } if (!this.pages[x]) { return; } if (y >= this.pages[x].length) { y = this.pages[x].length - 1; } else if (y < 0) { y = 0; } var posX = this.pages[x][y].x; var posY = this.pages[x][y].y; time = time === undefined ? snap.speed || Math.max(Math.max(Math.min(Math.abs(posX - this.x), 1000), Math.min(Math.abs(posY - this.y), 1000)), 300) : time; this.currentPage = { x: posX, y: posY, pageX: x, pageY: y }; this.scrollTo(posX, posY, time, easing); }; BScroll.prototype.goToPage = function (x, y, time, easing) { var snap = this.options.snap; if (!snap || !this.pages || !this.pages.length) { return; } if (snap.loop) { var len = void 0; if (snap._loopX) { len = this.pages.length - 2; if (x >= len) { x = len - 1; } else if (x < 0) { x = 0; } x += 1; } else { len = this.pages[0].length - 2; if (y >= len) { y = len - 1; } else if (y < 0) { y = 0; } y += 1; } } this._goToPage(x, y, time, easing); }; BScroll.prototype.next = function (time, easing) { var snap = this.options.snap; if (!snap) { return; } var x = this.currentPage.pageX; var y = this.currentPage.pageY; x++; if (x >= this.pages.length && this.hasVerticalScroll) { x = 0; y++; } this._goToPage(x, y, time, easing); }; BScroll.prototype.prev = function (time, easing) { var snap = this.options.snap; if (!snap) { return; } var x = this.currentPage.pageX; var y = this.currentPage.pageY; x--; if (x < 0 && this.hasVerticalScroll) { x = 0; y--; } this._goToPage(x, y, time, easing); }; BScroll.prototype.getCurrentPage = function () { var snap = this.options.snap; if (!snap) { return null; } if (snap.loop) { var currentPage = void 0; if (snap._loopX) { currentPage = extend({}, this.currentPage, { pageX: this.currentPage.pageX - 1 }); } else { currentPage = extend({}, this.currentPage, { pageY: this.currentPage.pageY - 1 }); } return currentPage; } return this.currentPage; }; } function wheelMixin(BScroll) { BScroll.prototype.wheelTo = function () { var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; if (this.options.wheel) { this.y = -index * this.itemHeight; this.scrollTo(0, this.y); } }; BScroll.prototype.getSelectedIndex = function () { return this.options.wheel && this.selectedIndex; }; BScroll.prototype._initWheel = function () { var wheel = this.options.wheel; if (!wheel.wheelWrapperClass) { wheel.wheelWrapperClass = 'wheel-scroll'; } if (!wheel.wheelItemClass) { wheel.wheelItemClass = 'wheel-item'; } if (wheel.selectedIndex === undefined) { wheel.selectedIndex = 0; warn('wheel option selectedIndex is required!'); } }; } var INDICATOR_MIN_LEN = 8; function scrollbarMixin(BScroll) { BScroll.prototype._initScrollbar = function () { var _this = this; var _options$scrollbar = this.options.scrollbar, _options$scrollbar$fa = _options$scrollbar.fade, fade = _options$scrollbar$fa === undefined ? true : _options$scrollbar$fa, _options$scrollbar$in = _options$scrollbar.interactive, interactive = _options$scrollbar$in === undefined ? false : _options$scrollbar$in; this.indicators = []; var indicator = void 0; if (this.options.scrollX) { indicator = { el: createScrollbar('horizontal'), direction: 'horizontal', fade: fade, interactive: interactive }; this._insertScrollBar(indicator.el); this.indicators.push(new Indicator(this, indicator)); } if (this.options.scrollY) { indicator = { el: createScrollbar('vertical'), direction: 'vertical', fade: fade, interactive: interactive }; this._insertScrollBar(indicator.el); this.indicators.push(new Indicator(this, indicator)); } this.on('refresh', function () { for (var i = 0; i < _this.indicators.length; i++) { _this.indicators[i].refresh(); } }); if (fade) { this.on('scrollEnd', function () { for (var i = 0; i < _this.indicators.length; i++) { _this.indicators[i].fade(); } }); this.on('scrollCancel', function () { for (var i = 0; i < _this.indicators.length; i++) { _this.indicators[i].fade(); } }); this.on('scrollStart', function () { for (var i = 0; i < _this.indicators.length; i++) { _this.indicators[i].fade(true); } }); this.on('beforeScrollStart', function () { for (var i = 0; i < _this.indicators.length; i++) { _this.indicators[i].fade(true, true); } }); } this.on('destroy', function () { _this._removeScrollBars(); }); }; BScroll.prototype._insertScrollBar = function (scrollbar) { this.wrapper.appendChild(scrollbar); }; BScroll.prototype._removeScrollBars = function () { for (var i = 0; i < this.indicators.length; i++) { this.indicators[i].destroy(); } }; } function createScrollbar(direction) { var scrollbar = document.createElement('div'); var indicator = document.createElement('div'); scrollbar.style.cssText = 'position:absolute;z-index:9999;pointerEvents:none'; indicator.style.cssText = 'box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px;'; indicator.className = 'bscroll-indicator'; if (direction === 'horizontal') { scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0'; indicator.style.height = '100%'; scrollbar.className = 'bscroll-horizontal-scrollbar'; } else { scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px'; indicator.style.width = '100%'; scrollbar.className = 'bscroll-vertical-scrollbar'; } scrollbar.style.cssText += ';overflow:hidden'; scrollbar.appendChild(indicator); return scrollbar; } function Indicator(scroller, options) { this.wrapper = options.el; this.wrapperStyle = this.wrapper.style; this.indicator = this.wrapper.children[0]; this.indicatorStyle = this.indicator.style; this.scroller = scroller; this.direction = options.direction; if (options.fade) { this.visible = 0; this.wrapperStyle.opacity = '0'; } else { this.visible = 1; } this.sizeRatioX = 1; this.sizeRatioY = 1; this.maxPosX = 0; this.maxPosY = 0; this.x = 0; this.y = 0; if (options.interactive) { this._addDOMEvents(); } } Indicator.prototype.handleEvent = function (e) { switch (e.type) { case 'touchstart': case 'mousedown': this._start(e); break; case 'touchmove': case 'mousemove': this._move(e); break; case 'touchend': case 'mouseup': case 'touchcancel': case 'mousecancel': this._end(e); break; } }; Indicator.prototype.refresh = function () { if (this._shouldShow()) { this.transitionTime(); this._calculate(); this.updatePosition(); } }; Indicator.prototype.fade = function (visible, hold) { var _this2 = this; if (hold && !this.visible) { return; } var time = visible ? 250 : 500; visible = visible ? '1' : '0'; this.wrapperStyle[style.transitionDuration] = time + 'ms'; clearTimeout(this.fadeTimeout); this.fadeTimeout = setTimeout(function () { _this2.wrapperStyle.opacity = visible; _this2.visible = +visible; }, 0); }; Indicator.prototype.updatePosition = function () { if (this.direction === 'vertical') { var y = Math.round(this.sizeRatioY * this.scroller.y); if (y < 0) { this.transitionTime(500); var height = Math.max(this.indicatorHeight + y * 3, INDICATOR_MIN_LEN); this.indicatorStyle.height = height + 'px'; y = 0; } else if (y > this.maxPosY) { this.transitionTime(500); var _height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, INDICATOR_MIN_LEN); this.indicatorStyle.height = _height + 'px'; y = this.maxPosY + this.indicatorHeight - _height; } else { this.indicatorStyle.height = this.indicatorHeight + 'px'; } this.y = y; if (this.scroller.options.useTransform) { this.indicatorStyle[style.transform] = 'translateY(' + y + 'px)' + this.scroller.translateZ; } else { this.indicatorStyle.top = y + 'px'; } } else { var x = Math.round(this.sizeRatioX * this.scroller.x); if (x < 0) { this.transitionTime(500); var width = Math.max(this.indicatorWidth + x * 3, INDICATOR_MIN_LEN); this.indicatorStyle.width = width + 'px'; x = 0; } else if (x > this.maxPosX) { this.transitionTime(500); var _width = Math.max(this.indicatorWidth - (x - this.maxPosX) * 3, INDICATOR_MIN_LEN); this.indicatorStyle.width = _width + 'px'; x = this.maxPosX + this.indicatorWidth - _width; } else { this.indicatorStyle.width = this.indicatorWidth + 'px'; } this.x = x; if (this.scroller.options.useTransform) { this.indicatorStyle[style.transform] = 'translateX(' + x + 'px)' + this.scroller.translateZ; } else { this.indicatorStyle.left = x + 'px'; } } }; Indicator.prototype.transitionTime = function () { var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; this.indicatorStyle[style.transitionDuration] = time + 'ms'; }; Indicator.prototype.transitionTimingFunction = function (easing) { this.indicatorStyle[style.transitionTimingFunction] = easing; }; Indicator.prototype.destroy = function () { this._removeDOMEvents(); this.wrapper.parentNode.removeChild(this.wrapper); }; Indicator.prototype._start = function (e) { var point = e.touches ? e.touches[0] : e; e.preventDefault(); e.stopPropagation(); this.transitionTime(); this.initiated = true; this.moved = false; this.lastPointX = point.pageX; this.lastPointY = point.pageY; this.startTime = getNow(); this._handleMoveEvents(addEvent); this.scroller.trigger('beforeScrollStart'); }; Indicator.prototype._move = function (e) { var point = e.touches ? e.touches[0] : e; e.preventDefault(); e.stopPropagation(); if (!this.moved) { this.scroller.trigger('scrollStart'); } this.moved = true; var deltaX = point.pageX - this.lastPointX; this.lastPointX = point.pageX; var deltaY = point.pageY - this.lastPointY; this.lastPointY = point.pageY; var newX = this.x + deltaX; var newY = this.y + deltaY; this._pos(newX, newY); }; Indicator.prototype._end = function (e) { if (!this.initiated) { return; } this.initiated = false; e.preventDefault(); e.stopPropagation(); this._handleMoveEvents(removeEvent); var snapOption = this.scroller.options.snap; if (snapOption) { var speed = snapOption.speed, _snapOption$easing = snapOption.easing, easing = _snapOption$easing === undefined ? ease.bounce : _snapOption$easing; var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y); var time = speed || Math.max(Math.max(Math.min(Math.abs(this.scroller.x - snap.x), 1000), Math.min(Math.abs(this.scroller.y - snap.y), 1000)), 300); if (this.scroller.x !== snap.x || this.scroller.y !== snap.y) { this.scroller.directionX = 0; this.scroller.directionY = 0; this.scroller.currentPage = snap; this.scroller.scrollTo(snap.x, snap.y, time, easing); } } if (this.moved) { this.scroller.trigger('scrollEnd', { x: this.scroller.x, y: this.scroller.y }); } }; Indicator.prototype._pos = function (x, y) { if (x < 0) { x = 0; } else if (x > this.maxPosX) { x = this.maxPosX; } if (y < 0) { y = 0; } else if (y > this.maxPosY) { y = this.maxPosY; } x = Math.round(x / this.sizeRatioX); y = Math.round(y / this.sizeRatioY); this.scroller.scrollTo(x, y); this.scroller.trigger('scroll', { x: this.scroller.x, y: this.scroller.y }); }; Indicator.prototype._shouldShow = function () { if (this.direction === 'vertical' && this.scroller.hasVerticalScroll || this.direction === 'horizontal' && this.scroller.hasHorizontalScroll) { this.wrapper.style.display = ''; return true; } this.wrapper.style.display = 'none'; return false; }; Indicator.prototype._calculate = function () { if (this.direction === 'vertical') { var wrapperHeight = this.wrapper.clientHeight; this.indicatorHeight = Math.max(Math.round(wrapperHeight * wrapperHeight / (this.scroller.scrollerHeight || wrapperHeight || 1)), INDICATOR_MIN_LEN); this.indicatorStyle.height = this.indicatorHeight + 'px'; this.maxPosY = wrapperHeight - this.indicatorHeight; this.sizeRatioY = this.maxPosY / this.scroller.maxScrollY; } else { var wrapperWidth = this.wrapper.clientWidth; this.indicatorWidth = Math.max(Math.round(wrapperWidth * wrapperWidth / (this.scroller.scrollerWidth || wrapperWidth || 1)), INDICATOR_MIN_LEN); this.indicatorStyle.width = this.indicatorWidth + 'px'; this.maxPosX = wrapperWidth - this.indicatorWidth; this.sizeRatioX = this.maxPosX / this.scroller.maxScrollX; } }; Indicator.prototype._addDOMEvents = function () { var eventOperation = addEvent; this._handleDOMEvents(eventOperation); }; Indicator.prototype._removeDOMEvents = function () { var eventOperation = removeEvent; this._handleDOMEvents(eventOperation); this._handleMoveEvents(eventOperation); }; Indicator.prototype._handleMoveEvents = function (eventOperation) { if (!this.scroller.options.disableTouch) { eventOperation(window, 'touchmove', this); } if (!this.scroller.options.disableMouse) { eventOperation(window, 'mousemove', this); } }; Indicator.prototype._handleDOMEvents = function (eventOperation) { if (!this.scroller.options.disableTouch) { eventOperation(this.indicator, 'touchstart', this); eventOperation(window, 'touchend', this); } if (!this.scroller.options.disableMouse) { eventOperation(this.indicator, 'mousedown', this); eventOperation(window, 'mouseup', this); } }; function pullDownMixin(BScroll) { BScroll.prototype._initPullDown = function () { // must watch scroll in real time this.options.probeType = PROBE_REALTIME; }; BScroll.prototype._checkPullDown = function () { var _options$pullDownRefr = this.options.pullDownRefresh, _options$pullDownRefr2 = _options$pullDownRefr.threshold, threshold = _options$pullDownRefr2 === undefined ? 90 : _options$pullDownRefr2, _options$pullDownRefr3 = _options$pullDownRefr.stop, stop = _options$pullDownRefr3 === undefined ? 40 : _options$pullDownRefr3; // check if a real pull down action if (this.directionY !== DIRECTION_DOWN || this.y < threshold) { return false; } if (!this.pulling) { this.pulling = true; this.trigger('pullingDown'); } this.scrollTo(this.x, stop, this.options.bounceTime, ease.bounce); return this.pulling; }; BScroll.prototype.finishPullDown = function () { this.pulling = false; this.resetPosition(this.options.bounceTime, ease.bounce); }; BScroll.prototype.openPullDown = function () { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.options.pullDownRefresh = config; this._initPullDown(); }; BScroll.prototype.closePullDown = function () { this.options.pullDownRefresh = false; }; } function pullUpMixin(BScroll) { BScroll.prototype._initPullUp = function () { // must watch scroll in real time this.options.probeType = PROBE_REALTIME; this.pullupWatching = false; this._watchPullUp(); }; BScroll.prototype._watchPullUp = function () { if (this.pullupWatching) { return; } this.pullupWatching = true; this.on('scroll', this._checkToEnd); }; BScroll.prototype._checkToEnd = function (pos) { var _this = this; var _options$pullUpLoad$t = this.options.pullUpLoad.threshold, threshold = _options$pullUpLoad$t === undefined ? 0 : _options$pullUpLoad$t; if (this.movingDirectionY === DIRECTION_UP && pos.y <= this.maxScrollY + threshold) { // reset pullupWatching status after scroll end. this.once('scrollEnd', function () { _this.pullupWatching = false; }); this.trigger('pullingUp'); this.off('scroll', this._checkToEnd); } }; BScroll.prototype.finishPullUp = function () { var _this2 = this; if (this.pullupWatching) { this.once('scrollEnd', function () { _this2._watchPullUp(); }); } else { this._watchPullUp(); } }; BScroll.prototype.openPullUp = function () { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.options.pullUpLoad = config; this._initPullUp(); }; BScroll.prototype.closePullUp = function () { this.options.pullUpLoad = false; if (!this.pullupWatching) { return; } this.pullupWatching = false; this.off('scroll', this._checkToEnd); }; } function mouseWheelMixin(BScroll) { BScroll.prototype._initMouseWheel = function () { var _this = this; this._handleMouseWheelEvent(addEvent); this.on('destroy', function () { clearTimeout(_this.mouseWheelTimer); clearTimeout(_this.mouseWheelEndTimer); _this._handleMouseWheelEvent(removeEvent); }); this.firstWheelOpreation = true; }; BScroll.prototype._handleMouseWheelEvent = function (eventOperation) { eventOperation(this.wrapper, 'wheel', this); eventOperation(this.wrapper, 'mousewheel', this); eventOperation(this.wrapper, 'DOMMouseScroll', this); }; BScroll.prototype._onMouseWheel = function (e) { var _this2 = this; if (!this.enabled) { return; } e.preventDefault(); if (this.options.stopPropagation) { e.stopPropagation(); } if (this.firstWheelOpreation) { this.trigger('scrollStart'); } this.firstWheelOpreation = false; var _options$mouseWheel = this.options.mouseWheel, _options$mouseWheel$s = _options$mouseWheel.speed, speed = _options$mouseWheel$s === undefined ? 20 : _options$mouseWheel$s, _options$mouseWheel$i = _options$mouseWheel.invert, invert = _options$mouseWheel$i === undefined ? false : _options$mouseWheel$i, _options$mouseWheel$e = _options$mouseWheel.easeTime, easeTime = _options$mouseWheel$e === undefined ? 300 : _options$mouseWheel$e; clearTimeout(this.mouseWheelTimer); this.mouseWheelTimer = setTimeout(function () { if (!_this2.options.snap && !easeTime) { _this2.trigger('scrollEnd', { x: _this2.x, y: _this2.y }); } _this2.firstWheelOpreation = true; }, 400); var wheelDeltaX = void 0; var wheelDeltaY = void 0; switch (true) { case 'deltaX' in e: if (e.deltaMode === 1) { wheelDeltaX = -e.deltaX * speed; wheelDeltaY = -e.deltaY * speed; } else { wheelDeltaX = -e.deltaX; wheelDeltaY = -e.deltaY; } break; case 'wheelDeltaX' in e: wheelDeltaX = e.wheelDeltaX / 120 * speed; wheelDeltaY = e.wheelDeltaY / 120 * speed; break; case 'wheelDelta' in e: wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * speed; break; case 'detail' in e: wheelDeltaX = wheelDeltaY = -e.detail / 3 * speed; break; default: return; } var direction = invert ? -1 : 1; wheelDeltaX *= direction; wheelDeltaY *= direction; if (!this.hasVerticalScroll) { wheelDeltaX = wheelDeltaY; wheelDeltaY = 0; } var newX = void 0; var newY = void 0; if (this.options.snap) { newX = this.currentPage.pageX; newY = this.currentPage.pageY; if (wheelDeltaX > 0) { newX--; } else if (wheelDeltaX < 0) { newX++; } if (wheelDeltaY > 0) { newY--; } else if (wheelDeltaY < 0) { newY++; } this._goToPage(newX, newY); return; } newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0); newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0); this.movingDirectionX = this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0; this.movingDirectionY = this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0; if (newX > this.minScrollX) { newX = this.minScrollX; } else if (newX < this.maxScrollX) { newX = this.maxScrollX; } if (newY > this.minScrollY) { newY = this.minScrollY; } else if (newY < this.maxScrollY) { newY = this.maxScrollY; } var needTriggerEnd = this.y === newY; this.scrollTo(newX, newY, easeTime, ease.swipe); this.trigger('scroll', { x: this.x, y: this.y }); clearTimeout(this.mouseWheelEndTimer); if (needTriggerEnd) { this.mouseWheelEndTimer = setTimeout(function () { _this2.trigger('scrollEnd', { x: _this2.x, y: _this2.y }); }, easeTime); } }; } function zoomMixin(BScroll) { BScroll.prototype._initZoom = function () { var _options$zoom = this.options.zoom, _options$zoom$start = _options$zoom.start, start = _options$zoom$start === undefined ? 1 : _options$zoom$start, _options$zoom$min = _options$zoom.min, min = _options$zoom$min === undefined ? 1 : _options$zoom$min, _options$zoom$max = _options$zoom.max, max = _options$zoom$max === undefined ? 4 : _options$zoom$max; this.scale = Math.min(Math.max(start, min), max); this.setScale(this.scale); this.scrollerStyle[style.transformOrigin] = '0 0'; }; BScroll.prototype._zoomTo = function (scale, originX, originY, startScale) { this.scaled = true; var lastScale = scale / (startScale || this.scale); this.setScale(scale); this.refresh(); var newX = Math.round(this.startX - (originX - this.relativeX) * (lastScale - 1)); var newY = Math.round(this.startY - (originY - this.relativeY) * (lastScale - 1)); if (newX > this.minScrollX) { newX = this.minScrollX; } else if (newX < this.maxScrollX) { newX = this.maxScrollX; } if (newY > this.minScrollY) { newY = this.minScrollY; } else if (newY < this.maxScrollY) { newY = this.maxScrollY; } if (this.x !== newX || this.y !== newY) { this.scrollTo(newX, newY, this.options.bounceTime); } this.scaled = false; }; BScroll.prototype.zoomTo = function (scale, x, y) { var _offsetToBody = offsetToBody(this.wrapper), left = _offsetToBody.left, top = _offsetToBody.top; var originX = x + left - this.x; var originY = y + top - this.y; this._zoomTo(scale, originX, originY); }; BScroll.prototype._zoomStart = function (e) { var firstFinger = e.touches[0]; var secondFinger = e.touches[1]; var deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX); var deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY); this.startDistance = getDistance(deltaX, deltaY); this.startScale = this.scale; var _offsetToBody2 = offsetToBody(this.wrapper), left = _offsetToBody2.left, top = _offsetToBody2.top; this.originX = Math.abs(firstFinger.pageX + secondFinger.pageX) / 2 + left - this.x; this.originY = Math.abs(firstFinger.pageY + secondFinger.pageY) / 2 + top - this.y; this.trigger('zoomStart'); }; BScroll.prototype._zoom = function (e) { if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) { return; } if (this.options.preventDefault) { e.preventDefault(); } if (this.options.stopPropagation) { e.stopPropagation(); } var firstFinger = e.touches[0]; var secondFinger = e.touches[1]; var deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX); var deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY); var distance = getDistance(deltaX, deltaY); var scale = distance / this.startDistance * this.startScale; this.scaled = true; var _options$zoom2 = this.options.zoom, _options$zoom2$min = _options$zoom2.min, min = _options$zoom2$min === undefined ? 1 : _options$zoom2$min, _options$zoom2$max = _options$zoom2.max, max = _options$zoom2$max === undefined ? 4 : _options$zoom2$max; if (scale < min) { scale = 0.5 * min * Math.pow(2.0, scale / min); } else if (scale > max) { scale = 2.0 * max * Math.pow(0.5, max / scale); } var lastScale = scale / this.startScale; var x = this.startX - (this.originX - this.relativeX) * (lastScale - 1); var y = this.startY - (this.originY - this.relativeY) * (lastScale - 1); this.setScale(scale); this.scrollTo(x, y, 0); }; BScroll.prototype._zoomEnd = function (e) { if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) { return; } if (this.options.preventDefault) { e.preventDefault(); } if (this.options.stopPropagation) { e.stopPropagation(); } this.isInTransition = false; this.isAnimating = false; this.initiated = 0; var _options$zoom3 = this.options.zoom, _options$zoom3$min = _options$zoom3.min, min = _options$zoom3$min === undefined ? 1 : _options$zoom3$min, _options$zoom3$max = _options$zoom3.max, max = _options$zoom3$max === undefined ? 4 : _options$zoom3$max; var scale = this.scale > max ? max : this.scale < min ? min : this.scale; this._zoomTo(scale, this.originX, this.originY, this.startScale); this.trigger('zoomEnd'); }; } // import { ease } from '../util/ease' // Number of items to instantiate beyond current view in the scroll direction. var RUNWAY_ITEMS = 30; // Number of items to instantiate beyond current view in the opposite direction. var RUNWAY_ITEMS_OPPOSITE = 10; // The animation interval (in ms) for fading in content from tombstones. var ANIMATION_DURATION_MS = 200; // The number of pixels of default additional length to allow scrolling to. var DEFAULT_SCROLL_RUNWAY = 2000; function infiniteMixin(BScroll) { BScroll.prototype._initInfinite = function () { this.options.probeType = 3; this.maxScrollY = -DEFAULT_SCROLL_RUNWAY; this.infiniteScroller = new InfiniteScroller(this, this.options.infinity); }; } function isTombstoneNode(node) { if (node && node.classList) { return node.classList.contains('tombstone'); } } function InfiniteScroller(scroller, options) { var _this = this; this.options = options; assert(typeof this.options.createTombstone === 'function', 'Infinite scroll need createTombstone Function to create tombstone'); assert(typeof this.options.fetch === 'function', 'Infinite scroll need fetch Function to fetch new data.'); assert(typeof this.options.render === 'function', 'Infinite scroll need render Function to render each item.'); this.firstAttachedItem = 0; this.lastAttachedItem = 0; this.anchorScrollTop = 0; this.anchorItem = { index: 0, offset: 0 }; this.tombstoneHeight = 0; this.tombstoneWidth = 0; this.tombstones = []; this.tombstonesAnimationHandlers = []; this.items = []; this.loadedItems = 0; this.requestInProgress = false; this.hasMore = true; this.scroller = scroller; this.wrapperEl = this.scroller.wrapper; this.scrollerEl = this.scroller.scroller; this.scroller.on('scroll', function () { _this.onScroll(); }); this.scroller.on('resize', function () { _this.onResize(); }); this.scroller.on('destroy', function () { _this.destroy(); }); // wait scroll core init this._onResizeHandler = setTimeout(function () { _this.onResize(); }); } InfiniteScroller.prototype.destroy = function () { var _this2 = this; // In extreme scene, destroy is triggered before _onResizeHandler clearTimeout(this._onResizeHandler); this.tombstonesAnimationHandlers.forEach(function (handler) { clearTimeout(handler); }); this.tombstonesAnimationHandlers = null; this.items.forEach(function (item) { if (item.node) { _this2.scrollerEl.removeChild(item.node); item.node = null; } }); this.scroller.infiniteScroller = null; this.scroller = null; this.wrapperEl = null; this.scrollerEl = null; this.items = null; this.tombstones = null; }; InfiniteScroller.prototype.onScroll = function () { var scrollTop = -this.scroller.y; var delta = scrollTop - this.anchorScrollTop; if (scrollTop === 0) { this.anchorItem = { index: 0, offset: 0 }; } else { this.anchorItem = this._calculateAnchoredItem(this.anchorItem, delta); } this.anchorScrollTop = scrollTop; var lastScreenItem = this._calculateAnchoredItem(this.anchorItem, this.scroller.wrapperHeight); var start = this.anchorItem.index; var end = lastScreenItem.index; if (delta < 0) { start -= RUNWAY_ITEMS; end += RUNWAY_ITEMS_OPPOSITE; } else { start -= RUNWAY_ITEMS_OPPOSITE; end += RUNWAY_ITEMS; } this.fill(start, end); this.maybeRequestContent(); }; InfiniteScroller.prototype.onResize = function () { var tombstone = this.options.createTombstone(); tombstone.style.position = 'absolute'; this.scrollerEl.appendChild(tombstone); tombstone.style.display = ''; this.tombstoneHeight = tombstone.offsetHeight; this.tombstoneWidth = tombstone.offsetWidth; this.scrollerEl.removeChild(tombstone); for (var i = 0; i < this.items.length; i++) { this.items[i].height = this.items[i].width = 0; } this.onScroll(); }; InfiniteScroller.prototype.fill = function (start, end) { this.firstAttachedItem = Math.max(0, start); if (!this.hasMore) { end = Math.min(end, this.items.length); } this.lastAttachedItem = end; this.attachContent(); }; InfiniteScroller.prototype.maybeRequestContent = function () { var _this3 = this; if (this.requestInProgress || !this.hasMore) { return; } var itemsNeeded = this.lastAttachedItem - this.loadedItems; if (itemsNeeded <= 0) { return; } this.requestInProgress = true; this.options.fetch(itemsNeeded).then(function (items) { _this3.requestInProgress = false; if (items) { _this3.addContent(items); } else { _this3.hasMore = false; var tombstoneLen = _this3._removeTombstones(); var curPos = 0; if (_this3.anchorItem.index <= _this3.items.length) { curPos = _this3._fixScrollPosition(); _this3._setupAnimations({}, curPos); _this3.scroller.resetPosition(_this3.scroller.options.bounceTime); } else { _this3.anchorItem.index -= tombstoneLen; curPos = _this3._fixScrollPosition(); _this3._setupAnimations({}, curPos); _this3.scroller.stop(); _this3.scroller.resetPosition(); _this3.onScroll(); } } }); }; InfiniteScroller.prototype.addContent = function (items) { for (var i = 0; i < items.length; i++) { if (this.items.length <= this.loadedItems) { this._addItem(); } this.items[this.loadedItems++].data = items[i]; } this.attachContent(); this.maybeRequestContent(); }; InfiniteScroller.prototype.attachContent = function () { var unusedNodes = this._collectUnusedNodes(); var tombstoneAnimations = this._createDOMNodes(unusedNodes); this._cleanupUnusedNodes(unusedNodes); this._cacheNodeSize(); var curPos = this._fixScrollPosition(); this._setupAnimations(tombstoneAnimations, curPos); }; InfiniteScroller.prototype.resetMore = function () { this.hasMore = true; }; InfiniteScroller.prototype._removeTombstones = function () { var markIndex = void 0; var tombstoneLen = 0; var itemLen = this.items.length; for (var i = 0; i < itemLen; i++) { var currentNode = this.items[i].node; var currentData = this.items[i].data; if ((!currentNode || isTombstoneNode(currentNode)) && !currentData) { // 0 should be excluded if (markIndex === void 0) { markIndex = i; } if (currentNode) { this.scrollerEl.removeChild(currentNode); } } } tombstoneLen = itemLen - markIndex; this.items.splice(markIndex); this.lastAttachedItem = Math.min(this.lastAttachedItem, this.items.length); return tombstoneLen; }; InfiniteScroller.prototype._collectUnusedNodes = function () { var unusedNodes = []; for (var i = 0; i < this.items.length; i++) { // Skip the items which should be visible. if (i === this.firstAttachedItem) { i = this.lastAttachedItem - 1; continue; } var currentNode = this.items[i].node; if (currentNode) { if (isTombstoneNode(currentNode)) { // Cache tombstones for reuse this.tombstones.push(currentNode); this.tombstones[this.tombstones.length - 1].style.display = 'none'; } else { unusedNodes.push(currentNode); } } this.items[i].node = null; } return unusedNodes; }; InfiniteScroller.prototype._createDOMNodes = function (unusedNodes) { var tombstoneAnimations = {}; for (var i = this.firstAttachedItem; i < this.lastAttachedItem; i++) { while (this.items.length <= i) { this._addItem(); } var currentNode = this.items[i].node; var currentData = this.items[i].data; if (currentNode) { if (isTombstoneNode(currentNode) && currentData) { currentNode.style.zIndex = 1; tombstoneAnimations[i] = [currentNode, this.items[i].top - this.anchorScrollTop]; this.items[i].node = null; } else { continue; } } var node = currentData ? this.options.render(currentData, unusedNodes.pop()) : this._getTombStone(); node.style.position = 'absolute'; this.items[i].top = -1; this.scrollerEl.appendChild(node); this.items[i].node = node; } return tombstoneAnimations; }; InfiniteScroller.prototype._cleanupUnusedNodes = function (unusedNodes) { while (unusedNodes.length) { this.scrollerEl.removeChild(unusedNodes.pop()); } }; InfiniteScroller.prototype._cacheNodeSize = function () { for (var i = this.firstAttachedItem; i < this.lastAttachedItem; i++) { var item = this.items[i]; // Only cache the height if we have the real contents, not a placeholder. if (item.data && !item.height) { var isTombstone = isTombstoneNode(item.node); item.height = isTombstone ? this.tombstoneHeight : item.node.offsetHeight; item.width = isTombstone ? this.tombstoneWidth : item.node.offsetWidth; } } }; InfiniteScroller.prototype._fixScrollPosition = function () { this.anchorScrollTop = 0; for (var _i = 0; _i < this.anchorItem.index; _i++) { this.anchorScrollTop += this.items[_i].height || this.tombstoneHeight; } this.anchorScrollTop += this.anchorItem.offset; // Position all nodes. var curPos = this.anchorScrollTop - this.anchorItem.offset; var i = this.anchorItem.index; while (i > this.firstAttachedItem) { curPos -= this.items[i - 1].height || this.tombstoneHeight; i--; } return curPos; }; InfiniteScroller.prototype._setupAnimations = function (tombstoneAnimations, curPos) { var _this4 = this; for (var i in tombstoneAnimations) { var animation = tombstoneAnimations[i]; this.items[i].node.style[style.transform] = 'translateY(' + (this.anchorScrollTop + animation[1]) + 'px) scale(' + this.tombstoneWidth / this.items[i].width + ', ' + this.tombstoneHeight / this.items[i].height + ')'; // Call offsetTop on the nodes to be animated to force them to apply current transforms. /* eslint-disable no-unused-expressions */ this.items[i].node.offsetTop; animation[0].offsetTop; this.items[i].node.style[style.transition] = cssVendor + 'transform ' + ANIMATION_DURATION_MS + 'ms'; } for (var _i2 = this.firstAttachedItem; _i2 < this.lastAttachedItem; _i2++) { var _animation = tombstoneAnimations[_i2]; if (_animation) { var tombstoneNode = _animation[0]; tombstoneNode.style[style.transition] = cssVendor + 'transform ' + ANIMATION_DURATION_MS + 'ms, opacity ' + ANIMATION_DURATION_MS + 'ms'; tombstoneNode.style[style.transform] = 'translateY(' + curPos + 'px) scale(' + this.items[_i2].width / this.tombstoneWidth + ', ' + this.items[_i2].height / this.tombstoneHeight + ')'; tombstoneNode.style.opacity = 0; } if (curPos !== this.items[_i2].top) { if (!_animation) { this.items[_i2].node.style[style.transition] = ''; } this.items[_i2].node.style[style.transform] = 'translateY(' + curPos + 'px)'; } this.items[_i2].top = curPos; curPos += this.items[_i2].height || this.tombstoneHeight; } this.scroller.maxScrollY = -(curPos - this.scroller.wrapperHeight + (this.hasMore ? DEFAULT_SCROLL_RUNWAY : 0)); var tombstoneAnimationsHandler = setTimeout(function () { for (var _i3 in tombstoneAnimations) { var _animation2 = tombstoneAnimations[_i3]; _animation2[0].style.display = 'none'; // Tombstone can be recycled now. _this4.tombstones.push(_animation2[0]); } }, ANIMATION_DURATION_MS); this.tombstonesAnimationHandlers.push(tombstoneAnimationsHandler); }; InfiniteScroller.prototype._getTombStone = function () { var tombstone = this.tombstones.pop(); if (tombstone) { tombstone.style.display = ''; tombstone.style.opacity = 1; tombstone.style[style.transform] = ''; tombstone.style[style.transition] = ''; return tombstone; } return this.options.createTombstone(); }; InfiniteScroller.prototype._addItem = function () { this.items.push({ data: null, node: null, height: 0, width: 0, top: 0 }); }; InfiniteScroller.prototype._calculateAnchoredItem = function (initialAnchor, delta) { if (delta === 0) { return initialAnchor; } var i = initialAnchor.index; var tombstones = 0; delta += initialAnchor.offset; if (delta < 0) { while (delta < 0 && i > 0 && this.items[i - 1].height) { delta += this.items[i - 1].height; i--; } tombstones = Math.max(-i, Math.ceil(Math.min(delta, 0) / this.tombstoneHeight)); } else { while (delta > 0 && i < this.items.length && this.items[i].height && this.items[i].height < delta) { delta -= this.items[i].height; i++; } if (i >= this.items.length || !this.items[i].height) { tombstones = Math.floor(Math.max(delta, 0) / this.tombstoneHeight); } } i += tombstones; delta -= tombstones * this.tombstoneHeight; return { index: i, offset: delta }; }; function BScroll(el, options) { this.wrapper = typeof el === 'string' ? document.querySelector(el) : el; if (!this.wrapper) { warn('Can not resolve the wrapper DOM.'); } this.scroller = this.wrapper.children[0]; if (!this.scroller) { warn('The wrapper need at least one child element to be scroller.'); } // cache style for better performance this.scrollerStyle = this.scroller.style; this._init(el, options); } initMixin(BScroll); coreMixin(BScroll); eventMixin(BScroll); snapMixin(BScroll); wheelMixin(BScroll); scrollbarMixin(BScroll); pullDownMixin(BScroll); pullUpMixin(BScroll); mouseWheelMixin(BScroll); zoomMixin(BScroll); infiniteMixin(BScroll); BScroll.Version = '1.13.2'; return BScroll; })));