You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2558 lines
72 KiB
2558 lines
72 KiB
/*
|
|
* Vuescroll v4.16.1
|
|
* (c) 2018-2020 Yi(Yves) Wang
|
|
* Released under the MIT License
|
|
* Github: https://github.com/YvesCoding/vuescroll
|
|
* Website: http://vuescrolljs.yvescoding.org/
|
|
*/
|
|
|
|
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) :
|
|
typeof define === 'function' && define.amd ? define(['vue'], factory) :
|
|
(global.vuescroll = factory(global.Vue));
|
|
}(this, (function (Vue) { 'use strict';
|
|
|
|
Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue;
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
|
return typeof obj;
|
|
} : function (obj) {
|
|
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var classCallCheck = function (instance, Constructor) {
|
|
if (!(instance instanceof Constructor)) {
|
|
throw new TypeError("Cannot call a class as a function");
|
|
}
|
|
};
|
|
|
|
var createClass = function () {
|
|
function defineProperties(target, props) {
|
|
for (var i = 0; i < props.length; i++) {
|
|
var descriptor = props[i];
|
|
descriptor.enumerable = descriptor.enumerable || false;
|
|
descriptor.configurable = true;
|
|
if ("value" in descriptor) descriptor.writable = true;
|
|
Object.defineProperty(target, descriptor.key, descriptor);
|
|
}
|
|
}
|
|
|
|
return function (Constructor, protoProps, staticProps) {
|
|
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
|
if (staticProps) defineProperties(Constructor, staticProps);
|
|
return Constructor;
|
|
};
|
|
}();
|
|
|
|
|
|
|
|
|
|
|
|
var defineProperty = function (obj, key, value) {
|
|
if (key in obj) {
|
|
Object.defineProperty(obj, key, {
|
|
value: value,
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
} else {
|
|
obj[key] = value;
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
var _extends = Object.assign || function (target) {
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var source = arguments[i];
|
|
|
|
for (var key in source) {
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return target;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var 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 isIE() {
|
|
/* istanbul ignore if */
|
|
if (isServer()) return false;
|
|
|
|
var agent = navigator.userAgent.toLowerCase();
|
|
return agent.indexOf('msie') !== -1 || agent.indexOf('trident') !== -1 || agent.indexOf(' edge/') !== -1;
|
|
}
|
|
|
|
var isIos = function isIos() {
|
|
/* istanbul ignore if */
|
|
if (isServer()) return false;
|
|
|
|
var u = navigator.userAgent;
|
|
return !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
|
|
};
|
|
|
|
/* istanbul ignore next */
|
|
var isServer = function isServer() {
|
|
return Vue.prototype.$isServer;
|
|
};
|
|
|
|
var touchManager = function () {
|
|
function touchManager() {
|
|
classCallCheck(this, touchManager);
|
|
}
|
|
|
|
createClass(touchManager, [{
|
|
key: 'getEventObject',
|
|
value: function getEventObject(originEvent) {
|
|
return this.touchObject ? this.isTouch ? originEvent.touches : [originEvent] : null;
|
|
}
|
|
}, {
|
|
key: 'getTouchObject',
|
|
value: function getTouchObject() /* istanbul ignore next */{
|
|
if (isServer()) return null;
|
|
|
|
this.isTouch = false;
|
|
var agent = navigator.userAgent,
|
|
platform = navigator.platform,
|
|
touchObject = {};
|
|
touchObject.touch = !!('ontouchstart' in window && !window.opera || 'msmaxtouchpoints' in window.navigator || 'maxtouchpoints' in window.navigator || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0);
|
|
touchObject.nonDeskTouch = touchObject.touch && !/win32/i.test(platform) || touchObject.touch && /win32/i.test(platform) && /mobile/i.test(agent);
|
|
|
|
touchObject.eventType = 'onmousedown' in window && !touchObject.nonDeskTouch ? 'mouse' : 'ontouchstart' in window ? 'touch' : 'msmaxtouchpoints' in window.navigator || navigator.msMaxTouchPoints > 0 ? 'mstouchpoints' : 'maxtouchpoints' in window.navigator || navigator.maxTouchPoints > 0 ? 'touchpoints' : 'mouse';
|
|
switch (touchObject.eventType) {
|
|
case 'mouse':
|
|
touchObject.touchstart = 'mousedown';
|
|
touchObject.touchend = 'mouseup';
|
|
touchObject.touchmove = 'mousemove';
|
|
|
|
touchObject.touchenter = 'mouseenter';
|
|
touchObject.touchmove = 'mousemove';
|
|
touchObject.touchleave = 'mouseleave';
|
|
break;
|
|
case 'touch':
|
|
touchObject.touchstart = 'touchstart';
|
|
touchObject.touchend = 'touchend';
|
|
touchObject.touchmove = 'touchmove';
|
|
touchObject.touchcancel = 'touchcancel';
|
|
|
|
touchObject.touchenter = 'touchstart';
|
|
touchObject.touchmove = 'touchmove';
|
|
touchObject.touchleave = 'touchend';
|
|
this.isTouch = true;
|
|
break;
|
|
case 'mstouchpoints':
|
|
touchObject.touchstart = 'MSPointerDown';
|
|
touchObject.touchend = 'MSPointerUp';
|
|
touchObject.touchmove = 'MSPointerMove';
|
|
touchObject.touchcancel = 'MSPointerCancel';
|
|
|
|
touchObject.touchenter = 'MSPointerDown';
|
|
touchObject.touchmove = 'MSPointerMove';
|
|
touchObject.touchleave = 'MSPointerUp';
|
|
break;
|
|
case 'touchpoints':
|
|
touchObject.touchstart = 'pointerdown';
|
|
touchObject.touchend = 'pointerup';
|
|
touchObject.touchmove = 'pointermove';
|
|
touchObject.touchcancel = 'pointercancel';
|
|
|
|
touchObject.touchenter = 'pointerdown';
|
|
touchObject.touchmove = 'pointermove';
|
|
touchObject.touchleave = 'pointerup';
|
|
break;
|
|
}
|
|
|
|
return this.touchObject = touchObject;
|
|
}
|
|
}]);
|
|
return touchManager;
|
|
}();
|
|
|
|
function deepCopy(from, to, shallow) {
|
|
if (shallow && isUndef(to)) {
|
|
return from;
|
|
}
|
|
|
|
if (isArray(from)) {
|
|
to = [];
|
|
from.forEach(function (item, index) {
|
|
to[index] = deepCopy(item, to[index]);
|
|
});
|
|
} else if (from) {
|
|
if (!isPlainObj(from)) {
|
|
return from;
|
|
}
|
|
to = {};
|
|
for (var key in from) {
|
|
to[key] = _typeof(from[key]) === 'object' ? deepCopy(from[key], to[key]) : from[key];
|
|
}
|
|
}
|
|
return to;
|
|
}
|
|
|
|
function mergeObject(from, to, force, shallow) {
|
|
if (shallow && isUndef(to)) {
|
|
return from;
|
|
}
|
|
|
|
to = to || {};
|
|
|
|
if (isArray(from)) {
|
|
if (!isArray(to) && force) {
|
|
to = [];
|
|
}
|
|
if (isArray(to)) {
|
|
from.forEach(function (item, index) {
|
|
to[index] = mergeObject(item, to[index], force, shallow);
|
|
});
|
|
}
|
|
} else if (from) {
|
|
if (!isPlainObj(from)) {
|
|
if (force) {
|
|
to = from;
|
|
}
|
|
} else {
|
|
for (var key in from) {
|
|
if (_typeof(from[key]) === 'object') {
|
|
if (isUndef(to[key])) {
|
|
to[key] = deepCopy(from[key], to[key], shallow);
|
|
} else {
|
|
mergeObject(from[key], to[key], force, shallow);
|
|
}
|
|
} else {
|
|
if (isUndef(to[key]) || force) to[key] = from[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return to;
|
|
}
|
|
|
|
function defineReactive(target, key, source, souceKey) {
|
|
/* istanbul ignore if */
|
|
if (!source[key] && typeof source !== 'function') {
|
|
return;
|
|
}
|
|
souceKey = souceKey || key;
|
|
Object.defineProperty(target, key, {
|
|
get: function get$$1() {
|
|
return source[souceKey];
|
|
},
|
|
|
|
configurable: true
|
|
});
|
|
}
|
|
|
|
var scrollBarWidth = void 0;
|
|
function getGutter() {
|
|
/* istanbul ignore next */
|
|
if (isServer()) return 0;
|
|
if (scrollBarWidth !== undefined) return scrollBarWidth;
|
|
var outer = document.createElement('div');
|
|
outer.style.visibility = 'hidden';
|
|
outer.style.width = '100px';
|
|
outer.style.position = 'absolute';
|
|
outer.style.top = '-9999px';
|
|
document.body.appendChild(outer);
|
|
|
|
var widthNoScroll = outer.offsetWidth;
|
|
outer.style.overflow = 'scroll';
|
|
|
|
var inner = document.createElement('div');
|
|
inner.style.width = '100%';
|
|
outer.appendChild(inner);
|
|
|
|
var widthWithScroll = inner.offsetWidth;
|
|
outer.parentNode.removeChild(outer);
|
|
scrollBarWidth = widthNoScroll - widthWithScroll;
|
|
|
|
return scrollBarWidth;
|
|
}
|
|
|
|
function eventCenter(dom, eventName, hander) {
|
|
var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
var type = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 'on';
|
|
|
|
type == 'on' ? dom.addEventListener(eventName, hander, capture) : dom.removeEventListener(eventName, hander, capture);
|
|
}
|
|
|
|
|
|
var warn = function warn(msg) {
|
|
console.warn('[vuescroll] ' + msg);
|
|
};
|
|
|
|
function isChildInParent(child, parent) {
|
|
var flag = false;
|
|
if (!child || !parent) {
|
|
return flag;
|
|
}
|
|
while (child.parentNode !== parent && child.parentNode.nodeType !== 9 && !child.parentNode._isVuescroll) {
|
|
child = child.parentNode;
|
|
}
|
|
if (child.parentNode == parent) {
|
|
flag = true;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
function getPrefix(global) {
|
|
var docStyle = document.documentElement.style;
|
|
var engine;
|
|
/* istanbul ignore if */
|
|
if (global.opera && Object.prototype.toString.call(opera) === '[object Opera]') {
|
|
engine = 'presto';
|
|
} /* istanbul ignore next */else if ('MozAppearance' in docStyle) {
|
|
engine = 'gecko';
|
|
} else if ('WebkitAppearance' in docStyle) {
|
|
engine = 'webkit';
|
|
} /* istanbul ignore next */else if (typeof navigator.cpuClass === 'string') {
|
|
engine = 'trident';
|
|
}
|
|
var vendorPrefix = {
|
|
trident: 'ms',
|
|
gecko: 'moz',
|
|
webkit: 'webkit',
|
|
presto: 'O'
|
|
}[engine];
|
|
return vendorPrefix;
|
|
}
|
|
|
|
function getComplitableStyle(property, value) {
|
|
/* istanbul ignore if */
|
|
if (isServer()) return false;
|
|
|
|
var compatibleValue = '-' + getPrefix(window) + '-' + value;
|
|
var testElm = document.createElement('div');
|
|
testElm.style[property] = compatibleValue;
|
|
if (testElm.style[property] == compatibleValue) {
|
|
return compatibleValue;
|
|
}
|
|
/* istanbul ignore next */
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Insert children into user-passed slot at vnode level
|
|
*/
|
|
function insertChildrenIntoSlot(h) {
|
|
var parentVnode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
var childVNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
|
|
var data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
var swapChildren = arguments[4];
|
|
|
|
/* istanbul ignore if */
|
|
if (parentVnode && parentVnode.length > 1) {
|
|
return swapChildren ? [].concat(toConsumableArray(childVNode), toConsumableArray(parentVnode)) : [].concat(toConsumableArray(parentVnode), toConsumableArray(childVNode));
|
|
}
|
|
|
|
parentVnode = parentVnode[0];
|
|
|
|
var _getVnodeInfo = getVnodeInfo(parentVnode),
|
|
ch = _getVnodeInfo.ch,
|
|
tag = _getVnodeInfo.tag,
|
|
isComponent = _getVnodeInfo.isComponent;
|
|
|
|
if (isComponent) {
|
|
parentVnode.data = mergeObject({ attrs: parentVnode.componentOptions.propsData }, parentVnode.data, false, // force: false
|
|
true // shallow: true
|
|
);
|
|
}
|
|
ch = swapChildren ? [].concat(toConsumableArray(childVNode), toConsumableArray(ch)) : [].concat(toConsumableArray(ch), toConsumableArray(childVNode));
|
|
delete parentVnode.data.slot;
|
|
|
|
return h(tag, mergeObject(data, parentVnode.data, false, true), ch);
|
|
}
|
|
|
|
/**
|
|
* Get the info of a vnode,
|
|
* vnode must be parentVnode
|
|
*/
|
|
function getVnodeInfo(vnode) {
|
|
if (!vnode || vnode.length > 1) return {};
|
|
|
|
vnode = vnode[0] ? vnode[0] : vnode;
|
|
var isComponent = !!vnode.componentOptions;
|
|
var ch = void 0;
|
|
var tag = void 0;
|
|
|
|
if (isComponent) {
|
|
ch = vnode.componentOptions.children || [];
|
|
tag = vnode.componentOptions.tag;
|
|
} else {
|
|
ch = vnode.children || [];
|
|
tag = vnode.tag;
|
|
}
|
|
|
|
return {
|
|
isComponent: isComponent,
|
|
ch: ch,
|
|
tag: tag
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the vuescroll instance instead of
|
|
* user pass component like slot.
|
|
*/
|
|
function getRealParent(ctx) {
|
|
var parent = ctx.$parent;
|
|
if (!parent._isVuescrollRoot && parent) {
|
|
parent = parent.$parent;
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
var isArray = function isArray(_) {
|
|
return Array.isArray(_);
|
|
};
|
|
var isPlainObj = function isPlainObj(_) {
|
|
return Object.prototype.toString.call(_) == '[object Object]';
|
|
};
|
|
var isUndef = function isUndef(_) {
|
|
return typeof _ === 'undefined';
|
|
};
|
|
|
|
function getNumericValue(distance, size) {
|
|
var number = void 0;
|
|
if (!(number = /(-?\d+(?:\.\d+?)?)%$/.exec(distance))) {
|
|
number = distance - 0;
|
|
} else {
|
|
number = number[1] - 0;
|
|
number = size * number / 100;
|
|
}
|
|
return number;
|
|
}
|
|
|
|
function createStyle(styleId, cssText) {
|
|
/* istanbul ignore if */
|
|
if (isServer() || document.getElementById(styleId)) {
|
|
return;
|
|
}
|
|
|
|
var head = document.head || doc.getElementsByTagName('head')[0];
|
|
var style = document.createElement('style');
|
|
|
|
style.id = styleId;
|
|
style.type = 'text/css';
|
|
|
|
/* istanbul ignore if */
|
|
if (style.styleSheet) {
|
|
style.styleSheet.cssText = cssText;
|
|
} else {
|
|
style.appendChild(document.createTextNode(cssText));
|
|
}
|
|
|
|
head.appendChild(style);
|
|
}
|
|
|
|
// Hide the ios native scrollbar.
|
|
function createHideBarStyle() {
|
|
/* istanbul ignore next */
|
|
{
|
|
var cssText = '.__hidebar::-webkit-scrollbar {\n width: 0;\n height: 0;\n }';
|
|
|
|
createStyle('vuescroll-hide-ios-bar', cssText);
|
|
}
|
|
}
|
|
|
|
// create slide mode style
|
|
|
|
var api = {
|
|
mounted: function mounted() {
|
|
vsInstances[this._uid] = this;
|
|
},
|
|
beforeDestroy: function beforeDestroy() {
|
|
delete vsInstances[this._uid];
|
|
},
|
|
|
|
methods: {
|
|
// public api
|
|
scrollTo: function scrollTo(_ref, speed, easing) {
|
|
var x = _ref.x,
|
|
y = _ref.y;
|
|
|
|
// istanbul ignore if
|
|
if (speed === true || typeof speed == 'undefined') {
|
|
speed = this.mergedOptions.scrollPanel.speed;
|
|
}
|
|
this.internalScrollTo(x, y, speed, easing);
|
|
},
|
|
scrollBy: function scrollBy(_ref2, speed, easing) {
|
|
var _ref2$dx = _ref2.dx,
|
|
dx = _ref2$dx === undefined ? 0 : _ref2$dx,
|
|
_ref2$dy = _ref2.dy,
|
|
dy = _ref2$dy === undefined ? 0 : _ref2$dy;
|
|
|
|
var _getPosition = this.getPosition(),
|
|
_getPosition$scrollLe = _getPosition.scrollLeft,
|
|
scrollLeft = _getPosition$scrollLe === undefined ? 0 : _getPosition$scrollLe,
|
|
_getPosition$scrollTo = _getPosition.scrollTop,
|
|
scrollTop = _getPosition$scrollTo === undefined ? 0 : _getPosition$scrollTo;
|
|
|
|
if (dx) {
|
|
scrollLeft += getNumericValue(dx, this.scrollPanelElm.scrollWidth - this.$el.clientWidth);
|
|
}
|
|
if (dy) {
|
|
scrollTop += getNumericValue(dy, this.scrollPanelElm.scrollHeight - this.$el.clientHeight);
|
|
}
|
|
this.internalScrollTo(scrollLeft, scrollTop, speed, easing);
|
|
},
|
|
scrollIntoView: function scrollIntoView(elm) {
|
|
var animate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
|
|
var parentElm = this.$el;
|
|
|
|
if (typeof elm === 'string') {
|
|
elm = parentElm.querySelector(elm);
|
|
}
|
|
|
|
if (!isChildInParent(elm, parentElm)) {
|
|
warn('The element or selector you passed is not the element of Vuescroll, please pass the element that is in Vuescroll to scrollIntoView API. ');
|
|
return;
|
|
}
|
|
|
|
// parent elm left, top
|
|
|
|
var _$el$getBoundingClien = this.$el.getBoundingClientRect(),
|
|
left = _$el$getBoundingClien.left,
|
|
top = _$el$getBoundingClien.top;
|
|
// child elm left, top
|
|
|
|
|
|
var _elm$getBoundingClien = elm.getBoundingClientRect(),
|
|
childLeft = _elm$getBoundingClien.left,
|
|
childTop = _elm$getBoundingClien.top;
|
|
|
|
var diffX = left - childLeft;
|
|
var diffY = top - childTop;
|
|
|
|
this.scrollBy({
|
|
dx: -diffX,
|
|
dy: -diffY
|
|
}, animate);
|
|
},
|
|
refresh: function refresh() {
|
|
this.refreshInternalStatus();
|
|
// refresh again to keep status is correct
|
|
this.$nextTick(this.refreshInternalStatus);
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Public Api */
|
|
|
|
/**
|
|
* Refresh all
|
|
*/
|
|
var vsInstances = {};
|
|
function refreshAll() {
|
|
for (var vs in vsInstances) {
|
|
vsInstances[vs].refresh();
|
|
}
|
|
}
|
|
|
|
var baseConfig = {
|
|
// vuescroll
|
|
vuescroll: {
|
|
// vuescroll's size(height/width) should be a percent(100%)
|
|
// or be a number that is equal to its parentNode's width or
|
|
// height ?
|
|
sizeStrategy: 'percent',
|
|
/** Whether to detect dom resize or not */
|
|
detectResize: true
|
|
},
|
|
scrollPanel: {
|
|
// when component mounted.. it will automatically scrolls.
|
|
initialScrollY: false,
|
|
initialScrollX: false,
|
|
// feat: #11
|
|
scrollingX: true,
|
|
scrollingY: true,
|
|
speed: 300,
|
|
easing: undefined,
|
|
// Sometimes, the nativebar maybe on the left,
|
|
// See https://github.com/YvesCoding/vuescroll/issues/64
|
|
verticalNativeBarPos: 'right',
|
|
maxHeight: undefined,
|
|
maxWidth: undefined
|
|
},
|
|
|
|
//
|
|
rail: {
|
|
background: '#01a99a',
|
|
opacity: 0,
|
|
border: 'none',
|
|
/** Rail's size(Height/Width) , default -> 6px */
|
|
size: '6px',
|
|
/** Specify rail's border-radius, or the border-radius of rail and bar will be equal to the rail's size. default -> false **/
|
|
specifyBorderRadius: false,
|
|
/** Rail the distance from the two ends of the X axis and Y axis. **/
|
|
gutterOfEnds: null,
|
|
/** Rail the distance from the side of container. **/
|
|
gutterOfSide: '2px',
|
|
/** Whether to keep rail show or not, default -> false, event content height is not enough */
|
|
keepShow: false
|
|
},
|
|
bar: {
|
|
/** How long to hide bar after mouseleave, default -> 500 */
|
|
showDelay: 500,
|
|
/** Specify bar's border-radius, or the border-radius of rail and bar will be equal to the rail's size. default -> false **/
|
|
specifyBorderRadius: false,
|
|
/** Whether to show bar on scrolling, default -> true */
|
|
onlyShowBarOnScroll: true,
|
|
/** Whether to keep show or not, default -> false */
|
|
keepShow: false,
|
|
/** Bar's background , default -> #00a650 */
|
|
background: 'rgb(3, 185, 118)',
|
|
/** Bar's opacity, default -> 1 */
|
|
opacity: 1,
|
|
/** bar's size(Height/Width) , default -> 6px */
|
|
|
|
size: '6px',
|
|
minSize: 0,
|
|
disable: false
|
|
},
|
|
scrollButton: {
|
|
enable: false,
|
|
background: 'rgb(3, 185, 118)',
|
|
opacity: 1,
|
|
step: 180,
|
|
mousedownStep: 30
|
|
}
|
|
};
|
|
/**
|
|
* validate the options
|
|
* @export
|
|
* @param {any} ops
|
|
*/
|
|
function validateOps(ops) {
|
|
var renderError = false;
|
|
var scrollPanel = ops.scrollPanel;
|
|
var _ops$bar = ops.bar,
|
|
vBar = _ops$bar.vBar,
|
|
hBar = _ops$bar.hBar;
|
|
var _ops$rail = ops.rail,
|
|
vRail = _ops$rail.vRail,
|
|
hRail = _ops$rail.hRail;
|
|
|
|
// validate scrollPanel
|
|
|
|
var initialScrollY = scrollPanel['initialScrollY'];
|
|
var initialScrollX = scrollPanel['initialScrollX'];
|
|
|
|
if (initialScrollY && !String(initialScrollY).match(/^\d+(\.\d+)?(%)?$/)) {
|
|
warn('The prop `initialScrollY` or `initialScrollX` should be a percent number like `10%` or an exact number that greater than or equal to 0 like `100`.');
|
|
}
|
|
if (initialScrollX && !String(initialScrollX).match(/^\d+(\.\d+)?(%)?$/)) {
|
|
warn('The prop `initialScrollY` or `initialScrollX` should be a percent number like `10%` or an exact number that greater than or equal to 0 like `100`.');
|
|
}
|
|
|
|
// validate deprecated vBar/hBar vRail/hRail
|
|
if (vBar || hBar || vRail || hRail) {
|
|
warn('The options: vRail, hRail, vBar, hBar have been deprecated since v4.7.0,' + 'please use corresponing rail/bar instead!');
|
|
}
|
|
|
|
if (_extraValidate) {
|
|
_extraValidate = [].concat(_extraValidate);
|
|
_extraValidate.forEach(function (hasError) {
|
|
if (hasError(ops)) {
|
|
renderError = true;
|
|
}
|
|
});
|
|
}
|
|
return renderError;
|
|
}
|
|
|
|
var _extraValidate = null;
|
|
var extendOpts = function extendOpts(extraOpts, extraValidate) {
|
|
extraOpts = [].concat(extraOpts);
|
|
extraOpts.forEach(function (opts) {
|
|
mergeObject(opts, baseConfig);
|
|
});
|
|
|
|
_extraValidate = extraValidate;
|
|
};
|
|
|
|
// all modes
|
|
|
|
// do nothing
|
|
|
|
// some small changes.
|
|
var smallChangeArray = ['mergedOptions.vuescroll.pullRefresh.tips', 'mergedOptions.vuescroll.pushLoad.tips', 'mergedOptions.vuescroll.scroller.disable', 'mergedOptions.rail', 'mergedOptions.bar'];
|
|
// refresh/load dom ref/key...
|
|
|
|
var scrollMap = {
|
|
vertical: {
|
|
size: 'height',
|
|
opsSize: 'width',
|
|
posName: 'top',
|
|
opposName: 'bottom',
|
|
sidePosName: 'right',
|
|
page: 'pageY',
|
|
scroll: 'scrollTop',
|
|
scrollSize: 'scrollHeight',
|
|
offset: 'offsetHeight',
|
|
client: 'clientY',
|
|
axis: 'Y',
|
|
scrollButton: {
|
|
start: 'top',
|
|
end: 'bottom'
|
|
}
|
|
},
|
|
horizontal: {
|
|
size: 'width',
|
|
opsSize: 'height',
|
|
posName: 'left',
|
|
opposName: 'right',
|
|
sidePosName: 'bottom',
|
|
page: 'pageX',
|
|
scroll: 'scrollLeft',
|
|
scrollSize: 'scrollWidth',
|
|
offset: 'offsetWidth',
|
|
client: 'clientX',
|
|
axis: 'X',
|
|
scrollButton: {
|
|
start: 'left',
|
|
end: 'right'
|
|
}
|
|
}
|
|
};
|
|
|
|
function requestAnimationFrame(global) {
|
|
// Check for request animation Frame support
|
|
var requestFrame = global.requestAnimationFrame || global.webkitRequestAnimationFrame || global.mozRequestAnimationFrame || global.oRequestAnimationFrame;
|
|
var isNative = !!requestFrame;
|
|
|
|
if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) {
|
|
isNative = false;
|
|
}
|
|
|
|
if (isNative) {
|
|
return function (callback, root) {
|
|
requestFrame(callback, root);
|
|
};
|
|
}
|
|
|
|
var TARGET_FPS = 60;
|
|
var requests = {};
|
|
var rafHandle = 1;
|
|
var intervalHandle = null;
|
|
var lastActive = +new Date();
|
|
|
|
return function (callback) {
|
|
var callbackHandle = rafHandle++;
|
|
|
|
// Store callback
|
|
requests[callbackHandle] = callback;
|
|
|
|
// Create timeout at first request
|
|
if (intervalHandle === null) {
|
|
intervalHandle = setInterval(function () {
|
|
var time = +new Date();
|
|
var currentRequests = requests;
|
|
|
|
// Reset data structure before executing callbacks
|
|
requests = {};
|
|
|
|
for (var key in currentRequests) {
|
|
if (currentRequests.hasOwnProperty(key)) {
|
|
currentRequests[key](time);
|
|
lastActive = time;
|
|
}
|
|
}
|
|
|
|
// Disable the timeout when nothing happens for a certain
|
|
// period of time
|
|
if (time - lastActive > 2500) {
|
|
clearInterval(intervalHandle);
|
|
intervalHandle = null;
|
|
}
|
|
}, 1000 / TARGET_FPS);
|
|
}
|
|
|
|
return callbackHandle;
|
|
};
|
|
}
|
|
|
|
var colorCache = {};
|
|
var rgbReg = /rgb\(/;
|
|
var extractRgbColor = /rgb\((.*)\)/;
|
|
|
|
// Transform a common color int oa `rgbA` color
|
|
function getRgbAColor(color, opacity) {
|
|
var id = color + '&' + opacity;
|
|
if (colorCache[id]) {
|
|
return colorCache[id];
|
|
}
|
|
|
|
var div = document.createElement('div');
|
|
div.style.background = color;
|
|
document.body.appendChild(div);
|
|
var computedColor = window.getComputedStyle(div).backgroundColor;
|
|
document.body.removeChild(div);
|
|
|
|
/* istanbul ignore if */
|
|
if (!rgbReg.test(computedColor)) {
|
|
return color;
|
|
}
|
|
|
|
return colorCache[id] = 'rgba(' + extractRgbColor.exec(computedColor)[1] + ', ' + opacity + ')';
|
|
}
|
|
|
|
var bar = {
|
|
name: 'bar',
|
|
props: {
|
|
ops: Object,
|
|
state: Object,
|
|
hideBar: Boolean,
|
|
otherBarHide: Boolean,
|
|
type: String
|
|
},
|
|
computed: {
|
|
bar: function bar() {
|
|
return scrollMap[this.type];
|
|
},
|
|
barSize: function barSize() {
|
|
return Math.max(this.state.size, this.ops.bar.minSize);
|
|
},
|
|
barRatio: function barRatio() {
|
|
return (1 - this.barSize) / (1 - this.state.size);
|
|
}
|
|
},
|
|
render: function render(h) {
|
|
var _style, _style2, _barStyle;
|
|
|
|
var vm = this;
|
|
/** Get rgbA format background color */
|
|
var railBackgroundColor = getRgbAColor(vm.ops.rail.background, vm.ops.rail.opacity);
|
|
|
|
if (!this.touchManager) {
|
|
this.touchManager = new touchManager();
|
|
}
|
|
|
|
/** Rail Data */
|
|
var railSize = vm.ops.rail.size;
|
|
var endPos = vm.otherBarHide ? 0 : railSize;
|
|
var touchObj = vm.touchManager.getTouchObject();
|
|
var rail = {
|
|
class: '__rail-is-' + vm.type,
|
|
style: (_style = {
|
|
position: 'absolute',
|
|
'z-index': '1',
|
|
|
|
borderRadius: vm.ops.rail.specifyBorderRadius || railSize,
|
|
background: railBackgroundColor,
|
|
border: vm.ops.rail.border
|
|
}, defineProperty(_style, vm.bar.opsSize, railSize), defineProperty(_style, vm.bar.posName, vm.ops.rail['gutterOfEnds'] || 0), defineProperty(_style, vm.bar.opposName, vm.ops.rail['gutterOfEnds'] || endPos), defineProperty(_style, vm.bar.sidePosName, vm.ops.rail['gutterOfSide']), _style)
|
|
};
|
|
|
|
if (touchObj) {
|
|
var _rail$on;
|
|
|
|
rail.on = (_rail$on = {}, defineProperty(_rail$on, touchObj.touchenter, function () {
|
|
vm.setRailHover();
|
|
}), defineProperty(_rail$on, touchObj.touchleave, function () {
|
|
vm.setRailLeave();
|
|
}), _rail$on);
|
|
}
|
|
|
|
// left space for scroll button
|
|
var buttonSize = vm.ops.scrollButton.enable ? railSize : 0;
|
|
var barWrapper = {
|
|
class: '__bar-wrap-is-' + vm.type,
|
|
style: (_style2 = {
|
|
position: 'absolute',
|
|
borderRadius: vm.ops.rail.specifyBorderRadius || railSize
|
|
}, defineProperty(_style2, vm.bar.posName, buttonSize), defineProperty(_style2, vm.bar.opposName, buttonSize), _style2),
|
|
on: {}
|
|
};
|
|
|
|
var scrollDistance = vm.state.posValue * vm.state.size;
|
|
var pos = scrollDistance * vm.barRatio / vm.barSize;
|
|
var opacity = vm.state.opacity;
|
|
var parent = getRealParent(this);
|
|
|
|
// set class hook
|
|
parent.setClassHook(this.type == 'vertical' ? 'vBarVisible' : 'hBarVisible', !!opacity);
|
|
|
|
/** Scrollbar style */
|
|
var barStyle = (_barStyle = {
|
|
cursor: 'pointer',
|
|
position: 'absolute',
|
|
margin: 'auto',
|
|
transition: 'opacity 0.5s',
|
|
'user-select': 'none',
|
|
'border-radius': 'inherit'
|
|
|
|
}, defineProperty(_barStyle, vm.bar.size, vm.barSize * 100 + '%'), defineProperty(_barStyle, 'background', vm.ops.bar.background), defineProperty(_barStyle, vm.bar.opsSize, vm.ops.bar.size), defineProperty(_barStyle, 'opacity', opacity), defineProperty(_barStyle, 'transform', 'translate' + scrollMap[vm.type].axis + '(' + pos + '%)'), _barStyle);
|
|
var bar = {
|
|
style: barStyle,
|
|
class: '__bar-is-' + vm.type,
|
|
ref: 'thumb',
|
|
on: {}
|
|
};
|
|
|
|
if (vm.type == 'vertical') {
|
|
barWrapper.style.width = '100%';
|
|
// Let bar to be on the center.
|
|
bar.style.left = 0;
|
|
bar.style.right = 0;
|
|
} else {
|
|
barWrapper.style.height = '100%';
|
|
bar.style.top = 0;
|
|
bar.style.bottom = 0;
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
{
|
|
var _touchObj = this.touchManager.getTouchObject();
|
|
bar.on[_touchObj.touchstart] = this.createBarEvent();
|
|
barWrapper.on[_touchObj.touchstart] = this.createTrackEvent();
|
|
}
|
|
|
|
return h(
|
|
'div',
|
|
rail,
|
|
[this.createScrollbarButton(h, 'start'), this.hideBar ? null : h(
|
|
'div',
|
|
barWrapper,
|
|
[h('div', bar)]
|
|
), this.createScrollbarButton(h, 'end')]
|
|
);
|
|
},
|
|
data: function data() {
|
|
return {
|
|
isBarDragging: false
|
|
};
|
|
},
|
|
|
|
methods: {
|
|
setRailHover: function setRailHover() {
|
|
var parent = getRealParent(this);
|
|
var state = parent.vuescroll.state;
|
|
|
|
if (!state.isRailHover) {
|
|
state.isRailHover = true;
|
|
parent.showBar();
|
|
}
|
|
},
|
|
setRailLeave: function setRailLeave() {
|
|
var parent = getRealParent(this);
|
|
var state = parent.vuescroll.state;
|
|
|
|
state.isRailHover = false;
|
|
parent.hideBar();
|
|
},
|
|
setBarDrag: function setBarDrag(val) /* istanbul ignore next */{
|
|
this.$emit('setBarDrag', this.isBarDragging = val);
|
|
|
|
var parent = getRealParent(this);
|
|
// set class hook
|
|
parent.setClassHook(this.type == 'vertical' ? 'vBarDragging' : 'hBarDragging', !!val);
|
|
},
|
|
createBarEvent: function createBarEvent() {
|
|
var ctx = this;
|
|
|
|
var parent = getRealParent(ctx);
|
|
var touchObj = ctx.touchManager.getTouchObject();
|
|
|
|
function mousedown(e) /* istanbul ignore next */{
|
|
var event = ctx.touchManager.getEventObject(e);
|
|
if (!event) return;
|
|
|
|
e.stopImmediatePropagation();
|
|
e.preventDefault();
|
|
event = event[0];
|
|
|
|
document.onselectstart = function () {
|
|
return false;
|
|
};
|
|
ctx.axisStartPos = event[ctx.bar.client] - ctx.$refs['thumb'].getBoundingClientRect()[ctx.bar.posName];
|
|
|
|
// Tell parent that the mouse has been down.
|
|
ctx.setBarDrag(true);
|
|
eventCenter(document, touchObj.touchmove, mousemove);
|
|
eventCenter(document, touchObj.touchend, mouseup);
|
|
}
|
|
|
|
function mousemove(e) /* istanbul ignore next */{
|
|
if (!ctx.axisStartPos) {
|
|
return;
|
|
}
|
|
|
|
var event = ctx.touchManager.getEventObject(e);
|
|
if (!event) return;
|
|
|
|
event = event[0];
|
|
|
|
var thubmParent = ctx.$refs.thumb.parentNode;
|
|
|
|
var delta = event[ctx.bar.client] - thubmParent.getBoundingClientRect()[ctx.bar.posName];
|
|
delta = delta / ctx.barRatio;
|
|
|
|
var percent = (delta - ctx.axisStartPos) / thubmParent[ctx.bar.offset];
|
|
parent.scrollTo(defineProperty({}, ctx.bar.axis.toLowerCase(), parent.scrollPanelElm[ctx.bar.scrollSize] * percent), false);
|
|
}
|
|
|
|
function mouseup() /* istanbul ignore next */{
|
|
ctx.setBarDrag(false);
|
|
parent.hideBar();
|
|
|
|
document.onselectstart = null;
|
|
ctx.axisStartPos = 0;
|
|
|
|
eventCenter(document, touchObj.touchmove, mousemove, false, 'off');
|
|
eventCenter(document, touchObj.touchend, mouseup, false, 'off');
|
|
}
|
|
|
|
return mousedown;
|
|
},
|
|
createTrackEvent: function createTrackEvent() {
|
|
var ctx = this;
|
|
|
|
return function handleClickTrack(e) {
|
|
var parent = getRealParent(ctx);
|
|
|
|
var _ctx$bar = ctx.bar,
|
|
client = _ctx$bar.client,
|
|
offset = _ctx$bar.offset,
|
|
posName = _ctx$bar.posName,
|
|
axis = _ctx$bar.axis;
|
|
|
|
var thumb = ctx.$refs['thumb'];
|
|
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
|
|
/* istanbul ignore if */
|
|
if (!thumb) return;
|
|
|
|
var barOffset = thumb[offset];
|
|
var event = ctx.touchManager.getEventObject(e)[0];
|
|
|
|
var percent = (event[client] - e.currentTarget.getBoundingClientRect()[posName] - barOffset / 2) / (e.currentTarget[offset] - barOffset);
|
|
|
|
parent.scrollTo(defineProperty({}, axis.toLowerCase(), percent * 100 + '%'));
|
|
};
|
|
},
|
|
|
|
|
|
// Scrollbuton relative things...
|
|
createScrollbarButton: function createScrollbarButton(h, type /* start or end */) {
|
|
var _style3;
|
|
|
|
var barContext = this;
|
|
|
|
if (!barContext.ops.scrollButton.enable) {
|
|
return null;
|
|
}
|
|
|
|
var size = barContext.ops.rail.size;
|
|
var _barContext$ops$scrol = barContext.ops.scrollButton,
|
|
opacity = _barContext$ops$scrol.opacity,
|
|
background = _barContext$ops$scrol.background;
|
|
|
|
var borderColor = getRgbAColor(background, opacity);
|
|
|
|
var wrapperProps = {
|
|
class: ['__bar-button', '__bar-button-is-' + barContext.type + '-' + type],
|
|
style: (_style3 = {}, defineProperty(_style3, barContext.bar.scrollButton[type], 0), defineProperty(_style3, 'width', size), defineProperty(_style3, 'height', size), defineProperty(_style3, 'position', 'absolute'), defineProperty(_style3, 'cursor', 'pointer'), defineProperty(_style3, 'display', 'table'), _style3),
|
|
ref: type
|
|
};
|
|
|
|
var innerProps = {
|
|
class: '__bar-button-inner',
|
|
style: {
|
|
border: 'calc(' + size + ' / 2.5) solid transparent',
|
|
width: '0',
|
|
height: '0',
|
|
margin: 'auto',
|
|
position: 'absolute',
|
|
top: '0',
|
|
bottom: '0',
|
|
right: '0',
|
|
left: '0'
|
|
},
|
|
on: {}
|
|
};
|
|
|
|
if (barContext.type == 'vertical') {
|
|
if (type == 'start') {
|
|
innerProps.style['border-bottom-color'] = borderColor;
|
|
innerProps.style['transform'] = 'translateY(-25%)';
|
|
} else {
|
|
innerProps.style['border-top-color'] = borderColor;
|
|
innerProps.style['transform'] = 'translateY(25%)';
|
|
}
|
|
} else {
|
|
if (type == 'start') {
|
|
innerProps.style['border-right-color'] = borderColor;
|
|
innerProps.style['transform'] = 'translateX(-25%)';
|
|
} else {
|
|
innerProps.style['border-left-color'] = borderColor;
|
|
innerProps.style['transform'] = 'translateX(25%)';
|
|
}
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
{
|
|
var touchObj = this.touchManager.getTouchObject();
|
|
innerProps.on[touchObj.touchstart] = this.createScrollButtonEvent(type, touchObj);
|
|
}
|
|
|
|
return h(
|
|
'div',
|
|
wrapperProps,
|
|
[h('div', innerProps)]
|
|
);
|
|
},
|
|
createScrollButtonEvent: function createScrollButtonEvent(type, touchObj) {
|
|
var ctx = this;
|
|
var parent = getRealParent(ctx);
|
|
|
|
var _ctx$ops$scrollButton = ctx.ops.scrollButton,
|
|
step = _ctx$ops$scrollButton.step,
|
|
mousedownStep = _ctx$ops$scrollButton.mousedownStep;
|
|
|
|
|
|
var stepWithDirection = type == 'start' ? -step : step;
|
|
var mousedownStepWithDirection = type == 'start' ? -mousedownStep : mousedownStep;
|
|
var ref = requestAnimationFrame(window);
|
|
|
|
// bar props: type
|
|
var barType = ctx.type;
|
|
|
|
var isMouseDown = false;
|
|
var isMouseout = true;
|
|
var timeoutId = void 0;
|
|
|
|
function start(e) {
|
|
/* istanbul ignore if */
|
|
|
|
if (3 == e.which) {
|
|
return;
|
|
}
|
|
|
|
// set class hook
|
|
parent.setClassHook('cliking' + barType + type + 'Button', true);
|
|
|
|
e.stopImmediatePropagation();
|
|
e.preventDefault();
|
|
|
|
isMouseout = false;
|
|
|
|
parent.scrollBy(defineProperty({}, 'd' + ctx.bar.axis.toLowerCase(), stepWithDirection));
|
|
|
|
eventCenter(document, touchObj.touchend, endPress, false);
|
|
|
|
if (touchObj.touchstart == 'mousedown') {
|
|
var elm = ctx.$refs[type];
|
|
eventCenter(elm, 'mouseenter', enter, false);
|
|
eventCenter(elm, 'mouseleave', leave, false);
|
|
}
|
|
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(function () /* istanbul ignore next */{
|
|
isMouseDown = true;
|
|
ref(pressing, window);
|
|
}, 500);
|
|
}
|
|
|
|
function pressing() /* istanbul ignore next */{
|
|
if (isMouseDown && !isMouseout) {
|
|
parent.scrollBy(defineProperty({}, 'd' + ctx.bar.axis.toLowerCase(), mousedownStepWithDirection), false);
|
|
ref(pressing, window);
|
|
}
|
|
}
|
|
|
|
function endPress() {
|
|
clearTimeout(timeoutId);
|
|
isMouseDown = false;
|
|
eventCenter(document, touchObj.touchend, endPress, false, 'off');
|
|
|
|
if (touchObj.touchstart == 'mousedown') {
|
|
var elm = ctx.$refs[type];
|
|
eventCenter(elm, 'mouseenter', enter, false, 'off');
|
|
eventCenter(elm, 'mouseleave', leave, false, 'off');
|
|
}
|
|
|
|
parent.setClassHook('cliking' + barType + type + 'Button', false);
|
|
}
|
|
|
|
function enter() /* istanbul ignore next */{
|
|
isMouseout = false;
|
|
pressing();
|
|
}
|
|
|
|
function leave() /* istanbul ignore next */{
|
|
isMouseout = true;
|
|
}
|
|
|
|
return start;
|
|
}
|
|
}
|
|
};
|
|
|
|
function getBarData(vm, type) {
|
|
var axis = scrollMap[type].axis;
|
|
/** type.charAt(0) = vBar/hBar */
|
|
var barType = type.charAt(0) + 'Bar';
|
|
|
|
var hideBar = !vm.bar[barType].state.size || !vm.mergedOptions.scrollPanel['scrolling' + axis] || vm.refreshLoad && type !== 'vertical' || vm.mergedOptions.bar.disable;
|
|
|
|
var keepShowRail = vm.mergedOptions.rail.keepShow;
|
|
|
|
if (hideBar && !keepShowRail) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
hideBar: hideBar,
|
|
props: {
|
|
type: type,
|
|
ops: {
|
|
bar: vm.mergedOptions.bar,
|
|
rail: vm.mergedOptions.rail,
|
|
scrollButton: vm.mergedOptions.scrollButton
|
|
},
|
|
state: vm.bar[barType].state,
|
|
hideBar: hideBar
|
|
},
|
|
on: {
|
|
setBarDrag: vm.setBarDrag
|
|
},
|
|
ref: type + 'Bar',
|
|
key: type
|
|
};
|
|
}
|
|
|
|
/**
|
|
* create bars
|
|
*
|
|
* @param {any} size
|
|
* @param {any} type
|
|
*/
|
|
function createBar(h, vm) {
|
|
var verticalBarProps = getBarData(vm, 'vertical');
|
|
var horizontalBarProps = getBarData(vm, 'horizontal');
|
|
|
|
// set class hooks
|
|
vm.setClassHook('hasVBar', !!(verticalBarProps && !verticalBarProps.hideBar));
|
|
vm.setClassHook('hasHBar', !!(horizontalBarProps && !horizontalBarProps.hideBar));
|
|
|
|
return [verticalBarProps ? h('bar', _extends({}, verticalBarProps, {
|
|
props: _extends({ otherBarHide: !horizontalBarProps }, verticalBarProps.props)
|
|
})) : null, horizontalBarProps ? h('bar', _extends({}, horizontalBarProps, {
|
|
props: _extends({ otherBarHide: !verticalBarProps }, horizontalBarProps.props)
|
|
})) : null];
|
|
}
|
|
|
|
/**
|
|
* This is like a HOC, It extracts the common parts of the
|
|
* native-mode, slide-mode and mix-mode.
|
|
* Each mode must implement the following methods:
|
|
* 1. refreshInternalStatus : use to refresh the component
|
|
* 2. destroy : Destroy some registryed events before component destroy.
|
|
* 3. updateBarStateAndEmitEvent: use to update bar states and emit events.
|
|
*/
|
|
|
|
var createComponent = function createComponent(_ref) {
|
|
var _render = _ref.render,
|
|
components = _ref.components,
|
|
mixins = _ref.mixins;
|
|
return {
|
|
name: 'vueScroll',
|
|
props: {
|
|
ops: { type: Object }
|
|
},
|
|
components: components,
|
|
mixins: [api].concat(toConsumableArray([].concat(mixins))),
|
|
created: function created() {
|
|
var _this = this;
|
|
|
|
/**
|
|
* Begin to merge options
|
|
*/
|
|
|
|
var _gfc = mergeObject(this.$vuescrollConfig || {}, {});
|
|
var ops = mergeObject(baseConfig, _gfc);
|
|
|
|
this.$options.propsData.ops = this.$options.propsData.ops || {};
|
|
Object.keys(this.$options.propsData.ops).forEach(function (key) {
|
|
{
|
|
defineReactive(_this.mergedOptions, key, _this.$options.propsData.ops);
|
|
}
|
|
});
|
|
// from ops to mergedOptions
|
|
mergeObject(ops, this.mergedOptions);
|
|
|
|
this._isVuescrollRoot = true;
|
|
this.renderError = validateOps(this.mergedOptions);
|
|
},
|
|
render: function render(h) {
|
|
var vm = this;
|
|
if (vm.renderError) {
|
|
return h('div', [[vm.$slots['default']]]);
|
|
}
|
|
|
|
if (!vm.touchManager) vm.touchManager = new touchManager();
|
|
|
|
// vuescroll data
|
|
var data = {
|
|
style: {
|
|
height: vm.vuescroll.state.height,
|
|
width: vm.vuescroll.state.width,
|
|
padding: 0,
|
|
position: 'relative',
|
|
overflow: 'hidden'
|
|
},
|
|
class: _extends({ __vuescroll: true }, vm.classHooks)
|
|
};
|
|
|
|
var touchObj = vm.touchManager.getTouchObject();
|
|
if (touchObj) {
|
|
var _data$on;
|
|
|
|
data.on = (_data$on = {}, defineProperty(_data$on, touchObj.touchenter, function () {
|
|
vm.vuescroll.state.pointerLeave = false;
|
|
vm.updateBarStateAndEmitEvent();
|
|
|
|
vm.setClassHook('mouseEnter', true);
|
|
}), defineProperty(_data$on, touchObj.touchleave, function () {
|
|
vm.vuescroll.state.pointerLeave = true;
|
|
vm.hideBar();
|
|
|
|
vm.setClassHook('mouseEnter', false);
|
|
}), defineProperty(_data$on, touchObj.touchmove, function () /* istanbul ignore next */{
|
|
vm.vuescroll.state.pointerLeave = false;
|
|
vm.updateBarStateAndEmitEvent();
|
|
}), _data$on);
|
|
}
|
|
|
|
var ch = [_render(h, vm)].concat(toConsumableArray(createBar(h, vm)));
|
|
|
|
var _customContainer = this.$slots['scroll-container'];
|
|
if (_customContainer) {
|
|
return insertChildrenIntoSlot(h, _customContainer, ch, data);
|
|
}
|
|
|
|
return h(
|
|
'div',
|
|
data,
|
|
[ch]
|
|
);
|
|
},
|
|
mounted: function mounted() {
|
|
var _this2 = this;
|
|
|
|
if (!this.renderError) {
|
|
this.initVariables();
|
|
this.initWatchOpsChange();
|
|
// Call external merged Api
|
|
this.refreshInternalStatus();
|
|
|
|
this.updatedCbs.push(function () {
|
|
_this2.scrollToAnchor();
|
|
// need to reflow to deal with the
|
|
// latest thing.
|
|
_this2.updateBarStateAndEmitEvent();
|
|
});
|
|
}
|
|
},
|
|
updated: function updated() {
|
|
var _this3 = this;
|
|
|
|
this.updatedCbs.forEach(function (cb) {
|
|
cb.call(_this3);
|
|
});
|
|
// Clear
|
|
this.updatedCbs = [];
|
|
},
|
|
beforeDestroy: function beforeDestroy() {
|
|
if (this.destroy) {
|
|
this.destroy();
|
|
}
|
|
},
|
|
|
|
|
|
/** ------------------------------- Computed ----------------------------- */
|
|
computed: {
|
|
scrollPanelElm: function scrollPanelElm() {
|
|
return this.$refs['scrollPanel']._isVue ? this.$refs['scrollPanel'].$el : this.$refs['scrollPanel'];
|
|
}
|
|
},
|
|
data: function data() {
|
|
return {
|
|
vuescroll: {
|
|
state: {
|
|
isDragging: false,
|
|
pointerLeave: true,
|
|
isRailHover: false,
|
|
/** Default sizeStrategies */
|
|
height: '100%',
|
|
width: '100%',
|
|
// current size strategy
|
|
currentSizeStrategy: 'percent',
|
|
currentScrollState: null,
|
|
currentScrollInfo: null
|
|
}
|
|
},
|
|
bar: {
|
|
vBar: {
|
|
state: {
|
|
posValue: 0,
|
|
size: 0,
|
|
opacity: 0
|
|
}
|
|
},
|
|
hBar: {
|
|
state: {
|
|
posValue: 0,
|
|
size: 0,
|
|
opacity: 0
|
|
}
|
|
}
|
|
},
|
|
mergedOptions: {
|
|
vuescroll: {},
|
|
scrollPanel: {},
|
|
scrollContent: {},
|
|
rail: {},
|
|
bar: {}
|
|
},
|
|
updatedCbs: [],
|
|
renderError: false,
|
|
|
|
classHooks: {
|
|
hasVBar: false,
|
|
hasHBar: false,
|
|
|
|
vBarVisible: false,
|
|
hBarVisible: false,
|
|
|
|
vBarDragging: false,
|
|
hBarDragging: false,
|
|
|
|
clikingVerticalStartButton: false,
|
|
clikingVerticalEndButton: false,
|
|
clikingHorizontalStartButton: false,
|
|
clikingHorizontalEndButton: false,
|
|
|
|
mouseEnter: false
|
|
}
|
|
};
|
|
},
|
|
|
|
/** ------------------------------- Methods -------------------------------- */
|
|
methods: {
|
|
/** ------------------------ Handlers --------------------------- */
|
|
|
|
scrollingComplete: function scrollingComplete() {
|
|
this.updateBarStateAndEmitEvent('handle-scroll-complete');
|
|
},
|
|
setBarDrag: function setBarDrag(val) {
|
|
/* istanbul ignore next */
|
|
this.vuescroll.state.isDragging = val;
|
|
},
|
|
setClassHook: function setClassHook(name, value) {
|
|
this.classHooks[name] = value;
|
|
},
|
|
|
|
|
|
/** ------------------------ Some Helpers --------------------------- */
|
|
|
|
/*
|
|
* To have a good ux, instead of hiding bar immediately, we hide bar
|
|
* after some seconds by using this simple debounce-hidebar method.
|
|
*/
|
|
showAndDefferedHideBar: function showAndDefferedHideBar(forceHideBar) {
|
|
var _this4 = this;
|
|
|
|
this.showBar();
|
|
|
|
if (this.timeoutId) {
|
|
clearTimeout(this.timeoutId);
|
|
this.timeoutId = 0;
|
|
}
|
|
|
|
this.timeoutId = setTimeout(function () {
|
|
_this4.timeoutId = 0;
|
|
_this4.hideBar(forceHideBar);
|
|
}, this.mergedOptions.bar.showDelay);
|
|
},
|
|
showBar: function showBar() {
|
|
var opacity = this.mergedOptions.bar.opacity;
|
|
this.bar.vBar.state.opacity = opacity;
|
|
this.bar.hBar.state.opacity = opacity;
|
|
},
|
|
hideBar: function hideBar(forceHideBar) {
|
|
var _vuescroll$state = this.vuescroll.state,
|
|
isDragging = _vuescroll$state.isDragging,
|
|
isRailHover = _vuescroll$state.isRailHover;
|
|
/* istanbul ignore next */
|
|
|
|
if (isDragging || isRailHover) {
|
|
return;
|
|
}
|
|
|
|
if (forceHideBar && !this.mergedOptions.bar.keepShow) {
|
|
this.bar.hBar.state.opacity = 0;
|
|
this.bar.vBar.state.opacity = 0;
|
|
}
|
|
|
|
// add isDragging condition
|
|
// to prevent from hiding bar while dragging the bar
|
|
if (!this.mergedOptions.bar.keepShow && !this.vuescroll.state.isDragging) {
|
|
this.bar.vBar.state.opacity = 0;
|
|
this.bar.hBar.state.opacity = 0;
|
|
}
|
|
},
|
|
useNumbericSize: function useNumbericSize() {
|
|
this.vuescroll.state.currentSizeStrategy = 'number';
|
|
var _mergedOptions$scroll = this.mergedOptions.scrollPanel,
|
|
maxHeight = _mergedOptions$scroll.maxHeight,
|
|
maxWidth = _mergedOptions$scroll.maxWidth;
|
|
var _$el$parentNode = this.$el.parentNode,
|
|
parentClientHeight = _$el$parentNode.clientHeight,
|
|
parentClientWidth = _$el$parentNode.clientWidth;
|
|
var _scrollPanelElm = this.scrollPanelElm,
|
|
scrollHeight = _scrollPanelElm.scrollHeight,
|
|
scrollWidth = _scrollPanelElm.scrollWidth;
|
|
|
|
var width = void 0;
|
|
var height = void 0;
|
|
|
|
if (maxHeight || maxWidth) {
|
|
height = scrollHeight <= maxHeight ? undefined : maxHeight;
|
|
width = scrollWidth <= maxWidth ? undefined : maxWidth;
|
|
} else {
|
|
height = parentClientHeight;
|
|
width = parentClientWidth;
|
|
}
|
|
|
|
this.vuescroll.state.height = height ? height + 'px' : undefined;
|
|
this.vuescroll.state.width = width ? width + 'px' : undefined;
|
|
},
|
|
usePercentSize: function usePercentSize() {
|
|
this.vuescroll.state.currentSizeStrategy = 'percent';
|
|
|
|
this.vuescroll.state.height = '100%';
|
|
this.vuescroll.state.width = '100%';
|
|
},
|
|
|
|
// Set its size to be equal to its parentNode
|
|
setVsSize: function setVsSize() {
|
|
var sizeStrategy = this.mergedOptions.vuescroll.sizeStrategy;
|
|
var _mergedOptions$scroll2 = this.mergedOptions.scrollPanel,
|
|
maxHeight = _mergedOptions$scroll2.maxHeight,
|
|
maxWidth = _mergedOptions$scroll2.maxWidth;
|
|
var _scrollPanelElm2 = this.scrollPanelElm,
|
|
clientHeight = _scrollPanelElm2.clientHeight,
|
|
clientWidth = _scrollPanelElm2.clientWidth;
|
|
|
|
if (sizeStrategy == 'number' || maxHeight && clientHeight > maxHeight || maxWidth && clientWidth > maxWidth) {
|
|
this.useNumbericSize();
|
|
} else if (sizeStrategy == 'percent' && clientHeight != maxHeight && clientWidth != maxWidth) {
|
|
this.usePercentSize();
|
|
}
|
|
},
|
|
|
|
|
|
/** ------------------------ Init --------------------------- */
|
|
initWatchOpsChange: function initWatchOpsChange() {
|
|
var _this5 = this;
|
|
|
|
var watchOpts = {
|
|
deep: true,
|
|
sync: true
|
|
};
|
|
this.$watch('mergedOptions', function () {
|
|
setTimeout(function () {
|
|
if (_this5.isSmallChangeThisTick) {
|
|
_this5.isSmallChangeThisTick = false;
|
|
_this5.updateBarStateAndEmitEvent('options-change');
|
|
return;
|
|
}
|
|
_this5.refreshInternalStatus();
|
|
}, 0);
|
|
}, watchOpts);
|
|
|
|
/**
|
|
* We also watch `small` changes, and when small changes happen, we send
|
|
* a signal to vuescroll, to tell it:
|
|
* 1. we don't need to registry resize
|
|
* 2. we don't need to registry scroller.
|
|
*/
|
|
smallChangeArray.forEach(function (opts) {
|
|
_this5.$watch(opts, function () {
|
|
_this5.isSmallChangeThisTick = true;
|
|
}, watchOpts);
|
|
});
|
|
},
|
|
|
|
// scrollTo hash-anchor while mounted component have mounted.
|
|
scrollToAnchor: function scrollToAnchor() /* istanbul ignore next */{
|
|
var validateHashSelector = function validateHashSelector(hash) {
|
|
return (/^#[a-zA-Z_]\d*$/.test(hash)
|
|
);
|
|
};
|
|
|
|
var hash = window.location.hash;
|
|
if (!hash || (hash = hash.slice(hash.lastIndexOf('#'))) && !validateHashSelector(hash)) {
|
|
return;
|
|
}
|
|
|
|
var elm = document.querySelector(hash);
|
|
if (!isChildInParent(elm, this.$el) || this.mergedOptions.scrollPanel.initialScrollY || this.mergedOptions.scrollPanel.initialScrollX) {
|
|
return;
|
|
}
|
|
|
|
this.scrollIntoView(elm);
|
|
}
|
|
|
|
/** ------------------------ Registry Resize --------------------------- */
|
|
|
|
}
|
|
};
|
|
};
|
|
|
|
// begin importing
|
|
var scrollPanel = {
|
|
name: 'scrollPanel',
|
|
props: { ops: { type: Object, required: true } },
|
|
methods: {
|
|
// trigger scrollPanel options initialScrollX,
|
|
// initialScrollY
|
|
updateInitialScroll: function updateInitialScroll() {
|
|
var x = 0;
|
|
var y = 0;
|
|
|
|
var parent = getRealParent(this);
|
|
|
|
if (this.ops.initialScrollX) {
|
|
x = this.ops.initialScrollX;
|
|
}
|
|
if (this.ops.initialScrollY) {
|
|
y = this.ops.initialScrollY;
|
|
}
|
|
if (x || y) {
|
|
parent.scrollTo({ x: x, y: y });
|
|
}
|
|
}
|
|
},
|
|
mounted: function mounted() {
|
|
var _this = this;
|
|
|
|
setTimeout(function () {
|
|
if (!_this._isDestroyed) {
|
|
_this.updateInitialScroll();
|
|
}
|
|
}, 0);
|
|
},
|
|
render: function render(h) {
|
|
// eslint-disable-line
|
|
var data = {
|
|
class: ['__panel'],
|
|
style: {
|
|
position: 'relative',
|
|
boxSizing: 'border-box'
|
|
}
|
|
};
|
|
|
|
var parent = getRealParent(this);
|
|
|
|
var _customPanel = parent.$slots['scroll-panel'];
|
|
if (_customPanel) {
|
|
return insertChildrenIntoSlot(h, _customPanel, this.$slots.default, data);
|
|
}
|
|
|
|
return h(
|
|
'div',
|
|
data,
|
|
[[this.$slots.default]]
|
|
);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Init following things
|
|
* 1. Component
|
|
* 2. Render
|
|
* 3. Config
|
|
*/
|
|
function _install(core, render) {
|
|
var _components;
|
|
|
|
var extraConfigs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
|
|
var extraValidators = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
|
|
var components = (_components = {}, defineProperty(_components, scrollPanel.name, scrollPanel), defineProperty(_components, bar.name, bar), _components);
|
|
|
|
var opts = {};
|
|
opts.components = components;
|
|
opts.render = render;
|
|
opts.mixins = core;
|
|
|
|
var comp = createComponent(opts);
|
|
|
|
// Init Config
|
|
extendOpts(extraConfigs, extraValidators);
|
|
|
|
return comp;
|
|
}
|
|
|
|
/**
|
|
* Get the children of parent those are in viewport
|
|
*/
|
|
function getCurrentViewportDom(parent, container) {
|
|
var children = parent.children;
|
|
var domFragment = [];
|
|
|
|
var isCurrentview = function isCurrentview(dom) {
|
|
var _dom$getBoundingClien = dom.getBoundingClientRect(),
|
|
left = _dom$getBoundingClien.left,
|
|
top = _dom$getBoundingClien.top,
|
|
width = _dom$getBoundingClien.width,
|
|
height = _dom$getBoundingClien.height;
|
|
|
|
var _container$getBoundin = container.getBoundingClientRect(),
|
|
parentLeft = _container$getBoundin.left,
|
|
parentTop = _container$getBoundin.top,
|
|
parentHeight = _container$getBoundin.height,
|
|
parentWidth = _container$getBoundin.width;
|
|
|
|
if (left - parentLeft + width > 0 && left - parentLeft < parentWidth && top - parentTop + height > 0 && top - parentTop < parentHeight) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
for (var i = 0; i < children.length; i++) {
|
|
var dom = children.item(i);
|
|
if (isCurrentview(dom) && !dom.isResizeElm) {
|
|
domFragment.push(dom);
|
|
}
|
|
}
|
|
return domFragment;
|
|
}
|
|
|
|
/**
|
|
* Compatible to scroller's animation function
|
|
*/
|
|
function createEasingFunction(easing, easingPattern) {
|
|
return function (time) {
|
|
return easingPattern(easing, time);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Calculate the easing pattern
|
|
* @link https://github.com/cferdinandi/smooth-scroll/blob/master/src/js/smooth-scroll.js
|
|
* modified by wangyi7099
|
|
* @param {String} type Easing pattern
|
|
* @param {Number} time Time animation should take to complete
|
|
* @returns {Number}
|
|
*/
|
|
function easingPattern(easing, time) {
|
|
var pattern = null;
|
|
/* istanbul ignore next */
|
|
{
|
|
// Default Easing Patterns
|
|
if (easing === 'easeInQuad') pattern = time * time; // accelerating from zero velocity
|
|
if (easing === 'easeOutQuad') pattern = time * (2 - time); // decelerating to zero velocity
|
|
if (easing === 'easeInOutQuad') pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
|
|
if (easing === 'easeInCubic') pattern = time * time * time; // accelerating from zero velocity
|
|
if (easing === 'easeOutCubic') pattern = --time * time * time + 1; // decelerating to zero velocity
|
|
if (easing === 'easeInOutCubic') pattern = time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
|
|
if (easing === 'easeInQuart') pattern = time * time * time * time; // accelerating from zero velocity
|
|
if (easing === 'easeOutQuart') pattern = 1 - --time * time * time * time; // decelerating to zero velocity
|
|
if (easing === 'easeInOutQuart') pattern = time < 0.5 ? 8 * time * time * time * time : 1 - 8 * --time * time * time * time; // acceleration until halfway, then deceleration
|
|
if (easing === 'easeInQuint') pattern = time * time * time * time * time; // accelerating from zero velocity
|
|
if (easing === 'easeOutQuint') pattern = 1 + --time * time * time * time * time; // decelerating to zero velocity
|
|
if (easing === 'easeInOutQuint') pattern = time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * --time * time * time * time * time; // acceleration until halfway, then deceleration
|
|
}
|
|
return pattern || time; // no easing, no acceleration
|
|
}
|
|
|
|
function noop() {
|
|
return true;
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
var now = Date.now || function () {
|
|
return new Date().getTime();
|
|
};
|
|
|
|
var ScrollControl = function () {
|
|
function ScrollControl() {
|
|
classCallCheck(this, ScrollControl);
|
|
|
|
this.init();
|
|
|
|
this.isRunning = false;
|
|
}
|
|
|
|
createClass(ScrollControl, [{
|
|
key: 'pause',
|
|
value: function pause() {
|
|
/* istanbul ignore if */
|
|
if (!this.isRunning) return;
|
|
|
|
this.isPaused = true;
|
|
}
|
|
}, {
|
|
key: 'stop',
|
|
value: function stop() {
|
|
this.isStopped = true;
|
|
}
|
|
}, {
|
|
key: 'continue',
|
|
value: function _continue() {
|
|
/* istanbul ignore if */
|
|
if (!this.isPaused) return;
|
|
|
|
this.isPaused = false;
|
|
this.ts = now() - this.percent * this.spd;
|
|
this.execScroll();
|
|
}
|
|
}, {
|
|
key: 'startScroll',
|
|
value: function startScroll(st, ed, spd) {
|
|
var stepCb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : noop;
|
|
var completeCb = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : noop;
|
|
var vertifyCb = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : noop;
|
|
var easingMethod = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : noop;
|
|
|
|
var df = ed - st;
|
|
var dir = df > 0 ? -1 : 1;
|
|
var nt = now();
|
|
|
|
if (!this.isRunning) {
|
|
this.init();
|
|
}
|
|
|
|
if (dir != this.dir || nt - this.ts > 200) {
|
|
this.ts = nt;
|
|
|
|
this.dir = dir;
|
|
this.st = st;
|
|
this.ed = ed;
|
|
this.df = df;
|
|
} /* istanbul ignore next */else {
|
|
this.df += df;
|
|
}
|
|
|
|
this.spd = spd;
|
|
|
|
this.completeCb = completeCb;
|
|
this.vertifyCb = vertifyCb;
|
|
this.stepCb = stepCb;
|
|
this.easingMethod = easingMethod;
|
|
|
|
if (!this.isRunning) this.execScroll();
|
|
}
|
|
}, {
|
|
key: 'execScroll',
|
|
value: function execScroll() {
|
|
var _this = this;
|
|
|
|
if (!this.df) return;
|
|
|
|
var percent = this.percent || 0;
|
|
this.percent = 0;
|
|
this.isRunning = true;
|
|
|
|
var loop = function loop() {
|
|
/* istanbul ignore if */
|
|
if (!_this.isRunning || !_this.vertifyCb(percent) || _this.isStopped) {
|
|
_this.isRunning = false;
|
|
return;
|
|
}
|
|
|
|
percent = (now() - _this.ts) / _this.spd;
|
|
|
|
if (_this.isPaused) {
|
|
_this.percent = percent;
|
|
_this.isRunning = false;
|
|
return;
|
|
}
|
|
|
|
if (percent < 1) {
|
|
var value = _this.st + _this.df * _this.easingMethod(percent);
|
|
_this.stepCb(value);
|
|
_this.ref(loop);
|
|
} else {
|
|
// trigger complete
|
|
_this.stepCb(_this.st + _this.df);
|
|
_this.completeCb();
|
|
|
|
_this.isRunning = false;
|
|
}
|
|
};
|
|
|
|
this.ref(loop);
|
|
}
|
|
}, {
|
|
key: 'init',
|
|
value: function init() {
|
|
this.st = 0;
|
|
this.ed = 0;
|
|
this.df = 0;
|
|
this.spd = 0;
|
|
this.ts = 0;
|
|
this.dir = 0;
|
|
this.ref = requestAnimationFrame(window);
|
|
|
|
this.isPaused = false;
|
|
this.isStopped = false;
|
|
}
|
|
}]);
|
|
return ScrollControl;
|
|
}();
|
|
|
|
function scrollTo(elm, x, y) {
|
|
var speed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 300;
|
|
var easing = arguments[4];
|
|
var scrollingComplete = arguments[5];
|
|
|
|
var scrollLeft = void 0,
|
|
scrollTop = void 0,
|
|
scrollHeight = void 0,
|
|
scrollWidth = void 0,
|
|
clientWidth = void 0,
|
|
clientHeight = void 0;
|
|
|
|
var _elm = elm,
|
|
nodeType = _elm.nodeType;
|
|
|
|
var scrollX = new ScrollControl();
|
|
var scrollY = new ScrollControl();
|
|
|
|
if (!nodeType) {
|
|
warn('You must pass a dom for the first param, ' + 'for window scrolling, ' + 'you can pass document as the first param.');
|
|
|
|
return;
|
|
}
|
|
|
|
if (nodeType == 9) {
|
|
// document
|
|
elm = elm.scrollingElement;
|
|
}
|
|
|
|
var _elm2 = elm;
|
|
scrollLeft = _elm2.scrollLeft;
|
|
scrollTop = _elm2.scrollTop;
|
|
scrollHeight = _elm2.scrollHeight;
|
|
scrollWidth = _elm2.scrollWidth;
|
|
clientWidth = _elm2.clientWidth;
|
|
clientHeight = _elm2.clientHeight;
|
|
|
|
|
|
if (typeof x === 'undefined') {
|
|
x = scrollLeft;
|
|
} else {
|
|
x = getNumericValue(x, scrollWidth - clientWidth);
|
|
}
|
|
if (typeof y === 'undefined') {
|
|
y = scrollTop;
|
|
} else {
|
|
y = getNumericValue(y, scrollHeight - clientHeight);
|
|
}
|
|
|
|
var easingMethod = createEasingFunction(easing, easingPattern);
|
|
scrollX.startScroll(scrollLeft, x, speed, function (dx) {
|
|
elm.scrollLeft = dx;
|
|
}, scrollingComplete, undefined, easingMethod);
|
|
scrollY.startScroll(scrollTop, y, speed, function (dy) {
|
|
elm.scrollTop = dy;
|
|
}, scrollingComplete, undefined, easingMethod);
|
|
}
|
|
|
|
var api$1 = {
|
|
mounted: function mounted() {
|
|
// registry scroll
|
|
this.scrollX = new ScrollControl();
|
|
this.scrollY = new ScrollControl();
|
|
},
|
|
|
|
methods: {
|
|
nativeStop: function nativeStop() {
|
|
this.scrollX.stop();
|
|
this.scrollY.stop();
|
|
},
|
|
nativePause: function nativePause() {
|
|
this.scrollX.pause();
|
|
this.scrollY.pause();
|
|
},
|
|
nativeContinue: function nativeContinue() {
|
|
this.scrollX.continue();
|
|
this.scrollY.continue();
|
|
},
|
|
nativeScrollTo: function nativeScrollTo(x, y, speed, easing) {
|
|
if (speed === false) {
|
|
|
|
} else if (typeof speed === 'undefined') {
|
|
speed = this.mergedOptions.scrollPanel.speed;
|
|
}
|
|
var elm = this.scrollPanelElm;
|
|
var scrollTop = elm.scrollTop,
|
|
scrollLeft = elm.scrollLeft,
|
|
scrollWidth = elm.scrollWidth,
|
|
clientWidth = elm.clientWidth,
|
|
scrollHeight = elm.scrollHeight,
|
|
clientHeight = elm.clientHeight;
|
|
|
|
if (typeof x === 'undefined') {
|
|
x = scrollLeft;
|
|
} else {
|
|
x = getNumericValue(x, scrollWidth - clientWidth);
|
|
}
|
|
if (typeof y === 'undefined') {
|
|
y = scrollTop;
|
|
} else {
|
|
y = getNumericValue(y, scrollHeight - clientHeight);
|
|
}
|
|
|
|
if (speed) {
|
|
easing = easing || this.mergedOptions.scrollPanel.easing;
|
|
var easingMethod = createEasingFunction(easing, easingPattern);
|
|
|
|
if (x != scrollLeft) {
|
|
this.scrollX.startScroll(scrollLeft, x, speed, function (x) {
|
|
elm.scrollLeft = x;
|
|
}, this.scrollingComplete.bind(this), undefined, easingMethod);
|
|
}
|
|
|
|
if (y != scrollTop) {
|
|
this.scrollY.startScroll(scrollTop, y, speed, function (y) {
|
|
elm.scrollTop = y;
|
|
}, this.scrollingComplete.bind(this), undefined, easingMethod);
|
|
}
|
|
} else {
|
|
elm.scrollTop = y;
|
|
elm.scrollLeft = x;
|
|
}
|
|
},
|
|
getCurrentviewDomNative: function getCurrentviewDomNative() {
|
|
var parent = this.scrollContentElm;
|
|
var domFragment = getCurrentViewportDom(parent, this.$el);
|
|
return domFragment;
|
|
}
|
|
}
|
|
};
|
|
|
|
function getPanelData(context) {
|
|
// scrollPanel data start
|
|
var data = {
|
|
ref: 'scrollPanel',
|
|
style: {
|
|
height: '100%',
|
|
overflowY: 'scroll',
|
|
overflowX: 'scroll'
|
|
},
|
|
class: [],
|
|
nativeOn: {
|
|
'&scroll': context.handleScroll
|
|
},
|
|
props: {
|
|
ops: context.mergedOptions.scrollPanel
|
|
}
|
|
};
|
|
|
|
context.scrollYEnable = true;
|
|
context.scrollXEnable = true;
|
|
|
|
data.nativeOn.DOMMouseScroll = data.nativeOn.mousewheel = context.onMouseWheel;
|
|
|
|
var _context$mergedOption = context.mergedOptions.scrollPanel,
|
|
scrollingY = _context$mergedOption.scrollingY,
|
|
scrollingX = _context$mergedOption.scrollingX;
|
|
|
|
|
|
if (!context.bar.hBar.state.size || !scrollingX) {
|
|
context.scrollXEnable = false;
|
|
data.style.overflowX = 'hidden';
|
|
}
|
|
|
|
if (!context.bar.vBar.state.size || !scrollingY) {
|
|
context.scrollYEnable = false;
|
|
data.style.overflowY = 'hidden';
|
|
}
|
|
|
|
var gutter = getGutter();
|
|
/* istanbul ignore if */
|
|
if (!gutter) {
|
|
createHideBarStyle();
|
|
data.class.push('__hidebar');
|
|
if (isIos()) {
|
|
data.style['-webkit-overflow-scrolling'] = 'touch';
|
|
}
|
|
} else {
|
|
// hide system bar by use a negative value px
|
|
// gutter should be 0 when manually disable scrollingX #14
|
|
if (context.bar.vBar.state.size && context.mergedOptions.scrollPanel.scrollingY) {
|
|
if (context.mergedOptions.scrollPanel.verticalNativeBarPos == 'right') {
|
|
data.style.marginRight = '-' + gutter + 'px';
|
|
} /* istanbul ignore next */else {
|
|
data.style.marginLeft = '-' + gutter + 'px';
|
|
}
|
|
}
|
|
if (context.bar.hBar.state.size && context.mergedOptions.scrollPanel.scrollingX) {
|
|
data.style.height = 'calc(100% + ' + gutter + 'px)';
|
|
}
|
|
}
|
|
|
|
// clear legency styles of slide mode...
|
|
data.style.transformOrigin = '';
|
|
data.style.transform = '';
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* create a scrollPanel
|
|
*
|
|
* @param {any} size
|
|
* @param {any} context
|
|
* @returns
|
|
*/
|
|
function createPanel(h, context) {
|
|
var data = {};
|
|
|
|
data = getPanelData(context);
|
|
|
|
return h(
|
|
'scrollPanel',
|
|
data,
|
|
[getPanelChildren(h, context)]
|
|
);
|
|
}
|
|
|
|
function getPanelChildren(h, context) {
|
|
var viewStyle = {
|
|
position: 'relative',
|
|
'box-sizing': 'border-box',
|
|
'min-width': '100%',
|
|
'min-height': '100%'
|
|
};
|
|
var data = {
|
|
style: viewStyle,
|
|
ref: 'scrollContent',
|
|
class: '__view'
|
|
};
|
|
var _customContent = context.$slots['scroll-content'];
|
|
|
|
if (context.mergedOptions.scrollPanel.scrollingX) {
|
|
viewStyle.width = getComplitableStyle('width', 'fit-content');
|
|
} else {
|
|
data.style['width'] = '100%';
|
|
}
|
|
|
|
if (context.mergedOptions.scrollPanel.padding) {
|
|
data.style.paddingRight = context.mergedOptions.rail.size;
|
|
}
|
|
|
|
if (_customContent) {
|
|
return insertChildrenIntoSlot(h, _customContent, context.$slots.default, data);
|
|
}
|
|
|
|
return h(
|
|
'div',
|
|
data,
|
|
[context.$slots.default]
|
|
);
|
|
}
|
|
|
|
// detect content size change
|
|
function installResizeDetection(element, callback) {
|
|
return injectObject(element, callback);
|
|
}
|
|
|
|
function injectObject(element, callback) {
|
|
if (element.hasResized) {
|
|
return;
|
|
}
|
|
|
|
var OBJECT_STYLE = 'display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;';
|
|
// define a wrap due to ie's zIndex bug
|
|
var objWrap = document.createElement('div');
|
|
objWrap.style.cssText = OBJECT_STYLE;
|
|
var object = document.createElement('object');
|
|
object.style.cssText = OBJECT_STYLE;
|
|
object.type = 'text/html';
|
|
object.tabIndex = -1;
|
|
|
|
object.onload = function () {
|
|
eventCenter(object.contentDocument.defaultView, 'resize', callback);
|
|
};
|
|
// https://github.com/wnr/element-resize-detector/blob/aafe9f7ea11d1eebdab722c7c5b86634e734b9b8/src/detection-strategy/object.js#L159
|
|
if (!isIE()) {
|
|
object.data = 'about:blank';
|
|
}
|
|
objWrap.isResizeElm = true;
|
|
objWrap.appendChild(object);
|
|
element.appendChild(objWrap);
|
|
if (isIE()) {
|
|
object.data = 'about:blank';
|
|
}
|
|
return function destroy() {
|
|
if (object.contentDocument) {
|
|
eventCenter(object.contentDocument.defaultView, 'resize', callback, 'off');
|
|
}
|
|
element.removeChild(objWrap);
|
|
element.hasResized = false;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* These mixes is exclusive for native mode
|
|
*/
|
|
|
|
var update = {
|
|
methods: {
|
|
updateNativeModeBarState: function updateNativeModeBarState() {
|
|
var container = this.scrollPanelElm;
|
|
var isPercent = this.vuescroll.state.currentSizeStrategy == 'percent';
|
|
var _vuescroll$state = this.vuescroll.state,
|
|
width = _vuescroll$state.width,
|
|
height = _vuescroll$state.height;
|
|
|
|
var clientWidth = isPercent || !width ? container.clientWidth : width.slice(0, -2); // xxxpx ==> xxx
|
|
var clientHeight = isPercent || !height ? container.clientHeight : height.slice(0, -2);
|
|
|
|
var heightPercentage = clientHeight / container.scrollHeight;
|
|
var widthPercentage = clientWidth / container.scrollWidth;
|
|
|
|
this.bar.vBar.state.posValue = container.scrollTop * 100 / clientHeight;
|
|
this.bar.hBar.state.posValue = container.scrollLeft * 100 / clientWidth;
|
|
|
|
this.bar.vBar.state.size = heightPercentage < 1 ? heightPercentage : 0;
|
|
this.bar.hBar.state.size = widthPercentage < 1 ? widthPercentage : 0;
|
|
},
|
|
getNativePosition: function getNativePosition() {
|
|
return {
|
|
scrollTop: this.scrollPanelElm.scrollTop,
|
|
scrollLeft: this.scrollPanelElm.scrollLeft
|
|
};
|
|
},
|
|
css: function css(dom, style) /* istanbul ignore next */{
|
|
return window.getComputedStyle(dom)[style];
|
|
},
|
|
checkScrollable: function checkScrollable(e, dir, delta) /* istanbul ignore next */{
|
|
var scrollable = false;
|
|
|
|
// check mouse point scrollable.
|
|
var dom = e.target ? e.target : e;
|
|
while (dom && dom.nodeType == 1 && dom !== this.scrollPanelElm.parentNode && !/^BODY|HTML/.test(dom.nodeName)) {
|
|
var ov = (dir == 'dy' ? this.css(dom, 'overflowY') : this.css(dom, 'overflowX')) || this.css(dom, 'overflow') || '';
|
|
if (/scroll|auto/.test(ov)) {
|
|
var _getScrollProcess = this.getScrollProcess(dom),
|
|
v = _getScrollProcess.v,
|
|
h = _getScrollProcess.h;
|
|
|
|
if (dir == 'dx' && (delta < 0 && h > 0 || delta > 0 && h < 1) || dir == 'dy' && (delta < 0 && v > 0 || delta > 0 && v < 1)) {
|
|
scrollable = dom == this.scrollPanelElm;
|
|
break;
|
|
}
|
|
}
|
|
dom = dom.parentNode ? dom.parentNode : false;
|
|
}
|
|
|
|
return scrollable;
|
|
},
|
|
onMouseWheel: function onMouseWheel(event) /* istanbul ignore next */{
|
|
var duration = this.mergedOptions.vuescroll.wheelScrollDuration;
|
|
var isReverse = this.mergedOptions.vuescroll.wheelDirectionReverse;
|
|
|
|
var delta = 0;
|
|
var dir = void 0;
|
|
if (event.wheelDelta) {
|
|
if (event.deltaY) {
|
|
dir = 'dy';
|
|
delta = event.deltaY;
|
|
} else if (event.deltaX) {
|
|
delta = event.deltaX;
|
|
dir = 'dx';
|
|
} else {
|
|
delta = -1 * event.wheelDelta / 2;
|
|
}
|
|
} else if (event.detail) {
|
|
// horizontal scroll
|
|
if (event.axis == 1) {
|
|
dir = 'dx';
|
|
} else if (event.axis == 2) {
|
|
// vertical scroll
|
|
dir = 'dy';
|
|
}
|
|
delta = event.detail * 16;
|
|
}
|
|
|
|
if (event.shiftKey) {
|
|
dir = 'dx';
|
|
} else {
|
|
dir = 'dy';
|
|
}
|
|
|
|
if (isReverse) {
|
|
dir = dir == 'dx' ? 'dy' : 'dx';
|
|
}
|
|
|
|
if (this.checkScrollable(event, dir, delta)) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.scrollBy(defineProperty({}, dir, delta), duration);
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
scrollContentElm: function scrollContentElm() {
|
|
return this.$refs['scrollContent']._isVue ? this.$refs['scrollContent'].$el : this.$refs['scrollContent'];
|
|
}
|
|
}
|
|
};
|
|
|
|
var mixins = [api$1, update];
|
|
|
|
var core = {
|
|
mixins: mixins,
|
|
methods: {
|
|
destroy: function destroy() {
|
|
/* istanbul ignore next */
|
|
if (this.destroyResize) {
|
|
this.destroyResize();
|
|
}
|
|
},
|
|
getCurrentviewDom: function getCurrentviewDom() {
|
|
return this.getCurrentviewDomNative();
|
|
},
|
|
internalScrollTo: function internalScrollTo(destX, destY, animate, easing) {
|
|
this.nativeScrollTo(destX, destY, animate, easing);
|
|
},
|
|
internalStop: function internalStop() {
|
|
this.nativeStop();
|
|
},
|
|
internalPause: function internalPause() {
|
|
this.nativePause();
|
|
},
|
|
internalContinue: function internalContinue() {
|
|
this.nativeContinue();
|
|
},
|
|
handleScroll: function handleScroll(nativeEvent) {
|
|
this.updateBarStateAndEmitEvent('handle-scroll', nativeEvent);
|
|
},
|
|
updateBarStateAndEmitEvent: function updateBarStateAndEmitEvent(eventType) {
|
|
var nativeEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
|
|
this.updateNativeModeBarState();
|
|
if (eventType) {
|
|
this.emitEvent(eventType, nativeEvent);
|
|
}
|
|
if (this.mergedOptions.bar.onlyShowBarOnScroll) {
|
|
if (eventType == 'handle-scroll' || eventType == 'handle-resize' || eventType == 'refresh-status' || eventType == 'window-resize' || eventType == 'options-change') {
|
|
this.showAndDefferedHideBar(true /* forceHideBar: true */);
|
|
}
|
|
} else {
|
|
this.showAndDefferedHideBar();
|
|
}
|
|
},
|
|
getScrollProcess: function getScrollProcess(elm) {
|
|
var _ref = elm || this.scrollPanelElm,
|
|
scrollHeight = _ref.scrollHeight,
|
|
scrollWidth = _ref.scrollWidth,
|
|
clientHeight = _ref.clientHeight,
|
|
clientWidth = _ref.clientWidth,
|
|
scrollTop = _ref.scrollTop,
|
|
scrollLeft = _ref.scrollLeft;
|
|
|
|
var v = Math.min(scrollTop / (scrollHeight - clientHeight || 1), 1);
|
|
var h = Math.min(scrollLeft / (scrollWidth - clientWidth || 1), 1);
|
|
|
|
return {
|
|
v: v,
|
|
h: h
|
|
};
|
|
},
|
|
emitEvent: function emitEvent(eventType) {
|
|
var nativeEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
var _scrollPanelElm = this.scrollPanelElm,
|
|
scrollTop = _scrollPanelElm.scrollTop,
|
|
scrollLeft = _scrollPanelElm.scrollLeft;
|
|
|
|
|
|
var vertical = {
|
|
type: 'vertical'
|
|
};
|
|
var horizontal = {
|
|
type: 'horizontal'
|
|
};
|
|
|
|
var _getScrollProcess = this.getScrollProcess(),
|
|
v = _getScrollProcess.v,
|
|
h = _getScrollProcess.h;
|
|
|
|
vertical.process = v;
|
|
horizontal.process = h;
|
|
|
|
vertical['barSize'] = this.bar.vBar.state.size;
|
|
horizontal['barSize'] = this.bar.hBar.state.size;
|
|
vertical['scrollTop'] = scrollTop;
|
|
horizontal['scrollLeft'] = scrollLeft;
|
|
|
|
this.$emit(eventType, vertical, horizontal, nativeEvent);
|
|
},
|
|
initVariables: function initVariables() {
|
|
this.$el._isVuescroll = true;
|
|
},
|
|
refreshInternalStatus: function refreshInternalStatus() {
|
|
// 1.set vuescroll height or width according to
|
|
// sizeStrategy
|
|
this.setVsSize();
|
|
// 2. registry resize event
|
|
this.registryResize();
|
|
// 3. update scrollbar's height/width
|
|
this.updateBarStateAndEmitEvent('refresh-status');
|
|
},
|
|
registryResize: function registryResize() {
|
|
var _this = this;
|
|
|
|
var resizeEnable = this.mergedOptions.vuescroll.detectResize;
|
|
|
|
/* istanbul ignore next */
|
|
if (this.destroyResize && resizeEnable) {
|
|
return;
|
|
}
|
|
|
|
if (this.destroyResize) {
|
|
this.destroyResize();
|
|
}
|
|
|
|
if (!resizeEnable) {
|
|
return;
|
|
}
|
|
|
|
var contentElm = this.scrollContentElm;
|
|
|
|
var vm = this;
|
|
var handleWindowResize = function handleWindowResize() /* istanbul ignore next */{
|
|
vm.updateBarStateAndEmitEvent('window-resize');
|
|
};
|
|
var handleDomResize = function handleDomResize() {
|
|
var currentSize = {};
|
|
currentSize['width'] = _this.scrollPanelElm.scrollWidth;
|
|
currentSize['height'] = _this.scrollPanelElm.scrollHeight;
|
|
_this.updateBarStateAndEmitEvent('handle-resize', currentSize);
|
|
|
|
// Since content sie changes, we should tell parent to set
|
|
// correct size to fit content's size
|
|
_this.setVsSize();
|
|
};
|
|
window.addEventListener('resize', handleWindowResize, false);
|
|
|
|
var destroyDomResize = installResizeDetection(contentElm, handleDomResize);
|
|
|
|
var destroyWindowResize = function destroyWindowResize() {
|
|
window.removeEventListener('resize', handleWindowResize, false);
|
|
};
|
|
|
|
this.destroyResize = function () {
|
|
destroyWindowResize();
|
|
destroyDomResize();
|
|
|
|
_this.destroyResize = null;
|
|
};
|
|
},
|
|
getPosition: function getPosition() {
|
|
return this.getNativePosition();
|
|
}
|
|
}
|
|
};
|
|
|
|
var config = {
|
|
vuescroll: {
|
|
wheelScrollDuration: 0,
|
|
wheelDirectionReverse: false
|
|
}
|
|
};
|
|
|
|
var component = _install(core, createPanel, [config]);
|
|
|
|
function install(Vue$$1) {
|
|
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
|
|
Vue$$1.component(opts.name || component.name, component);
|
|
Vue$$1.prototype.$vuescrollConfig = opts.ops || {};
|
|
}
|
|
|
|
var Vuescroll = _extends({
|
|
install: install,
|
|
version: '4.16.1',
|
|
refreshAll: refreshAll,
|
|
scrollTo: scrollTo
|
|
}, component);
|
|
|
|
/* istanbul ignore if */
|
|
if (typeof window !== 'undefined' && window.Vue) {
|
|
window.Vue.use(Vuescroll);
|
|
}
|
|
|
|
return Vuescroll;
|
|
|
|
})));
|
|
|