赞
踩
移动端页面上下滑动一般都使用第三方插件来实现,这里我们就使用BetterScroll插件,对他进行一层封装,应用到项目中的上下滑动。
BetterScroll源码在githud上可以获取,同时还有使用文档。目前移动端上下滑动使用这个插件算是主流。
当我们在移动端滑动页面时候,第一页滑动到底部后,再次滑动就会出现加载下一页的数据。这个时候就是通过上拉加载更多插件功能触发组件发送网络请求下一页数据。
bscroll.js文件可以到GitHub上该项目选择Tags为1.15版本,在该版本源代码查看dist文件夹下找到,下面直接贴出bscroll.js全部源代码。
/*!
* 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;
})));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.content {
height: 300px;
background-color: antiquewhite;
overflow: hidden;
}
</style>
</head>
<body>
<div>
<div class="content">
<ul>
<button class="btn">按钮</button>
<li>列表数据1</li>
<li>列表数据2</li>
<li>列表数据3</li>
<li>列表数据4</li>
<li>列表数据5</li>
<li>列表数据6</li>
<li>列表数据7</li>
<li>列表数据8</li>
<li>列表数据9</li>
<li>列表数据10</li>
<li>列表数据11</li>
<li>列表数据12</li>
<li>列表数据13</li>
<li>列表数据14</li>
<li>列表数据15</li>
<li>列表数据16</li>
<li>列表数据17</li>
<li>列表数据18</li>
<li>列表数据19</li>
<li>列表数据20</li>
<li>列表数据21</li>
<li>列表数据22</li>
<li>列表数据23</li>
<li>列表数据24</li>
<li>列表数据25</li>
<li>列表数据26</li>
<li>列表数据27</li>
<li>列表数据28</li>
<li>列表数据29</li>
<li>列表数据30</li>
<li>列表数据31</li>
<li>列表数据32</li>
<li>列表数据33</li>
<li>列表数据34</li>
<li>列表数据35</li>
<li>列表数据36</li>
<li>列表数据37</li>
<li>列表数据38</li>
<li>列表数据39</li>
<li>列表数据40</li>
<li>列表数据41</li>
<li>列表数据42</li>
<li>列表数据43</li>
<li>列表数据44</li>
<li>列表数据45</li>
<li>列表数据46</li>
<li>列表数据47</li>
<li>列表数据48</li>
<li>列表数据49</li>
<li>列表数据50</li>
<li>列表数据51</li>
<li>列表数据52</li>
<li>列表数据53</li>
<li>列表数据54</li>
<li>列表数据55</li>
<li>列表数据56</li>
<li>列表数据57</li>
<li>列表数据58</li>
<li>列表数据59</li>
<li>列表数据60</li>
<li>列表数据61</li>
<li>列表数据62</li>
<li>列表数据63</li>
<li>列表数据64</li>
<li>列表数据65</li>
<li>列表数据66</li>
<li>列表数据67</li>
<li>列表数据68</li>
<li>列表数据69</li>
<li>列表数据70</li>
<li>列表数据71</li>
<li>列表数据72</li>
<li>列表数据73</li>
<li>列表数据74</li>
<li>列表数据75</li>
<li>列表数据76</li>
<li>列表数据77</li>
<li>列表数据78</li>
<li>列表数据79</li>
<li>列表数据80</li>
<li>列表数据81</li>
<li>列表数据82</li>
<li>列表数据83</li>
<li>列表数据84</li>
<li>列表数据85</li>
<li>列表数据86</li>
<li>列表数据87</li>
<li>列表数据88</li>
<li>列表数据89</li>
<li>列表数据90</li>
<li>列表数据91</li>
<li>列表数据92</li>
<li>列表数据93</li>
<li>列表数据94</li>
<li>列表数据95</li>
<li>列表数据96</li>
<li>列表数据97</li>
<li>列表数据98</li>
<li>列表数据99</li>
<li>列表数据100</li>
</ul>
</div>
</div>
<!-- 1.引入bscroll文件 -->
<script src="./bscroll.js"></script>
<script>
/* 2.创建bscroll对象,并传递两个参数。
- 第一个参数:传入需要滚动的标签,而且这个标签的子标签只能有一个。
- 第二个参数:是一个对象,是可选项在滚动效果上通过可选参数实现一些附加功能。
*/
const bscroll = new BScroll(document.querySelector('.content'),
{
/* 默认情况下BScroll是不可以实时的监听滚动位置
probe 侦测
0,1都是不侦测实时的位置
2: 在手指滚动的过程中侦测, 手指离开后的惯性滚动过程中不侦测.
3: 只要是滚动, 都侦测. */
// 1.设置开启监听滚动位置级别
probeType: 3,
// 1.设置开启上拉加载更多
pullUpLoad: true
}
)
// 2.添加监听滚动位置事件,第一个参数是事件类型官网有介绍,这个不是随便定义的名字
bscroll.on('scroll', (position) => {
// console.log(position);
})
// 2.创建上拉加载更多事件,第一个参数是事件类型官网有介绍,这个不是随便定义的名字
bscroll.on('pullingUp', () => {
// 触发网络请求,发送网络请求, 请求更多页的数据
console.log('上拉加载更多');
// 等数据请求完成, 并且将新的数据展示出来后,执行finishPullUp方法已完成上拉加载,
//下次在滑动时候才会再次触发上拉加载
bscroll.finishPullUp()
})
</script>
</body>
</html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。