/* * Vuescroll v4.18.1 * (c) 2018-2023 Yi(Yves) Wang * Released under the MIT License * Github: https://github.com/YvesCoding/vuescroll * Website: http://vuescrolljs.yvescoding.me/ */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) : typeof define === 'function' && define.amd ? define(['vue'], factory) : (global = global || self, global.vuescroll = factory(global.Vue)); }(this, (function (Vue) { 'use strict'; Vue = Vue && Object.prototype.hasOwnProperty.call(Vue, '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; }(); /** * ZoomManager * Get the browser zoom ratio */ var ZoomManager = function () { function ZoomManager() { var _this = this; classCallCheck(this, ZoomManager); this.originPixelRatio = this.getRatio(); this.lastPixelRatio = this.originPixelRatio; window.addEventListener('resize', function () { _this.lastPixelRatio = _this.getRatio(); }); } createClass(ZoomManager, [{ key: 'getRatio', value: function getRatio() { var ratio = 0; var screen = window.screen; var ua = navigator.userAgent.toLowerCase(); if (window.devicePixelRatio !== undefined) { ratio = window.devicePixelRatio; } else if (~ua.indexOf('msie')) { if (screen.deviceXDPI && screen.logicalXDPI) { ratio = screen.deviceXDPI / screen.logicalXDPI; } } else if (window.outerWidth !== undefined && window.innerWidth !== undefined) { ratio = window.outerWidth / window.innerWidth; } if (ratio) { ratio = Math.round(ratio * 100); } return ratio; } }, { key: 'getRatioBetweenPreAndCurrent', value: function getRatioBetweenPreAndCurrent() { return this.originPixelRatio / this.lastPixelRatio; } }]); return ZoomManager; }(); 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() { return source[souceKey]; }, configurable: true }); } 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 error = function error(msg) { console.error('[vuescroll] ' + msg); }; 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); } // create slide mode style function createSlideModeStyle() { var cssText = '\n @-webkit-keyframes loading-rotate {\n to {\n -webkit-transform: rotate(1turn);\n transform: rotate(1turn);\n }\n }\n\n @keyframes loading-rotate {\n to {\n -webkit-transform: rotate(1turn);\n transform: rotate(1turn);\n }\n }\n\n @-webkit-keyframes loading-wipe {\n 0% {\n stroke-dasharray: 1, 200;\n stroke-dashoffset: 0;\n }\n 50% {\n stroke-dasharray: 90, 150;\n stroke-dashoffset: -40px;\n }\n to {\n stroke-dasharray: 90, 150;\n stroke-dashoffset: -120px;\n }\n }\n\n @keyframes loading-wipe {\n 0% {\n stroke-dasharray: 1, 200;\n stroke-dashoffset: 0;\n }\n 50% {\n stroke-dasharray: 90, 150;\n stroke-dashoffset: -40px;\n }\n to {\n stroke-dasharray: 90, 150;\n stroke-dashoffset: -120px;\n }\n }\n\n .__vuescroll .__refresh,\n .__vuescroll .__load {\n position: absolute;\n width: 100%;\n color: black;\n height: 50px;\n line-height: 50px;\n text-align: center;\n font-size: 16px;\n }\n .__vuescroll .__refresh svg,\n .__vuescroll .__load svg {\n margin-right: 10px;\n width: 25px;\n height: 25px;\n vertical-align: sub;\n }\n .__vuescroll .__refresh svg.active,\n .__vuescroll .__load svg.active {\n transition: all 0.2s;\n }\n .__vuescroll .__refresh svg.active.deactive,\n .__vuescroll .__load svg.active.deactive {\n transform: rotateZ(180deg);\n }\n .__vuescroll .__refresh svg path,\n .__vuescroll .__refresh svg rect,\n .__vuescroll .__load svg path,\n .__vuescroll .__load svg rect {\n fill: #20a0ff;\n }\n .__vuescroll .__refresh svg.start,\n .__vuescroll .__load svg.start {\n stroke: #343640;\n stroke-width: 4;\n stroke-linecap: round;\n -webkit-animation: loading-rotate 2s linear infinite;\n animation: loading-rotate 2s linear infinite;\n }\n .__vuescroll .__refresh svg.start .bg-path,\n .__vuescroll .__load svg.start .bg-path {\n stroke: #f2f2f2;\n fill: none;\n }\n .__vuescroll .__refresh svg.start .active-path,\n .__vuescroll .__load svg.start .active-path {\n stroke: #20a0ff;\n fill: none;\n stroke-dasharray: 90, 150;\n stroke-dashoffset: 0;\n -webkit-animation: loading-wipe 1.5s ease-in-out infinite;\n animation: loading-wipe 1.5s ease-in-out infinite;\n }\n '; createStyle('vuescroll-silde-mode-style', cssText); } 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, /** Enable locking to the main axis if user moves only slightly on one of them at start */ locking: 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 var NOOP$1 = function NOOP() {}; // 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 __REFRESH_DOM_NAME = 'refreshDom'; var __LOAD_DOM_NAME = 'loadDom'; 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); } function getPanelData(context) { // scrollPanel data start var data = { ref: 'scrollPanel', style: { 'user-select': 'none', '-webkit-user-select': 'none', 'min-width': '100%', 'min-height': '100%' }, class: [], props: { ops: context.mergedOptions.scrollPanel } }; data.class.push('__slide'); /* istanbul ignore if */ if (isIos()) { data.style = defineProperty({}, '-webkit-overflow-scrolling', 'touch'); } if (context.mergedOptions.vuescroll.renderMethod == 'transform') { data.style['transform-origin'] = 'left top 0px'; } var _context$mergedOption = context.mergedOptions.scrollPanel, scrollingX = _context$mergedOption.scrollingX, scrollingY = _context$mergedOption.scrollingY, padding = _context$mergedOption.padding; if (scrollingX && !context.refreshLoad) { var width = getComplitableStyle('width', 'fit-content'); if (width) { data.style['width'] = width; } /* istanbul ignore next */else { data['display'] = 'inline-block'; } } /* istanbul ignore if */ if (!scrollingX) { data.class.push('x-hidden'); } /* istanbul ignore if */ if (!scrollingY) { data.class.push('y-hidden'); } if (padding) { data.style.paddingRight = context.mergedOptions.rail.size; } return data; } function getPanelChildren(h, context) { var renderChildren = getVnodeInfo(context.$slots['scroll-panel']).ch || context.$slots.default; var finalChildren = []; /* istanbul ignore if */ if (!renderChildren) { context.$slots.default = renderChildren = []; } // handle refresh if (context.mergedOptions.vuescroll.pullRefresh.enable) { finalChildren.push(h( 'div', { 'class': '__refresh', style: { visibility: context.refrehDomVisiable ? '' : 'hidden' }, ref: __REFRESH_DOM_NAME, key: __REFRESH_DOM_NAME }, [createTipDom(h, context, 'refresh', context.pullRefreshTip)] )); } finalChildren.push(renderChildren); // handle load if (context.mergedOptions.vuescroll.pushLoad.enable) { finalChildren.push(h( 'div', { ref: __LOAD_DOM_NAME, key: __LOAD_DOM_NAME, 'class': '__load', style: { visibility: context.loadDomVisiable ? '' : 'hidden' } }, [createTipDom(h, context, 'load', context.pushLoadTip)] )); } return finalChildren; } // Create load or refresh tip dom of each stages function createTipDom(h, context, type, tip) { var stage = context.vuescroll.state[type + 'Stage']; var dom = null; // Return user specified animation dom /* istanbul ignore if */ if (dom = context.$slots[type + '-' + stage]) { return dom; } switch (stage) { // The dom will show at deactive stage case 'deactive': case 'active': { var className = 'active'; if (stage == 'deactive') { className += ' deactive'; } dom = h( 'svg', { 'class': className, attrs: { version: '1.1', xmlns: 'http://www.w3.org/2000/svg', xmlnsXlink: 'http://www.w3.org/1999/xlink', x: '0px', y: '0px', viewBox: '0 0 1000 1000', 'enable-background': 'new 0 0 1000 1000', xmlSpace: 'preserve' } }, [h('metadata', [' Svg Vector Icons : http://www.sfont.cn ']), h('g', [h( 'g', { attrs: { transform: 'matrix(1 0 0 -1 0 1008)' } }, [h('path', { attrs: { d: 'M10,543l490,455l490-455L885,438L570,735.5V18H430v717.5L115,438L10,543z' } })] )])] ); } break; case 'start': dom = h( 'svg', { attrs: { viewBox: '0 0 50 50' }, 'class': 'start' }, [h('circle', { attrs: { stroke: 'true', cx: '25', cy: '25', r: '20' }, 'class': 'bg-path' }), h('circle', { attrs: { cx: '25', cy: '25', r: '20' }, 'class': 'active-path' })] ); break; case 'beforeDeactive': dom = h( 'svg', { attrs: { viewBox: '0 0 1024 1024', version: '1.1', xmlns: 'http://www.w3.org/2000/svg', 'p-id': '3562' } }, [h('path', { attrs: { d: 'M512 0C229.706831 0 0 229.667446 0 512s229.667446 512 512 512c282.293169 0 512-229.667446 512-512S794.332554 0 512 0z m282.994215 353.406031L433.2544 715.145846a31.484062 31.484062 0 0 1-22.275938 9.231754h-0.4096a31.586462 31.586462 0 0 1-22.449231-9.814646L228.430769 546.327631a31.507692 31.507692 0 0 1 45.701908-43.386093l137.4208 144.785724L750.442338 308.854154a31.507692 31.507692 0 1 1 44.551877 44.551877z', fill: '', 'p-id': '3563' } })] ); break; } return [dom, tip]; } /** * create a scrollPanel * * @param {any} size * @param {any} context * @returns */ function createPanel(h, context) { var data = getPanelData(context); return h( 'scrollPanel', data, [getPanelChildren(h, context)] ); } // 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; }; } var api$1 = { methods: { slideScrollTo: function slideScrollTo(x, y, speed, easing) { var _getPosition = this.getPosition(), scrollLeft = _getPosition.scrollLeft, scrollTop = _getPosition.scrollTop; x = getNumericValue(x || scrollLeft, this.scroller.__maxScrollLeft); y = getNumericValue(y || scrollTop, this.scroller.__maxScrollTop); this.scroller.scrollTo(x, y, speed > 0, undefined, false, speed, easing); }, zoomBy: function zoomBy(factor, animate, originLeft, originTop, callback) { if (!this.scroller) { warn('zoomBy and zoomTo are only for slide mode!'); return; } this.scroller.zoomBy(factor, animate, originLeft, originTop, callback); }, zoomTo: function zoomTo(level) { var animate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var originLeft = arguments[2]; var originTop = arguments[3]; var callback = arguments[4]; if (!this.scroller) { warn('zoomBy and zoomTo are only for slide mode!'); return; } this.scroller.zoomTo(level, animate, originLeft, originTop, callback); }, getCurrentPage: function getCurrentPage() { if (!this.scroller || !this.mergedOptions.vuescroll.paging) { warn('getCurrentPage and goToPage are only for slide mode and paging is enble!'); return; } return this.scroller.getCurrentPage(); }, goToPage: function goToPage(dest) { var animate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!this.scroller || !this.mergedOptions.vuescroll.paging) { warn('getCurrentPage and goToPage are only for slide mode and paging is enble!'); return; } this.scroller.goToPage(dest, animate); }, triggerRefreshOrLoad: function triggerRefreshOrLoad(type) { if (!this.scroller) { warn('You can only use triggerRefreshOrLoad in slide mode!'); return; } var isRefresh = this.mergedOptions.vuescroll.pullRefresh.enable; var isLoad = this.mergedOptions.vuescroll.pushLoad.enable; if (type == 'refresh' && !isRefresh) { warn('refresh must be enabled!'); return; } else if (type == 'load' && !isLoad) { warn('load must be enabled and content\'s height > container\'s height!'); return; } else if (type !== 'refresh' && type !== 'load') { warn('param must be one of load and refresh!'); return; } /* istanbul ignore if */ if (this.vuescroll.state[type + 'Stage'] == 'start') { return; } this.scroller.triggerRefreshOrLoad(type); return true; }, getCurrentviewDomSlide: function getCurrentviewDomSlide() { var parent = this.scrollPanelElm; var domFragment = getCurrentViewportDom(parent, this.$el); return domFragment; } } }; /* * Scroller * http://github.com/zynga/scroller * * Copyright 2011, Zynga Inc. * Licensed under the MIT License. * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt * * Based on the work of: Unify Project (unify-project.org) * http://unify-project.org * Copyright 2011, Deutsche Telekom AG * License: MIT + Apache (V2) */ var time = Date.now || function () { return +new Date(); }; var desiredFrames = 60; var millisecondsPerSecond = 1000; var running = {}; var counter = 1; var core = { effect: {} }; var global = null; if (typeof window !== 'undefined') { global = window; } else { global = {}; } core.effect.Animate = { /** * A requestAnimationFrame wrapper / polyfill. * * @param callback {Function} The callback to be invoked before the next repaint. * @param root {HTMLElement} The root element for the repaint */ requestAnimationFrame: requestAnimationFrame(global), /** * Stops the given animation. * * @param id {Integer} Unique animation ID * @return {Boolean} Whether the animation was stopped (aka, was running before) */ stop: function stop(id) { var cleared = running[id] != null; if (cleared) { running[id] = null; } return cleared; }, /** * Whether the given animation is still running. * * @param id {Integer} Unique animation ID * @return {Boolean} Whether the animation is still running */ isRunning: function isRunning(id) { return running[id] != null; }, /** * Start the animation. * * @param stepCallback {Function} Pointer to function which is executed on every step. * Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }` * @param verifyCallback {Function} Executed before every animation step. * Signature of the method should be `function() { return continueWithAnimation; }` * @param completedCallback {Function} * Signature of the method should be `function(droppedFrames, finishedAnimation) {}` * @param duration {Integer} Milliseconds to run the animation * @param easingMethod {Function} Pointer to easing function * Signature of the method should be `function(percent) { return modifiedValue; }` * @param root {Element ? document.body} Render root, when available. Used for internal * usage of requestAnimationFrame. * @return {Integer} Identifier of animation. Can be used to stop it any time. */ start: function start(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) { var start = time(); var lastFrame = start; var percent = 0; var dropCounter = 0; var id = counter++; if (!root) { root = document.body; } // Compacting running db automatically every few new animations if (id % 20 === 0) { var newRunning = {}; for (var usedId in running) { newRunning[usedId] = true; } running = newRunning; } // This is the internal step method which is called every few milliseconds var step = function step(virtual) { // Normalize virtual value var render = virtual !== true; // Get current time var now = time(); // Verification is executed before next animation step if (!running[id] || verifyCallback && !verifyCallback(id)) { running[id] = null; completedCallback && completedCallback(desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), id, false); return; } // For the current rendering to apply let's update omitted steps in memory. // This is important to bring internal state variables up-to-date with progress in time. if (render) { var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1; for (var j = 0; j < Math.min(droppedFrames, 4); j++) { step(true); dropCounter++; } } if (!running[id]) { return; } // Compute percent value if (duration) { percent = (now - start) / duration; if (percent > 1) { percent = 1; } } // Execute step callback, then... var value = easingMethod ? easingMethod(percent) : percent; if ((stepCallback(value, now, render) === false || percent === 1) && render) { running[id] = null; completedCallback && completedCallback(desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), id, percent === 1 || duration == null); } else if (render) { lastFrame = now; core.effect.Animate.requestAnimationFrame(step, root); } }; // Mark as running running[id] = true; // Init first step core.effect.Animate.requestAnimationFrame(step, root); // Return unique animation ID return id; } }; /* * Scroller * http://github.com/zynga/scroller * * modified by wangyi7099 * * Copyright 2011, Zynga Inc. * Licensed under the MIT License. * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt * * Based on the work of: Unify Project (unify-project.org) * http://unify-project.org * Copyright 2011, Deutsche Telekom AG * License: MIT + Apache (V2) */ var animatingMethod = null; var noAnimatingMethod = null; function Scroller(callback, options) { this.__callback = callback; this.options = { /** Enable scrolling on x-axis */ scrollingX: true, /** Enable scrolling on y-axis */ scrollingY: true, /** Enable animations for deceleration, snap back, zooming and scrolling */ animating: true, /** duration for animations triggered by scrollTo/zoomTo */ animationDuration: 250, /** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */ bouncing: { top: 100, bottom: 100, left: 100, right: 100 }, /** Enable locking to the main axis if user moves only slightly on one of them at start */ locking: true, /** Enable pagination mode (switching between full page content panes) */ paging: false, /** Enable snapping of content to a configured pixel grid */ snapping: false, /** Enable zooming of content via API, fingers and mouse wheel */ zooming: false, /** Minimum zoom level */ minZoom: 0.5, /** Maximum zoom level */ maxZoom: 3, /** Multiply or decrease scrolling speed **/ speedMultiplier: 1, /** Callback that is fired on the later of touch end or deceleration end, provided that another scrolling action has not begun. Used to know when to fade out a scrollbar. */ scrollingComplete: NOOP$1, animatingEasing: 'easeOutCubic', noAnimatingEasing: 'easeInOutCubic', /** This configures the amount of change applied to deceleration when reaching boundaries **/ penetrationDeceleration: 0.03, /** This configures the amount of change applied to acceleration when reaching boundaries **/ penetrationAcceleration: 0.08 }; for (var key in options) { this.options[key] = options[key]; } animatingMethod = createEasingFunction(this.options.animatingEasing, easingPattern); noAnimatingMethod = createEasingFunction(this.options.noAnimatingEasing, easingPattern); } var members = { /* --------------------------------------------------------------------------- INTERNAL FIELDS :: STATUS --------------------------------------------------------------------------- */ /** {Boolean} Whether only a single finger is used in touch handling */ __isSingleTouch: false, /** {Boolean} Whether a touch event sequence is in progress */ __isTracking: false, /** {Boolean} Whether a deceleration animation went to completion. */ __didDecelerationComplete: false, /** * {Boolean} Whether a gesture zoom/rotate event is in progress. Activates when * a gesturestart event happens. This has higher priority than dragging. */ __isGesturing: false, /** * {Boolean} Whether the user has moved by such a distance that we have enabled * dragging mode. Hint: It's only enabled after some pixels of movement to * not interrupt with clicks etc. */ __isDragging: false, /** * {Boolean} Not touching and dragging anymore, and smoothly animating the * touch sequence using deceleration. */ __isDecelerating: false, /** * {Boolean} Smoothly animating the currently configured change */ __isAnimating: false, /* --------------------------------------------------------------------------- INTERNAL FIELDS :: DIMENSIONS --------------------------------------------------------------------------- */ /** {Integer} Available outer left position (from document perspective) */ __clientLeft: 0, /** {Integer} Available outer top position (from document perspective) */ __clientTop: 0, /** {Integer} Available outer width */ __clientWidth: 0, /** {Integer} Available outer height */ __clientHeight: 0, /** {Integer} Outer width of content */ __contentWidth: 0, /** {Integer} Outer height of content */ __contentHeight: 0, /** {Integer} Snapping width for content */ __snapWidth: 100, /** {Integer} Snapping height for content */ __snapHeight: 100, /** {Integer} Height to assign to refresh area */ __refreshHeight: null, /** {Integer} Height to assign to refresh area */ __loadHeight: null, /** {Boolean} Whether the refresh process is enabled when the event is released now */ __refreshActive: false, /** {Function} Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release */ __refreshActivate: null, __refreshBeforeDeactivate: null, /** {Function} Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled */ __refreshDeactivate: null, /** {Function} Callback to execute to start the actual refresh. Call {@link #refreshFinish} when done */ __refreshStart: null, __loadActive: null, __loadActivate: null, __loadBeforeDeactivate: null, __loadDeactivate: null, __loadStart: null, /** {Number} Zoom level */ __zoomLevel: 1, /** {Number} Scroll position on x-axis */ __scrollLeft: 0, /** {Number} Scroll position on y-axis */ __scrollTop: 0, /** {Integer} Maximum allowed scroll position on x-axis */ __maxScrollLeft: 0, /** {Integer} Maximum allowed scroll position on y-axis */ __maxScrollTop: 0, /* {Number} Scheduled left position (final position when animating) */ __scheduledLeft: 0, /* {Number} Scheduled top position (final position when animating) */ __scheduledTop: 0, /* {Number} Scheduled zoom level (final scale when animating) */ __scheduledZoom: 0, /** * current page */ __currentPageX: null, __currentPageY: null, /** * total page */ __totalXPage: null, __totalYPage: null, /* --------------------------------------------------------------------------- INTERNAL FIELDS :: LAST POSITIONS --------------------------------------------------------------------------- */ /** whether the scroller is disabled or not */ __disable: false, /** {Number} Left position of finger at start */ __lastTouchLeft: null, /** {Number} Top position of finger at start */ __lastTouchTop: null, /** {Date} Timestamp of last move of finger. Used to limit tracking range for deceleration speed. */ __lastTouchMove: null, /** {Array} List of positions, uses three indexes for each state: left, top, timestamp */ __positions: null, /* --------------------------------------------------------------------------- INTERNAL FIELDS :: DECELERATION SUPPORT --------------------------------------------------------------------------- */ /** {Integer} Minimum left scroll position during deceleration */ __minDecelerationScrollLeft: null, /** {Integer} Minimum top scroll position during deceleration */ __minDecelerationScrollTop: null, /** {Integer} Maximum left scroll position during deceleration */ __maxDecelerationScrollLeft: null, /** {Integer} Maximum top scroll position during deceleration */ __maxDecelerationScrollTop: null, /** {Number} Current factor to modify horizontal scroll position with on every step */ __decelerationVelocityX: null, /** {Number} Current factor to modify vertical scroll position with on every step */ __decelerationVelocityY: null, /* --------------------------------------------------------------------------- PUBLIC API --------------------------------------------------------------------------- */ /** * Configures the dimensions of the client (outer) and content (inner) elements. * Requires the available space for the outer element and the outer size of the inner element. * All values which are falsy (null or zero etc.) are ignored and the old value is kept. * * @param clientWidth {Integer ? null} Inner width of outer element * @param clientHeight {Integer ? null} Inner height of outer element * @param contentWidth {Integer ? null} Outer width of inner element * @param contentHeight {Integer ? null} Outer height of inner element */ setDimensions: function setDimensions(clientWidth, clientHeight, contentWidth, contentHeight, animate) { var noScroll = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; var self = this; // Only update values which are defined if (clientWidth === +clientWidth) { self.__clientWidth = clientWidth; } if (clientHeight === +clientHeight) { self.__clientHeight = clientHeight; } if (contentWidth === +contentWidth) { self.__contentWidth = contentWidth; } if (contentHeight === +contentHeight) { self.__contentHeight = contentHeight; } // Refresh maximums self.__computeScrollMax(); if (!noScroll) { // Refresh scroll position self.scrollTo(self.__scrollLeft, self.__scrollTop, animate); } }, /** * Sets the client coordinates in relation to the document. * * @param left {Integer ? 0} Left position of outer element * @param top {Integer ? 0} Top position of outer element */ setPosition: function setPosition(left, top) { var self = this; self.__clientLeft = left || 0; self.__clientTop = top || 0; }, /** * Configures the snapping (when snapping is active) * * @param width {Integer} Snapping width * @param height {Integer} Snapping height */ setSnapSize: function setSnapSize(width, height) { var self = this; self.__snapWidth = width; self.__snapHeight = height; }, /** * Activates pull-to-refresh. A special zone on the top of the list to start a list refresh whenever * the user event is released during visibility of this zone. This was introduced by some apps on iOS like * the official Twitter client. * * @param height {Integer} Height of pull-to-refresh zone on top of rendered list * @param activateCallback {Function} Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release. * @param deactivateCallback {Function} Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled. * @param startCallback {Function} Callback to execute to start the real async refresh action. Call {@link #finishPullToRefresh} after finish of refresh. */ activatePullToRefresh: function activatePullToRefresh(height, _ref) { var activateCallback = _ref.activateCallback, deactivateCallback = _ref.deactivateCallback, startCallback = _ref.startCallback, beforeDeactivateCallback = _ref.beforeDeactivateCallback, beforeDeactiveEnd = _ref.beforeDeactiveEnd; var self = this; self.__refreshHeight = height; self.__refreshActivate = activateCallback; self.__refreshBeforeDeactivate = beforeDeactivateCallback; self.__refreshBeforeDeactiveEnd = beforeDeactiveEnd; self.__refreshDeactivate = deactivateCallback; self.__refreshStart = startCallback; }, activatePushToLoad: function activatePushToLoad(height, _ref2) { var activateCallback = _ref2.activateCallback, deactivateCallback = _ref2.deactivateCallback, startCallback = _ref2.startCallback, beforeDeactivateCallback = _ref2.beforeDeactivateCallback, beforeDeactiveEnd = _ref2.beforeDeactiveEnd; var self = this; self.__loadHeight = height; self.__loadActivate = activateCallback; self.__loadBeforeDeactivate = beforeDeactivateCallback; self.__loadBeforeDeactiveEnd = beforeDeactiveEnd; self.__loadDeactivate = deactivateCallback; self.__loadStart = startCallback; }, /** * Starts pull-to-refresh manually. */ triggerRefreshOrLoad: function triggerRefreshOrLoad() { var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'refresh'; var wasDecelerating = this.__isDecelerating; if (wasDecelerating) { core.effect.Animate.stop(wasDecelerating); this.__isDecelerating = false; } // Use publish instead of scrollTo to allow scrolling to out of boundary position // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled if (type == 'refresh') { if (this.__refreshActive || this.__refreshBeforeDeactiveStarted) return; this.__publish(this.__scrollLeft, -this.__refreshHeight, this.__zoomLevel, true); if (this.__refreshStart) { this.__refreshStart(); this.__refreshActive = true; } } else if (type == 'load') { if (this.__loadActive || this.__loadBeforeDeactiveStarted) return; this.__publish(this.__scrollLeft, this.__maxScrollTop + this.__loadHeight, this.__zoomLevel, true); if (this.__loadStart) { this.__loadStart(); this.__loadActive = true; } } }, /** * Signalizes that pull-to-refresh is finished. */ finishRefreshOrLoad: function finishRefreshOrLoad() { var self = this; if (self.__refreshActive) { self.__refreshActive = false; var endRefreshActive = function endRefreshActive() { if (self.__refreshBeforeDeactiveEnd) { self.__refreshBeforeDeactiveEnd(); } self.__refreshBeforeDeactiveStarted = true; self.scrollTo(self.__scrollLeft, self.__scrollTop, true); }; if (self.__refreshBeforeDeactivate) { self.__refreshBeforeDeactivate(endRefreshActive); } else { endRefreshActive(); } } if (self.__loadActive) { self.__loadActive = false; var endLoadActive = function endLoadActive() { if (self.__loadBeforeDeactiveEnd) { self.__loadBeforeDeactiveEnd(); } self.__loadBeforeDeactiveStarted = true; self.scrollTo(self.__scrollLeft, self.__scrollTop, true); }; if (self.__loadBeforeDeactivate) { self.__loadBeforeDeactivate(endLoadActive); } else { endLoadActive(); } } }, /** * Returns the scroll position and zooming values * * @return {Map} `left` and `top` scroll position and `zoom` level */ getValues: function getValues() { var self = this; return { left: self.__scrollLeft, top: self.__scrollTop, zoom: self.__zoomLevel }; }, /** * Returns the maximum scroll values * * @return {Map} `left` and `top` maximum scroll values */ getScrollMax: function getScrollMax() { var self = this; return { left: self.__maxScrollLeft, top: self.__maxScrollTop }; }, /** * Zooms to the given level. Supports optional animation. Zooms * the center when no coordinates are given. * * @param level {Number} Level to zoom to * @param animate {Boolean ? false} Whether to use animation * @param originLeft {Number ? null} Zoom in at given left coordinate * @param originTop {Number ? null} Zoom in at given top coordinate * @param callback {Function ? null} A callback that gets fired when the zoom is complete. */ zoomTo: function zoomTo(level, animate, originLeft, originTop, callback) { var self = this; if (!self.options.zooming) { throw new Error('Zooming is not enabled!'); } // Add callback if exists if (callback) { self.__zoomComplete = callback; } // Stop deceleration if (self.__isDecelerating) { core.effect.Animate.stop(self.__isDecelerating); self.__isDecelerating = false; } var oldLevel = self.__zoomLevel; // Normalize input origin to center of viewport if not defined if (originLeft == null) { originLeft = self.__clientWidth / 2; } if (originTop == null) { originTop = self.__clientHeight / 2; } // Limit level according to configuration level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom); // Recompute maximum values while temporary tweaking maximum scroll ranges self.__computeScrollMax(level); // Recompute left and top coordinates based on new zoom level var left = (originLeft + self.__scrollLeft) * level / oldLevel - originLeft; var top = (originTop + self.__scrollTop) * level / oldLevel - originTop; // Limit x-axis if (left > self.__maxScrollLeft) { left = self.__maxScrollLeft; } else if (left < 0) { left = 0; } // Limit y-axis if (top > self.__maxScrollTop) { top = self.__maxScrollTop; } else if (top < 0) { top = 0; } // Push values out self.__publish(left, top, level, animate); }, /** * Zooms the content by the given factor. * * @param factor {Number} Zoom by given factor * @param animate {Boolean ? false} Whether to use animation * @param originLeft {Number ? 0} Zoom in at given left coordinate * @param originTop {Number ? 0} Zoom in at given top coordinate * @param callback {Function ? null} A callback that gets fired when the zoom is complete. */ zoomBy: function zoomBy(factor, animate, originLeft, originTop, callback) { var self = this; self.zoomTo(self.__zoomLevel * factor, animate, originLeft, originTop, callback); }, /** * Scrolls to the given position. Respect limitations and snapping automatically. * * @param left {Number?null} Horizontal scroll position, keeps current if value is null * @param top {Number?null} Vertical scroll position, keeps current if value is null * @param animate {Boolean?false} Whether the scrolling should happen using an animation * @param zoom {Number?null} Zoom level to go to */ scrollTo: function scrollTo(left, top, animate, zoom, force, speed, easing) { var self = this; // Stop deceleration if (self.__isDecelerating) { core.effect.Animate.stop(self.__isDecelerating); self.__isDecelerating = false; } // Correct coordinates based on new zoom level if (zoom != null && zoom !== self.__zoomLevel) { if (!self.options.zooming) { throw new Error('Zooming is not enabled!'); } left *= zoom; top *= zoom; // Recompute maximum values while temporary tweaking maximum scroll ranges self.__computeScrollMax(zoom); } else { // Keep zoom when not defined zoom = self.__zoomLevel; } if (!self.options.scrollingX && !force) { left = self.__scrollLeft; } else { if (self.options.paging) { left = Math.round(left / self.__clientWidth) * self.__clientWidth; } else if (self.options.snapping) { left = Math.round(left / self.__snapWidth) * self.__snapWidth; } } if (!self.options.scrollingY && !force) { top = self.__scrollTop; } else { if (self.options.paging) { top = Math.round(top / self.__clientHeight) * self.__clientHeight; } else if (self.options.snapping) { top = Math.round(top / self.__snapHeight) * self.__snapHeight; } } if (!force) { // Limit for allowed ranges left = Math.max(Math.min(self.__maxScrollLeft, left), 0); top = Math.max(Math.min(self.__maxScrollTop, top), 0); } // Don't animate when no change detected, still call publish to make sure // that rendered position is really in-sync with internal data if (left === self.__scrollLeft && top === self.__scrollTop) { animate = false; } // Publish new values if (!self.__isTracking) { self.__publish(left, top, zoom, animate, speed, easing); } }, /** * Scroll by the given offset * * @param left {Number ? 0} Scroll x-axis by given offset * @param top {Number ? 0} Scroll x-axis by given offset * @param animate {Boolean ? false} Whether to animate the given change */ scrollBy: function scrollBy(left, top, animate) { var self = this; var startLeft = self.__isAnimating ? self.__scheduledLeft : self.__scrollLeft; var startTop = self.__isAnimating ? self.__scheduledTop : self.__scrollTop; self.scrollTo(startLeft + (left || 0), startTop + (top || 0), animate); }, getCurrentPage: function getCurrentPage() { this.__computePage(); return { x: this.__currentPageX, y: this.__currentPageY }; }, goToPage: function goToPage(_ref3, animate) { var x = _ref3.x, y = _ref3.y; if (isNaN(x)) { x = 1; } if (isNaN(y)) { y = 1; } this.scrollTo((x - 1) * this.__clientWidth, (y - 1) * this.__clientHeight, animate); }, /* --------------------------------------------------------------------------- EVENT CALLBACKS --------------------------------------------------------------------------- */ /** * Mouse wheel handler for zooming support */ doMouseZoom: function doMouseZoom(wheelDelta, timeStamp, pageX, pageY) { var self = this; var change = wheelDelta > 0 ? 0.97 : 1.03; return self.zoomTo(self.__zoomLevel * change, false, pageX - self.__clientLeft, pageY - self.__clientTop); }, /** * Touch start handler for scrolling support */ doTouchStart: function doTouchStart(touches, timeStamp) { // Array-like check is enough here if (touches.length == null) { throw new Error('Invalid touch list: ' + touches); } if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf(); } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp); } var self = this; // Reset interruptedAnimation flag self.__interruptedAnimation = true; // Stop deceleration if (self.__isDecelerating) { core.effect.Animate.stop(self.__isDecelerating); self.__isDecelerating = false; self.__interruptedAnimation = true; } // Stop animation if (self.__isAnimating) { core.effect.Animate.stop(self.__isAnimating); self.__isAnimating = false; self.__interruptedAnimation = true; } // Use center point when dealing with two fingers var currentTouchLeft, currentTouchTop; var isSingleTouch = touches.length === 1; if (isSingleTouch) { currentTouchLeft = touches[0].pageX; currentTouchTop = touches[0].pageY; } else { currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2; currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2; } // Store initial positions self.__initialTouchLeft = currentTouchLeft; self.__initialTouchTop = currentTouchTop; // Store current zoom level self.__zoomLevelStart = self.__zoomLevel; // Store initial touch positions self.__lastTouchLeft = currentTouchLeft; self.__lastTouchTop = currentTouchTop; // Store initial move time stamp self.__lastTouchMove = timeStamp; // Reset initial scale self.__lastScale = 1; // Reset locking flags self.__enableScrollX = !isSingleTouch && self.options.scrollingX; self.__enableScrollY = !isSingleTouch && self.options.scrollingY; // Reset tracking flag self.__isTracking = true; // Reset deceleration complete flag self.__didDecelerationComplete = false; // Dragging starts directly with two fingers, otherwise lazy with an offset self.__isDragging = !isSingleTouch; // Some features are in multi touch scenarios self.__isSingleTouch = isSingleTouch; // Clearing data structure self.__positions = []; }, /** * Touch move handler for scrolling support */ doTouchMove: function doTouchMove(touches, timeStamp, scale) { // Array-like check is enough here if (touches.length == null) { throw new Error('Invalid touch list: ' + touches); } if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf(); } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp); } var self = this; // Ignore event when tracking is not enabled (event might be outside of element) if (!self.__isTracking) { return; } var currentTouchLeft, currentTouchTop; // Compute move based around of center of fingers if (touches.length === 2) { currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2; currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2; } else { currentTouchLeft = touches[0].pageX; currentTouchTop = touches[0].pageY; } var positions = self.__positions; // Are we already is dragging mode? if (self.__isDragging) { // Compute move distance var moveX = currentTouchLeft - self.__lastTouchLeft; var moveY = currentTouchTop - self.__lastTouchTop; // Read previous scroll position and zooming var scrollLeft = self.__scrollLeft; var scrollTop = self.__scrollTop; var level = self.__zoomLevel; // Work with scaling if (scale != null && self.options.zooming) { var oldLevel = level; // Recompute level based on previous scale and new scale level = level / self.__lastScale * scale; // Limit level according to configuration level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom); // Only do further compution when change happened if (oldLevel !== level) { // Compute relative event position to container var currentTouchLeftRel = currentTouchLeft - self.__clientLeft; var currentTouchTopRel = currentTouchTop - self.__clientTop; // Recompute left and top coordinates based on new zoom level scrollLeft = (currentTouchLeftRel + scrollLeft) * level / oldLevel - currentTouchLeftRel; scrollTop = (currentTouchTopRel + scrollTop) * level / oldLevel - currentTouchTopRel; // Recompute max scroll values self.__computeScrollMax(level); } } var bouncing = self.options.bouncing; if (self.__enableScrollX) { scrollLeft -= moveX * this.options.speedMultiplier; var maxScrollLeft = self.__maxScrollLeft; if (scrollLeft > maxScrollLeft || scrollLeft < 0) { scrollLeft += moveX / 2 * this.options.speedMultiplier; // fix scrollLeft scrollLeft = Math.min(Math.max(-bouncing.left, scrollLeft), maxScrollLeft + bouncing.right); } } // Compute new vertical scroll position if (self.__enableScrollY) { scrollTop -= moveY * this.options.speedMultiplier; var maxScrollTop = self.__maxScrollTop; if (scrollTop > maxScrollTop || scrollTop < 0) { scrollTop += moveY / 2 * this.options.speedMultiplier; // fix scrollTop scrollTop = Math.min(Math.max(-bouncing.top, scrollTop), maxScrollTop + bouncing.bottom); // Trigger pull to refresh or push to load if (!self.__enableScrollX && (self.__refreshHeight != null || self.__loadHeight != null)) { if (!self.__refreshActive && scrollTop <= -self.__refreshHeight) { self.__refreshActive = true; if (self.__refreshActivate) { self.__refreshActivate(); } } else if (self.__refreshActive && scrollTop > -self.__refreshHeight) { self.__refreshActive = false; if (self.__refreshDeactivate) { self.__refreshDeactivate(); } } // handle for push-load else if (!self.__loadActive && scrollTop >= self.__maxScrollTop + self.__loadHeight && self.__loadHeight > 0) { self.__loadActive = true; if (self.__loadActivate) { self.__loadActivate(); } } else if (self.__loadActive && scrollTop < self.__maxScrollTop + self.__loadHeight) { self.__loadActive = false; if (self.__loadDeactivate) { self.__loadDeactivate(); } } } } } // Keep list from growing infinitely (holding min 10, max 20 measure points) if (positions.length > 60) { positions.splice(0, 30); } // Track scroll movement for decleration positions.push(scrollLeft, scrollTop, timeStamp); // Sync scroll position self.__publish(scrollLeft, scrollTop, level); // Otherwise figure out whether we are switching into dragging mode now. } else { var minimumTrackingForScroll = self.options.locking ? 3 : 0; var minimumTrackingForDrag = 5; var distanceX = Math.abs(currentTouchLeft - self.__initialTouchLeft); var distanceY = Math.abs(currentTouchTop - self.__initialTouchTop); self.__enableScrollX = self.options.scrollingX && distanceX >= minimumTrackingForScroll; self.__enableScrollY = self.options.scrollingY && distanceY >= minimumTrackingForScroll; positions.push(self.__scrollLeft, self.__scrollTop, timeStamp); self.__isDragging = (self.__enableScrollX || self.__enableScrollY) && (distanceX >= minimumTrackingForDrag || distanceY >= minimumTrackingForDrag); if (self.__isDragging) { self.__interruptedAnimation = false; } } // Update last touch positions and time stamp for next event self.__lastTouchLeft = currentTouchLeft; self.__lastTouchTop = currentTouchTop; self.__lastTouchMove = timeStamp; self.__lastScale = scale; }, /** * Touch end handler for scrolling support */ doTouchEnd: function doTouchEnd(timeStamp) { if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf(); } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp); } var self = this; // Ignore event when tracking is not enabled (no touchstart event on element) // This is required as this listener ('touchmove') sits on the document and not on the element itself. if (!self.__isTracking) { return; } // Not touching anymore (when two finger hit the screen there are two touch end events) self.__isTracking = false; // Be sure to reset the dragging flag now. Here we also detect whether // the finger has moved fast enough to switch into a deceleration animation. if (self.__isDragging) { // Reset dragging flag self.__isDragging = false; // Start deceleration // Verify that the last move detected was in some relevant time frame if (self.__isSingleTouch && self.options.animating && timeStamp - self.__lastTouchMove <= 100) { // Then figure out what the scroll position was about 100ms ago var positions = self.__positions; var endPos = positions.length - 1; var startPos = endPos; // Move pointer to position measured 100ms ago for (var i = endPos; i > 0 && positions[i] > self.__lastTouchMove - 100; i -= 3) { startPos = i; } // If start and stop position is identical in a 100ms timeframe, // we cannot compute any useful deceleration. if (startPos !== endPos) { // Compute relative movement between these two points var timeOffset = positions[endPos] - positions[startPos]; var movedLeft = self.__scrollLeft - positions[startPos - 2]; var movedTop = self.__scrollTop - positions[startPos - 1]; // Based on 50ms compute the movement to apply for each render step self.__decelerationVelocityX = movedLeft / timeOffset * (1000 / 60); self.__decelerationVelocityY = movedTop / timeOffset * (1000 / 60); // How much velocity is required to start the deceleration var minVelocityToStartDeceleration = self.options.paging || self.options.snapping ? 4 : 1; // Verify that we have enough velocity to start deceleration if (Math.abs(self.__decelerationVelocityX) > minVelocityToStartDeceleration || Math.abs(self.__decelerationVelocityY) > minVelocityToStartDeceleration) { // Deactivate pull-to-refresh when decelerating if (!self.__refreshActive && !self.__loadActive) { self.__startDeceleration(timeStamp); } } else { self.__scrollComplete(); } } else { self.__scrollComplete(); } } else if (timeStamp - self.__lastTouchMove > 100) { self.__scrollComplete(); } } // If this was a slower move it is per default non decelerated, but this // still means that we want snap back to the bounds which is done here. // This is placed outside the condition above to improve edge case stability // e.g. touchend fired without enabled dragging. This should normally do not // have modified the scroll positions or even showed the scrollbars though. if (!self.__isDecelerating) { if (self.__refreshActive && self.__refreshStart) { // Use publish instead of scrollTo to allow scrolling to out of boundary position // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled self.__publish(self.__scrollLeft, -self.__refreshHeight, self.__zoomLevel, true); if (self.__refreshStart) { self.__refreshStart(); } } else if (self.__loadActive && self.__loadStart) { // Use publish instead of scrollTo to allow scrolling to out of boundary position // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled self.__publish(self.__scrollLeft, self.__maxScrollTop + self.__loadHeight, self.__zoomLevel, true); if (self.__loadStart) { self.__loadStart(); } } else { if (self.__interruptedAnimation || self.__isDragging) { self.__scrollComplete(); } self.scrollTo(self.__scrollLeft, self.__scrollTop, true, self.__zoomLevel); // Directly signalize deactivation (nothing todo on refresh?) if (self.__refreshActive) { self.__refreshActive = false; if (self.__refreshDeactivate) { self.__refreshDeactivate(); } } else if (self.__loadActive) { self.__loadActive = false; if (self.__loadDeactivate) { self.__loadDeactivate(); } } } } // Fully cleanup list self.__positions.length = 0; }, /** Handle for scroll/publish */ onScroll: NOOP$1, stop: function stop() { var self = this; self.__disable = true; }, start: function start() { var self = this; self.__disable = true; }, /* --------------------------------------------------------------------------- PRIVATE API --------------------------------------------------------------------------- */ /** * Applies the scroll position to the content element * * @param left {Number} Left scroll position * @param top {Number} Top scroll position * @param animate {Boolean?false} Whether animation should be used to move to the new coordinates */ __publish: function __publish(left, top, zoom, animate, speed, easing) { var self = this; if (self.__disable) { return; } if (isNaN(left)) { left = this.__scrollLeft; } if (isNaN(top)) { top = this.__scrollTop; } // Remember whether we had an animation, then we try to continue based on the current "drive" of the animation var wasAnimating = self.__isAnimating; if (wasAnimating) { core.effect.Animate.stop(wasAnimating); self.__isAnimating = false; } if (animate && (self.options.animating || speed)) { // Keep scheduled positions for scrollBy/zoomBy functionality self.__scheduledLeft = left; self.__scheduledTop = top; self.__scheduledZoom = zoom; var oldLeft = self.__scrollLeft; var oldTop = self.__scrollTop; var oldZoom = self.__zoomLevel; var diffLeft = left - oldLeft; var diffTop = top - oldTop; var diffZoom = zoom - oldZoom; var step = function step(percent, now, render) { if (render) { self.__scrollLeft = oldLeft + diffLeft * percent; self.__scrollTop = oldTop + diffTop * percent; self.__zoomLevel = oldZoom + diffZoom * percent; // Push values out if (self.__callback) { self.__callback(self.__scrollLeft, self.__scrollTop, self.__zoomLevel); self.onScroll(); } } }; var verify = function verify(id) { return self.__isAnimating === id; }; var completed = function completed(renderedFramesPerSecond, animationId, wasFinished) { if (animationId === self.__isAnimating) { self.__isAnimating = false; } if (self.__didDecelerationComplete || wasFinished) { self.__scrollComplete(); } if (self.options.zooming) { self.__computeScrollMax(); if (self.__zoomComplete) { self.__zoomComplete(); self.__zoomComplete = null; } } if (self.__refreshBeforeDeactiveStarted) { self.__refreshBeforeDeactiveStarted = false; if (self.__refreshDeactivate) self.__refreshDeactivate(); } if (self.__loadBeforeDeactiveStarted) { self.__loadBeforeDeactiveStarted = false; if (self.__loadDeactivate) self.__loadDeactivate(); } }; var easingFunction = animatingMethod; if (easing) { easingFunction = createEasingFunction(easing, easingPattern); } // When continuing based on previous animation we choose an ease-out animation instead of ease-in-out self.__isAnimating = core.effect.Animate.start(step, verify, completed, speed || self.options.animationDuration, wasAnimating ? easingFunction : noAnimatingMethod); } else { self.__scheduledLeft = self.__scrollLeft = left; self.__scheduledTop = self.__scrollTop = top; self.__scheduledZoom = self.__zoomLevel = zoom; // Push values out if (self.__callback) { self.__callback(left, top, zoom); self.onScroll(); } // Fix max scroll ranges if (self.options.zooming) { self.__computeScrollMax(); if (self.__zoomComplete) { self.__zoomComplete(); self.__zoomComplete = null; } } if (self.__refreshBeforeDeactiveStarted) { self.__refreshBeforeDeactiveStarted = false; if (self.__refreshDeactivate) self.__refreshDeactivate(); } if (self.__loadBeforeDeactiveStarted) { self.__loadBeforeDeactiveStarted = false; if (self.__loadDeactivate) self.__loadDeactivate(); } } }, /** * Recomputes scroll minimum values based on client dimensions and content dimensions. */ __computeScrollMax: function __computeScrollMax(zoomLevel) { var self = this; if (zoomLevel == null) { zoomLevel = self.__zoomLevel; } self.__maxScrollLeft = Math.max(self.__contentWidth * zoomLevel - self.__clientWidth, 0); self.__maxScrollTop = Math.max(self.__contentHeight * zoomLevel - self.__clientHeight, 0); }, /** compute current page total page */ __computePage: function __computePage() { var self = this; var clientWidth = self.__clientWidth; var clientHeight = self.__clientHeight; var left = self.__scrollLeft; var top = self.__scrollTop; self.__totalXPage = Math.ceil(self.__contentWidth / clientWidth); self.__currentPageX = Math.ceil(left / clientWidth + 1); self.__totalYPage = Math.ceil(self.__contentHeight / clientHeight); self.__currentPageY = Math.ceil(top / clientHeight + 1); }, /** complete scroll*/ __scrollComplete: function __scrollComplete() { var self = this; self.options.scrollingComplete(); }, /* --------------------------------------------------------------------------- ANIMATION (DECELERATION) SUPPORT --------------------------------------------------------------------------- */ /** * Called when a touch sequence end and the speed of the finger was high enough * to switch into deceleration mode. */ __startDeceleration: function __startDeceleration() { var self = this; if (self.options.paging) { var scrollLeft = Math.max(Math.min(self.__scrollLeft, self.__maxScrollLeft), 0); var scrollTop = Math.max(Math.min(self.__scrollTop, self.__maxScrollTop), 0); var clientWidth = self.__clientWidth; var clientHeight = self.__clientHeight; // We limit deceleration not to the min/max values of the allowed range, but to the size of the visible client area. // Each page should have exactly the size of the client area. self.__minDecelerationScrollLeft = Math.floor(scrollLeft / clientWidth) * clientWidth; self.__minDecelerationScrollTop = Math.floor(scrollTop / clientHeight) * clientHeight; self.__maxDecelerationScrollLeft = Math.ceil(scrollLeft / clientWidth) * clientWidth; self.__maxDecelerationScrollTop = Math.ceil(scrollTop / clientHeight) * clientHeight; } else { self.__minDecelerationScrollLeft = 0; self.__minDecelerationScrollTop = 0; self.__maxDecelerationScrollLeft = self.__maxScrollLeft; self.__maxDecelerationScrollTop = self.__maxScrollTop; } // Wrap class method var step = function step(percent, now, render) { self.__stepThroughDeceleration(render); }; // How much velocity is required to keep the deceleration running var minVelocityToKeepDecelerating = self.options.snapping ? 4 : 0.001; // Detect whether it's still worth to continue animating steps // If we are already slow enough to not being user perceivable anymore, we stop the whole process here. var verify = function verify() { var shouldContinue = Math.abs(self.__decelerationVelocityX) >= minVelocityToKeepDecelerating || Math.abs(self.__decelerationVelocityY) >= minVelocityToKeepDecelerating; if (!shouldContinue) { self.__didDecelerationComplete = true; } return shouldContinue; }; var completed = function completed() { if (!self.__isDecelerating) { return; } self.__isDecelerating = false; if (self.__didDecelerationComplete) { self.__scrollComplete(); } // Animate to grid when snapping is active, otherwise just fix out-of-boundary positions self.scrollTo(self.__scrollLeft, self.__scrollTop, self.options.snapping); }; // Start animation and switch on flag self.__isDecelerating = core.effect.Animate.start(step, verify, completed); }, /** * Called on every step of the animation * * @param inMemory {Boolean?false} Whether to not render the current step, but keep it in memory only. Used internally only! */ __stepThroughDeceleration: function __stepThroughDeceleration(render) { var self = this; var bouncing = self.options.bouncing; var minLeft = self.__minDecelerationScrollLeft; var maxLeft = self.__maxDecelerationScrollLeft; var minTop = self.__minDecelerationScrollTop; var maxTop = self.__maxDecelerationScrollTop; // Add deceleration to scroll position var scrollLeft = self.__scrollLeft + self.__decelerationVelocityX; var scrollTop = self.__scrollTop + self.__decelerationVelocityY; var bounceX = scrollLeft < minLeft || scrollLeft > maxLeft; var bounceY = scrollTop < minTop || scrollTop > maxTop; // fix scrollLeft and scrollTop var fixedScrollLeft = Math.min(Math.max(minLeft - bouncing.left, scrollLeft), maxLeft + bouncing.right); var fixedScrollTop = Math.min(Math.max(minTop - bouncing.top, scrollTop), maxTop + bouncing.bottom); // // UPDATE SCROLL POSITION // if (render) { self.__publish(fixedScrollLeft, fixedScrollTop, self.__zoomLevel); } else { self.__scrollLeft = scrollLeft; self.__scrollTop = scrollTop; } // // SLOW DOWN // // Slow down velocity on every iteration if (!self.options.paging) { // This is the factor applied to every iteration of the animation // to slow down the process. This should emulate natural behavior where // objects slow down when the initiator of the movement is removed var frictionFactor = 0.95; self.__decelerationVelocityX *= frictionFactor; self.__decelerationVelocityY *= frictionFactor; } // // BOUNCING SUPPORT // var scrollOutsideX = 0; var scrollOutsideY = 0; // This configures the amount of change applied to deceleration/acceleration when reaching boundaries var penetrationDeceleration = self.options.penetrationDeceleration; var penetrationAcceleration = self.options.penetrationAcceleration; if (bounceX) { // Check limits if (scrollLeft < self.__minDecelerationScrollLeft) { scrollOutsideX = self.__minDecelerationScrollLeft - scrollLeft; } else if (scrollLeft > self.__maxDecelerationScrollLeft) { scrollOutsideX = self.__maxDecelerationScrollLeft - scrollLeft; } } if (bounceY) { if (scrollTop < self.__minDecelerationScrollTop) { scrollOutsideY = self.__minDecelerationScrollTop - scrollTop; } else if (scrollTop > self.__maxDecelerationScrollTop) { scrollOutsideY = self.__maxDecelerationScrollTop - scrollTop; } } if (scrollOutsideX !== 0) { if (scrollOutsideX * self.__decelerationVelocityX <= 0) { self.__decelerationVelocityX += scrollOutsideX * penetrationDeceleration; if (scrollOutsideX < 0 && -scrollOutsideX >= bouncing.right && self.__decelerationVelocityX > 0) { self.__decelerationVelocityX = -bouncing.right / 2; } if (scrollOutsideX > 0 && scrollOutsideX >= bouncing.left && self.__decelerationVelocityX < 0) { self.__decelerationVelocityX = bouncing.left / 2; } } else { self.__decelerationVelocityX = scrollOutsideX * penetrationAcceleration; } } if (scrollOutsideY !== 0) { if (scrollOutsideY * self.__decelerationVelocityY <= 0) { self.__decelerationVelocityY += scrollOutsideY * penetrationDeceleration; if (scrollOutsideY < 0 && -scrollOutsideY >= bouncing.bottom && self.__decelerationVelocityY > 0) { self.__decelerationVelocityY = -bouncing.bottom / 2; } if (scrollOutsideY > 0 && scrollOutsideY >= bouncing.top && self.__decelerationVelocityY < 0) { self.__decelerationVelocityY = bouncing.top / 2; } } else { self.__decelerationVelocityY = scrollOutsideY * penetrationAcceleration / 2; } } } }; // Copy over members to prototype for (var key in members) { Scroller.prototype[key] = members[key]; } /* DOM-based rendering (Uses 3D when available, falls back on margin when transform not available) */ function render(content, global, suffix, type) { if (type == 'position') { return function (left, top) { content.style.left = -left + 'px'; content.style.top = -top + 'px'; }; } var vendorPrefix = getPrefix(global); var helperElem = document.createElement('div'); var undef; var perspectiveProperty = vendorPrefix + 'Perspective'; var transformProperty = 'transform'; //vendorPrefix + 'Transform'; if (helperElem.style[perspectiveProperty] !== undef) { return function (left, top, zoom) { content.style[transformProperty] = 'translate3d(' + -left + suffix + ',' + -top + suffix + ',0) scale(' + zoom + ')'; }; } else if (helperElem.style[transformProperty] !== undef) { return function (left, top, zoom) { content.style[transformProperty] = 'translate(' + -left + suffix + ',' + -top + suffix + ') scale(' + zoom + ')'; }; } } var touch = new touchManager(); function listenContainer(container, scroller, eventCallback, zooming, preventDefault, preventDefaultOnMove) { var destroy = null; var mousedown = false; var touchObj = touch.getTouchObject(); function touchstart(e) { var event = touch.getEventObject(e); // Don't react if initial down happens on a form element if (event[0] && event[0].target && event[0].target.tagName.match(/input|textarea|select/i) || scroller.__disable) { return; } eventCallback('mousedown'); mousedown = true; scroller.doTouchStart(event, e.timeStamp); if (preventDefault) { e.preventDefault(); } e.stopPropagation(); // here , we want to manully prevent default, so we // set passive to false // see https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener document.addEventListener(touchObj.touchmove, touchmove, { passive: false }); } function touchmove(e) { if (scroller.__disable || !mousedown) return; var event = touch.getEventObject(e); eventCallback('mousemove'); scroller.doTouchMove(event, e.timeStamp, e.scale); if (preventDefaultOnMove) { e.preventDefault(); } } function touchend(e) { eventCallback('mouseup'); mousedown = false; scroller.doTouchEnd(e.timeStamp); document.removeEventListener(touchObj.touchmove, touchmove); } function touchcancel(e) { scroller.doTouchEnd(e.timeStamp); } function zoomHandle(e) { scroller.doMouseZoom(e.detail ? e.detail * -120 : e.wheelDelta, e.timeStamp, e.pageX, e.pageY); } container.addEventListener(touchObj.touchstart, touchstart, false); document.addEventListener(touchObj.touchend, touchend, false); document.addEventListener(touchObj.touchcancel, touchcancel, false); if (zooming && !touch.isTouch) { container.addEventListener(navigator.userAgent.indexOf('Firefox') > -1 ? 'DOMMouseScroll' : 'mousewheel', zoomHandle, false); } destroy = function destroy() { container.removeEventListener(touchObj.touchstart, touchstart, false); document.removeEventListener(touchObj.touchend, touchend, false); document.removeEventListener(touchObj.touchcancel, touchcancel, false); container.removeEventListener(navigator.userAgent.indexOf('Firefox') > -1 ? 'DOMMouseScroll' : 'mousewheel', zoomHandle, false); }; // handle __publish event scroller.onScroll = function () { eventCallback('onscroll'); }; return destroy; } /** * These mixes is exclusive for slide mode */ createSlideModeStyle(); /** * @description refresh and load callback */ function createStateCallbacks(type, stageType, vm, tipDom) { var listeners = vm.$listeners; var activateCallback = function activateCallback() { vm.vuescroll.state[stageType] = 'active'; vm.$emit(type + '-activate', vm, tipDom); }; var deactivateCallback = function deactivateCallback() { vm.vuescroll.state[stageType] = 'deactive'; vm.$emit(type + '-deactivate', vm, tipDom); }; var beforeDeactiveEnd = function beforeDeactiveEnd() { vm.vuescroll.state[stageType] = 'beforeDeactiveEnd'; vm.$emit(type + '-before-deactivate-end', vm, tipDom); }; var startCallback = function startCallback() { vm.vuescroll.state[stageType] = 'start'; setTimeout(function () { vm.scroller.finishRefreshOrLoad(); }, 2000); // Default start stage duration }; // let beforeDeactivateCallback = done => { // vm.vuescroll.state[stageType] = 'beforeDeactive'; // setTimeout(function() { // done(); // }, 500); // Default before-deactivated stage duration // }; var beforeDeactivateCallback = void 0; /* istanbul ignore if */ if (listeners[type + '-before-deactivate']) { beforeDeactivateCallback = function beforeDeactivateCallback(done) { vm.vuescroll.state[stageType] = 'beforeDeactive'; vm.$emit(type + '-before-deactivate', vm, tipDom, done.bind(vm.scroller)); }; } /* istanbul ignore if */ if (listeners[type + '-start']) { startCallback = function startCallback() { vm.vuescroll.state[stageType] = 'start'; vm.$emit(type + '-start', vm, tipDom, vm.scroller.finishRefreshOrLoad.bind(vm.scroller)); }; } return { activateCallback: activateCallback, deactivateCallback: deactivateCallback, startCallback: startCallback, beforeDeactivateCallback: beforeDeactivateCallback, beforeDeactiveEnd: beforeDeactiveEnd }; } var update = { mounted: function mounted() { this.vsMounted = true; }, computed: { pullRefreshTip: function pullRefreshTip() { return this.mergedOptions.vuescroll.pullRefresh.tips[this.vuescroll.state.refreshStage]; }, pushLoadTip: function pushLoadTip() { return this.mergedOptions.vuescroll.pushLoad.tips[this.vuescroll.state.loadStage]; }, refreshLoad: function refreshLoad() { return this.mergedOptions.vuescroll.pullRefresh.enable || this.mergedOptions.vuescroll.pushLoad.enable; }, refrehDomVisiable: function refrehDomVisiable() { return this.vsMounted && this.outTheTopBoundary; }, loadDomVisiable: function loadDomVisiable() { return this.vsMounted && this.outTheBottomBoundary; } }, data: function data() { return { vuescroll: { state: { /** Default tips of refresh and load */ refreshStage: 'deactive', loadStage: 'deactive' } }, vsMounted: false, outTheTopBoundary: false, outTheBottomBoundary: false }; }, methods: { // Update: // 1. update height/width // 2. update refresh or load updateScroller: function updateScroller() { this.updateDimesion(); this.registryRefreshLoad(); }, updateDimesion: function updateDimesion() { var clientWidth = this.$el.clientWidth; var clientHeight = this.$el.clientHeight; var contentWidth = this.scrollPanelElm.scrollWidth; var contentHeight = this.scrollPanelElm.scrollHeight; var refreshHeight = 0; var loadHeight = 0; // If the refresh option is true,let's give a "margin-top" style to // the refresh-tip dom. let it to be invisible when doesn't trigger // refresh. if (this.mergedOptions.vuescroll.pullRefresh.enable) { if (this.vsMounted) { var refreshDom = this.$refs[__REFRESH_DOM_NAME].elm || this.$refs[__REFRESH_DOM_NAME]; refreshHeight = refreshDom.offsetHeight; refreshDom.style.marginTop = -refreshHeight + 'px'; } } if (this.mergedOptions.vuescroll.pushLoad.enable) { if (this.vsMounted) { var loadDom = this.$refs[__LOAD_DOM_NAME].elm || this.$refs[__LOAD_DOM_NAME]; loadHeight = loadDom.offsetHeight; contentHeight -= loadHeight; loadDom.style.bottom = '-' + loadHeight + 'px'; } } if (this.scroller) { this.scroller.setDimensions(clientWidth, clientHeight, contentWidth, contentHeight, false); } }, registryRefreshLoad: function registryRefreshLoad() { // registry refresh if (this.mergedOptions.vuescroll.pullRefresh.enable) { this.registryEvent('refresh'); } // registry load if (this.mergedOptions.vuescroll.pushLoad.enable) { this.registryEvent('load'); } }, registryScroller: function registryScroller() { var _this = this; var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$left = _ref.left, left = _ref$left === undefined ? 0 : _ref$left, _ref$top = _ref.top, top = _ref$top === undefined ? 0 : _ref$top, _ref$zoom = _ref.zoom, zoom = _ref$zoom === undefined ? 1 : _ref$zoom; var _mergedOptions$vuescr = this.mergedOptions.vuescroll.scroller, preventDefault = _mergedOptions$vuescr.preventDefault, preventDefaultOnMove = _mergedOptions$vuescr.preventDefaultOnMove; var _mergedOptions$vuescr2 = this.mergedOptions.vuescroll, paging = _mergedOptions$vuescr2.paging, snapping = _mergedOptions$vuescr2.snapping.enable, renderMethod = _mergedOptions$vuescr2.renderMethod, zooming = _mergedOptions$vuescr2.zooming, locking = _mergedOptions$vuescr2.locking; // disale zooming when refresh or load enabled zooming = !this.refreshLoad && !paging && !snapping && zooming; var _mergedOptions$scroll = this.mergedOptions.scrollPanel, scrollingY = _mergedOptions$scroll.scrollingY, scrollingX = _mergedOptions$scroll.scrollingX; var scrollingComplete = this.scrollingComplete.bind(this); // Initialize Scroller this.scroller = new Scroller(render(this.scrollPanelElm, window, 'px', renderMethod), _extends({}, this.mergedOptions.vuescroll.scroller, { zooming: zooming, scrollingY: scrollingY, scrollingX: scrollingX && !this.refreshLoad, animationDuration: this.mergedOptions.scrollPanel.speed, paging: paging, snapping: snapping, scrollingComplete: scrollingComplete, locking: locking })); this.scroller.__disable = this.mergedOptions.vuescroll.scroller.disable; this.scroller.__scrollLeft = left; this.scroller.__scrollTop = top; this.scroller.__zoomLevel = zoom; // Set snap if (snapping) { this.scroller.setSnapSize(this.mergedOptions.vuescroll.snapping.width, this.mergedOptions.vuescroll.snapping.height); } var rect = this.$el.getBoundingClientRect(); this.scroller.setPosition(rect.left + this.$el.clientLeft, rect.top + this.$el.clientTop); // Get destroy callback var cb = listenContainer(this.$el, this.scroller, function (eventType) { // Thie is to dispatch the event from the scroller. // to let vuescroll refresh the dom switch (eventType) { case 'mousedown': _this.vuescroll.state.isDragging = true; break; case 'onscroll': { /** * Trigger auto load */ var stage = _this.vuescroll.state['loadStage']; var _mergedOptions$vuescr3 = _this.mergedOptions.vuescroll.pushLoad, enable = _mergedOptions$vuescr3.enable, auto = _mergedOptions$vuescr3.auto, autoLoadDistance = _mergedOptions$vuescr3.autoLoadDistance; var _scroller = _this.scroller, __scrollTop = _scroller.__scrollTop, __maxScrollTop = _scroller.__maxScrollTop; if (stage != 'start' && enable && auto && !_this.lockAutoLoad && // auto load debounce autoLoadDistance >= __maxScrollTop - __scrollTop && __scrollTop > 0) { _this.lockAutoLoad = true; _this.triggerRefreshOrLoad('load'); } if (autoLoadDistance < __maxScrollTop - __scrollTop) { _this.lockAutoLoad = false; } _this.handleScroll(false); } break; case 'mouseup': _this.vuescroll.state.isDragging = false; break; } }, zooming, preventDefault, preventDefaultOnMove); this.updateScroller(); return cb; }, updateSlideModeBarState: function updateSlideModeBarState() { // update slide mode scrollbars' state var heightPercentage = void 0, widthPercentage = void 0; var vuescroll = this.$el; var scroller = this.scroller; var outerLeft = 0; var outerTop = 0; var _$el = this.$el, clientWidth = _$el.clientWidth, clientHeight = _$el.clientHeight; var contentWidth = clientWidth + this.scroller.__maxScrollLeft; var contentHeight = clientHeight + this.scroller.__maxScrollTop; // We should add the the height or width that is // out of horizontal bountry to the total length /* istanbul ignore if */ if (scroller.__scrollLeft < 0) { outerLeft = -scroller.__scrollLeft; } /* istanbul ignore next */else if (scroller.__scrollLeft > scroller.__maxScrollLeft) { outerLeft = scroller.__scrollLeft - scroller.__maxScrollLeft; } // out of vertical bountry if (scroller.__scrollTop < 0) { outerTop = -scroller.__scrollTop; this.outTheBottomBoundary = false; this.outTheTopBoundary = true; } else if (scroller.__scrollTop > scroller.__maxScrollTop) { outerTop = scroller.__scrollTop - scroller.__maxScrollTop; this.outTheTopBoundary = false; this.outTheBottomBoundary = true; } else { this.outTheTopBoundary = this.outTheBottomBoundary = false; } heightPercentage = clientHeight / (contentHeight + outerTop); widthPercentage = clientWidth / (contentWidth + outerLeft); var scrollTop = Math.min(Math.max(0, scroller.__scrollTop), scroller.__maxScrollTop); var scrollLeft = Math.min(Math.max(0, scroller.__scrollLeft), scroller.__maxScrollLeft); this.bar.vBar.state.posValue = (scrollTop + outerTop) * 100 / vuescroll.clientHeight; this.bar.hBar.state.posValue = (scrollLeft + outerLeft) * 100 / vuescroll.clientWidth; /* istanbul ignore if */ if (scroller.__scrollLeft < 0) { this.bar.hBar.state.posValue = 0; } if (scroller.__scrollTop < 0) { this.bar.vBar.state.posValue = 0; } this.bar.vBar.state.size = heightPercentage < 1 ? heightPercentage : 0; this.bar.hBar.state.size = widthPercentage < 1 ? widthPercentage : 0; }, registryEvent: function registryEvent(type) { var domName = type == 'refresh' ? __REFRESH_DOM_NAME : __LOAD_DOM_NAME; var activateFunc = type == 'refresh' ? this.scroller.activatePullToRefresh : this.scroller.activatePushToLoad; var stageType = type == 'refresh' ? 'refreshStage' : 'loadStage'; var tipDom = this.$refs[domName].elm || this.$refs[domName]; var cbs = createStateCallbacks(type, stageType, this, tipDom); var height = tipDom.offsetHeight; activateFunc.bind(this.scroller)(height, cbs); }, getSlidePosition: function getSlidePosition() { return { scrollLeft: this.scroller.__scrollLeft, scrollTop: this.scroller.__scrollTop }; } } }; var mixins = [api$1, update]; var core$1 = { mixins: mixins, mounted: function mounted() { var _this = this; this.$nextTick(function () { if (!_this._isDestroyed && !_this.renderError) { _this.updatedCbs.push(function () { _this.updateScroller(); }); _this.$watch('mergedOptions.vuescroll.scroller.disable', { sync: true, handler: function handler(newVal) { if (this.scroller) { this.scroller.__disable = newVal; } } }); } }); }, methods: { destroy: function destroy() { /* istanbul ignore next */ if (this.destroyScroller) { this.scroller.stop(); this.destroyScroller(); this.destroyScroller = null; } /* istanbul ignore next */ if (this.destroyResize) { this.destroyResize(); } }, getCurrentviewDom: function getCurrentviewDom() { return this.getCurrentviewDomSlide(); }, internalScrollTo: function internalScrollTo(destX, destY, speed, sasing) { this.slideScrollTo(destX, destY, speed, sasing); }, handleScroll: function handleScroll(nativeEvent) { this.updateBarStateAndEmitEvent('handle-scroll', nativeEvent); }, updateBarStateAndEmitEvent: function updateBarStateAndEmitEvent(eventType) { var nativeEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (!this.scroller) { return; } this.updateSlideModeBarState(); 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() { var _scrollPanelElm = this.scrollPanelElm, scrollHeight = _scrollPanelElm.scrollHeight, scrollWidth = _scrollPanelElm.scrollWidth, clientHeight = _scrollPanelElm.clientHeight, clientWidth = _scrollPanelElm.clientWidth, scrollTop = _scrollPanelElm.scrollTop, scrollLeft = _scrollPanelElm.scrollLeft; scrollHeight = this.scroller.__contentHeight; scrollWidth = this.scroller.__contentWidth; scrollTop = this.scroller.__scrollTop; scrollLeft = this.scroller.__scrollLeft; clientHeight = this.$el.clientHeight; clientWidth = this.$el.clientWidth; 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 vertical = { type: 'vertical' }; var horizontal = { type: 'horizontal' }; var _getPosition = this.getPosition(), scrollTop = _getPosition.scrollTop, scrollLeft = _getPosition.scrollLeft; 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; }, refreshMode: function refreshMode() { var initPos = void 0; if (this.scroller) { initPos = this.scroller.getValues(); } if (this.destroyScroller) { this.scroller.stop(); this.destroyScroller(); this.destroyScroller = null; } this.destroyScroller = this.registryScroller(initPos); }, refreshInternalStatus: function refreshInternalStatus() { // 1.set vuescroll height or width according to // sizeStrategy this.setVsSize(); // 2. registry resize event this.registryResize(); // 3. registry scroller if mode is 'slide' // or remove 'transform origin' is the mode is not `slide` this.refreshMode(); // 4. update scrollbar's height/width this.updateBarStateAndEmitEvent('refresh-status'); }, registryResize: function registryResize() { var _this2 = 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.scrollPanelElm; var vm = this; var handleWindowResize = function handleWindowResize() /* istanbul ignore next */{ vm.updateBarStateAndEmitEvent('window-resize'); vm.updatedCbs.push(vm.updateScroller); vm.$forceUpdate(); }; var handleDomResize = function handleDomResize() { var currentSize = {}; currentSize['width'] = _this2.scroller.__contentWidth; currentSize['height'] = _this2.scroller.__contentHeight; _this2.updateBarStateAndEmitEvent('handle-resize', currentSize); // update scroller should after rendering _this2.updatedCbs.push(_this2.updateScroller); _this2.$forceUpdate(); // Since content sie changes, we should tell parent to set // correct size to fit content's size _this2.setVsSize(); }; window.addEventListener('resize', handleWindowResize, false); var destroyDomResize = resizeEnable ? installResizeDetection(contentElm, handleDomResize) : NOOP; var destroyWindowResize = function destroyWindowResize() { window.removeEventListener('resize', handleWindowResize, false); }; this.destroyResize = function () { destroyWindowResize(); destroyDomResize(); _this2.destroyResize = null; }; }, getPosition: function getPosition() { return this.getSlidePosition(); } } }; /** * The slide mode config */ var config = { // vuescroll vuescroll: { // position or transform renderMethod: 'transform', // pullRefresh or pushLoad is only for the slide mode... pullRefresh: { enable: false, tips: { deactive: 'Pull to Refresh', active: 'Release to Refresh', start: 'Refreshing...', beforeDeactive: 'Refresh Successfully!' } }, pushLoad: { enable: false, tips: { deactive: 'Push to Load', active: 'Release to Load', start: 'Loading...', beforeDeactive: 'Load Successfully!' }, auto: false, autoLoadDistance: 0 }, paging: false, zooming: true, snapping: { enable: false, width: 100, height: 100 }, /* some scroller options */ scroller: { /** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */ bouncing: { top: 100, bottom: 100, left: 100, right: 100 }, /** Minimum zoom level */ minZoom: 0.5, /** Maximum zoom level */ maxZoom: 3, /** Multiply or decrease scrolling speed **/ speedMultiplier: 1, /** This configures the amount of change applied to deceleration when reaching boundaries **/ penetrationDeceleration: 0.03, /** This configures the amount of change applied to acceleration when reaching boundaries **/ penetrationAcceleration: 0.08, /** Whether call e.preventDefault event when sliding the content or not */ preventDefault: false, /** Whether call preventDefault when (mouse/touch)move*/ preventDefaultOnMove: true, disable: false } } }; /** * validate the options * @export * @param {any} ops */ function configValidator(ops) { var renderError = false; var vuescroll = ops.vuescroll; // validate pushLoad, pullReresh, snapping if (vuescroll.paging == vuescroll.snapping.enable && vuescroll.paging && (vuescroll.pullRefresh || vuescroll.pushLoad)) { error('paging, snapping, (pullRefresh with pushLoad) can only one of them to be true.'); } return renderError; } var component = _install(core$1, createPanel, [config], [configValidator]); function install(Vue) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; Vue.component(opts.name || component.name, component); Vue.prototype.$vuescrollConfig = opts.ops || {}; } var Vuescroll = _extends({ install: install, version: '4.18.1', refreshAll: refreshAll, scrollTo: scrollTo }, component); /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { window.Vue.use(Vuescroll); } return Vuescroll; })));