From 897983b5280e6b1291914942582911e018cf8ce2 Mon Sep 17 00:00:00 2001 From: masongzhi Date: Fri, 14 Jul 2023 15:27:16 +0800 Subject: [PATCH] feat(master): toThresholdTop and bottomThreshold feat(master): toThresholdTop and bottomThreshold feat(master): toThresholdTop and bottomThreshold feat(master): toThresholdTop and bottomThreshold --- README.md | 16 ++- dist/index.js | 345 +++++++++++++++++++++++++++----------------------- src/index.js | 8 +- 3 files changed, 208 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index f9bbb2c..cd1cd7d 100644 --- a/README.md +++ b/README.md @@ -186,12 +186,24 @@ More usages or getting start you can refer to these clearly [examples](https://g Emited when scrolled to top or left, no param. + + toThresholdTop + Event + + Emited when scrolled to top or left with topThreshold, no param. + tobottom Event Emited when scrolled to bottom or right, no param. + + toThresholdBottom + Event + + Emited when scrolled to bottom or right with bottomThreshold, no param. + resized Event @@ -214,13 +226,13 @@ More usages or getting start you can refer to these clearly [examples](https://g top-threshold Number 0 - The threshold to emit totop event, attention to multiple calls. + The threshold to emit toThresholdTop event, attention to multiple calls. bottom-threshold Number 0 - The threshold to emit tobottom event, attention to multiple calls. + The threshold to emit toThresholdBottom event, attention to multiple calls. root-tag diff --git a/dist/index.js b/dist/index.js index 60cdc77..9c79605 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,5 @@ /*! - * vue-virtual-scroll-list v2.3.4 + * vue-virtual-scroll-list v2.3.6 * open source under the MIT license * https://github.com/tangbc/vue-virtual-scroll-list#readme */ @@ -12,32 +12,12 @@ Vue = Vue && Object.prototype.hasOwnProperty.call(Vue, 'default') ? Vue['default'] : Vue; - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; - } - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - return target; - } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; @@ -47,14 +27,13 @@ Object.defineProperty(target, descriptor.key, descriptor); } } + function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); return Constructor; } + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { @@ -66,30 +45,73 @@ } else { obj[key] = value; } + return obj; } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } + function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } + function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } + function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } + function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; } + function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } @@ -97,59 +119,57 @@ /** * virtual list core calculating center */ - var DIRECTION_TYPE = { FRONT: 'FRONT', // scroll up or left BEHIND: 'BEHIND' // scroll down or right - }; + }; var CALC_TYPE = { INIT: 'INIT', FIXED: 'FIXED', DYNAMIC: 'DYNAMIC' }; var LEADING_BUFFER = 0; + var Virtual = /*#__PURE__*/function () { function Virtual(param, callUpdate) { _classCallCheck(this, Virtual); + this.init(param, callUpdate); } + _createClass(Virtual, [{ key: "init", value: function init(param, callUpdate) { // param data this.param = param; - this.callUpdate = callUpdate; + this.callUpdate = callUpdate; // size data - // size data this.sizes = new Map(); this.firstRangeTotalSize = 0; this.firstRangeAverageSize = 0; this.fixedSizeValue = 0; - this.calcType = CALC_TYPE.INIT; + this.calcType = CALC_TYPE.INIT; // scroll data - // scroll data this.offset = 0; - this.direction = ''; + this.direction = ''; // range data - // range data this.range = Object.create(null); + if (param) { this.checkRange(0, param.keeps - 1); - } - - // benchmark test data + } // benchmark test data // this.__bsearchCalls = 0 // this.__getIndexOffsetCalls = 0 + } }, { key: "destroy", value: function destroy() { this.init(null, null); - } + } // return current render range - // return current render range }, { key: "getRange", value: function getRange() { @@ -169,9 +189,8 @@ key: "isFront", value: function isFront() { return this.direction === DIRECTION_TYPE.FRONT; - } + } // return start index offset - // return start index offset }, { key: "getOffset", value: function getOffset(start) { @@ -181,6 +200,7 @@ key: "updateParam", value: function updateParam(key, value) { var _this = this; + if (this.param && key in this.param) { // if uniqueIds change, find out deleted id and remove from size map if (key === 'uniqueIds') { @@ -190,29 +210,28 @@ } }); } + this.param[key] = value; } - } + } // save each size map by id - // save each size map by id }, { key: "saveSize", value: function saveSize(id, size) { - this.sizes.set(id, size); - - // we assume size type is fixed at the beginning and remember first size value + this.sizes.set(id, size); // we assume size type is fixed at the beginning and remember first size value // if there is no size value different from this at next comming saving // we think it's a fixed size list, otherwise is dynamic size list + if (this.calcType === CALC_TYPE.INIT) { this.fixedSizeValue = size; this.calcType = CALC_TYPE.FIXED; } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) { - this.calcType = CALC_TYPE.DYNAMIC; - // it's no use at all + this.calcType = CALC_TYPE.DYNAMIC; // it's no use at all + delete this.fixedSizeValue; - } + } // calculate the average size only in the first range + - // calculate the average size only in the first range if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== 'undefined') { if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) { this.firstRangeTotalSize = _toConsumableArray(this.sizes.values()).reduce(function (acc, val) { @@ -224,93 +243,97 @@ delete this.firstRangeTotalSize; } } - } - - // in some special situation (e.g. length change) we need to update in a row + } // in some special situation (e.g. length change) we need to update in a row // try goiong to render next range by a leading buffer according to current direction + }, { key: "handleDataSourcesChange", value: function handleDataSourcesChange() { var start = this.range.start; + if (this.isFront()) { start = start - LEADING_BUFFER; } else if (this.isBehind()) { start = start + LEADING_BUFFER; } + start = Math.max(start, 0); this.updateRange(this.range.start, this.getEndByStart(start)); - } + } // when slot size change, we also need force update - // when slot size change, we also need force update }, { key: "handleSlotSizeChange", value: function handleSlotSizeChange() { this.handleDataSourcesChange(); - } + } // calculating range on scroll - // calculating range on scroll }, { key: "handleScroll", value: function handleScroll(offset) { this.direction = offset < this.offset || offset === 0 ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND; this.offset = offset; + if (!this.param) { return; } + if (this.direction === DIRECTION_TYPE.FRONT) { this.handleFront(); } else if (this.direction === DIRECTION_TYPE.BEHIND) { this.handleBehind(); } - } + } // ----------- public method end ----------- - // ----------- public method end ----------- }, { key: "handleFront", value: function handleFront() { - var overs = this.getScrollOvers(); - // should not change range if start doesn't exceed overs + var overs = this.getScrollOvers(); // should not change range if start doesn't exceed overs + if (overs > this.range.start) { return; - } + } // move up start by a buffer length, and make sure its safety + - // move up start by a buffer length, and make sure its safety var start = Math.max(overs - this.param.buffer, 0); this.checkRange(start, this.getEndByStart(start)); } }, { key: "handleBehind", value: function handleBehind() { - var overs = this.getScrollOvers(); - // range should not change if scroll overs within buffer + var overs = this.getScrollOvers(); // range should not change if scroll overs within buffer + if (overs < this.range.start + this.param.buffer) { return; } + this.checkRange(overs, this.getEndByStart(overs)); - } + } // return the pass overs according to current scroll offset - // return the pass overs according to current scroll offset }, { key: "getScrollOvers", value: function getScrollOvers() { // if slot header exist, we need subtract its size var offset = this.offset - this.param.slotHeaderSize; + if (offset <= 0) { return 0; - } + } // if is fixed type, that can be easily + - // if is fixed type, that can be easily if (this.isFixedType()) { return Math.floor(offset / this.fixedSizeValue); } + var low = 0; var middle = 0; var middleOffset = 0; var high = this.param.uniqueIds.length; + while (low <= high) { // this.__bsearchCalls++ middle = low + Math.floor((high - low) / 2); middleOffset = this.getIndexOffset(middle); + if (middleOffset === offset) { return middle; } else if (middleOffset < offset) { @@ -319,50 +342,49 @@ high = middle - 1; } } - return low > 0 ? --low : 0; - } - // return a scroll offset from given index, can efficiency be improved more here? + return low > 0 ? --low : 0; + } // return a scroll offset from given index, can efficiency be improved more here? // although the call frequency is very high, its only a superposition of numbers + }, { key: "getIndexOffset", value: function getIndexOffset(givenIndex) { if (!givenIndex) { return 0; } + var offset = 0; var indexSize = 0; + for (var index = 0; index < givenIndex; index++) { // this.__getIndexOffsetCalls++ indexSize = this.sizes.get(this.param.uniqueIds[index]); offset = offset + (typeof indexSize === 'number' ? indexSize : this.getEstimateSize()); } + return offset; - } + } // is fixed size type - // is fixed size type }, { key: "isFixedType", value: function isFixedType() { return this.calcType === CALC_TYPE.FIXED; - } + } // return the real last index - // return the real last index }, { key: "getLastIndex", value: function getLastIndex() { return this.param.uniqueIds.length - 1; - } - - // in some conditions range is broke, we need correct it + } // in some conditions range is broke, we need correct it // and then decide whether need update to next range + }, { key: "checkRange", value: function checkRange(start, end) { var keeps = this.param.keeps; - var total = this.param.uniqueIds.length; + var total = this.param.uniqueIds.length; // datas less than keeps, render all - // datas less than keeps, render all if (total <= keeps) { start = 0; end = this.getLastIndex(); @@ -370,12 +392,12 @@ // if range length is less than keeps, corrent it base on end start = end - keeps + 1; } + if (this.range.start !== start) { this.updateRange(start, end); } - } + } // setting to a new range and rerender - // setting to a new range and rerender }, { key: "updateRange", value: function updateRange(start, end) { @@ -384,18 +406,16 @@ this.range.padFront = this.getPadFront(); this.range.padBehind = this.getPadBehind(); this.callUpdate(this.getRange()); - } + } // return end base on start - // return end base on start }, { key: "getEndByStart", value: function getEndByStart(start) { var theoryEnd = start + this.param.keeps - 1; var truelyEnd = Math.min(theoryEnd, this.getLastIndex()); return truelyEnd; - } + } // return total front offset - // return total front offset }, { key: "getPadFront", value: function getPadFront() { @@ -404,34 +424,34 @@ } else { return this.getIndexOffset(this.range.start); } - } + } // return total behind offset - // return total behind offset }, { key: "getPadBehind", value: function getPadBehind() { var end = this.range.end; var lastIndex = this.getLastIndex(); + if (this.isFixedType()) { return (lastIndex - end) * this.fixedSizeValue; } + return (lastIndex - end) * this.getEstimateSize(); - } + } // get the item estimate size - // get the item estimate size }, { key: "getEstimateSize", value: function getEstimateSize() { return this.isFixedType() ? this.fixedSizeValue : this.firstRangeAverageSize || this.param.estimateSize; } }]); + return Virtual; }(); /** * props declaration for default, item and slot component */ - var VirtualProps = { dataKey: { type: [String, Function], @@ -459,8 +479,8 @@ direction: { type: String, "default": 'vertical' // the other value is horizontal - }, + }, start: { type: Number, "default": 0 @@ -589,6 +609,7 @@ }, mounted: function mounted() { var _this = this; + if (typeof ResizeObserver !== 'undefined') { this.resizeObserver = new ResizeObserver(function () { _this.dispatchSizeChange(); @@ -616,27 +637,28 @@ this.$parent.$emit(this.event, this.uniqueKey, this.getCurrentSize(), this.hasInitial); } } - }; + }; // wrapping for item - // wrapping for item var Item = Vue.component('virtual-list-item', { mixins: [Wrapper], props: ItemProps, render: function render(h) { var tag = this.tag, - component = this.component, - _this$extraProps = this.extraProps, - extraProps = _this$extraProps === void 0 ? {} : _this$extraProps, - index = this.index, - source = this.source, - _this$scopedSlots = this.scopedSlots, - scopedSlots = _this$scopedSlots === void 0 ? {} : _this$scopedSlots, - uniqueKey = this.uniqueKey, - slotComponent = this.slotComponent; - var props = _objectSpread2(_objectSpread2({}, extraProps), {}, { + component = this.component, + _this$extraProps = this.extraProps, + extraProps = _this$extraProps === void 0 ? {} : _this$extraProps, + index = this.index, + source = this.source, + _this$scopedSlots = this.scopedSlots, + scopedSlots = _this$scopedSlots === void 0 ? {} : _this$scopedSlots, + uniqueKey = this.uniqueKey, + slotComponent = this.slotComponent; + + var props = _objectSpread2({}, extraProps, { source: source, index: index }); + return h(tag, { key: uniqueKey, attrs: { @@ -651,15 +673,14 @@ scopedSlots: scopedSlots })]); } - }); + }); // wrapping for slot - // wrapping for slot var Slot = Vue.component('virtual-list-slot', { mixins: [Wrapper], props: SlotProps, render: function render(h) { var tag = this.tag, - uniqueKey = this.uniqueKey; + uniqueKey = this.uniqueKey; return h(tag, { key: uniqueKey, attrs: { @@ -707,12 +728,10 @@ created: function created() { this.isHorizontal = this.direction === 'horizontal'; this.directionKey = this.isHorizontal ? 'scrollLeft' : 'scrollTop'; - this.installVirtual(); + this.installVirtual(); // listen item size change - // listen item size change - this.$on(EVENT_TYPE.ITEM, this.onItemResized); + this.$on(EVENT_TYPE.ITEM, this.onItemResized); // listen slot size change - // listen slot size change if (this.$slots.header || this.$slots.footer) { this.$on(EVENT_TYPE.SLOT, this.onSlotResized); } @@ -720,6 +739,7 @@ activated: function activated() { // set back offset when awake from keep-alive this.scrollToOffset(this.virtual.offset); + if (this.pageMode) { document.addEventListener('scroll', this.onScroll, { passive: false @@ -737,9 +757,9 @@ this.scrollToIndex(this.start); } else if (this.offset) { this.scrollToOffset(this.offset); - } + } // in page mode we bind scroll event to document + - // in page mode we bind scroll event to document if (this.pageMode) { this.updatePageModeFront(); document.addEventListener('scroll', this.onScroll, { @@ -749,6 +769,7 @@ }, beforeDestroy: function beforeDestroy() { this.virtual.destroy(); + if (this.pageMode) { document.removeEventListener('scroll', this.onScroll); } @@ -774,6 +795,7 @@ // return client viewport size getClientSize: function getClientSize() { var key = this.isHorizontal ? 'clientWidth' : 'clientHeight'; + if (this.pageMode) { return document.documentElement[key] || document.body[key]; } else { @@ -784,6 +806,7 @@ // return all scroll size getScrollSize: function getScrollSize() { var key = this.isHorizontal ? 'scrollWidth' : 'scrollHeight'; + if (this.pageMode) { return document.documentElement[key] || document.body[key]; } else { @@ -798,6 +821,7 @@ document.documentElement[this.directionKey] = offset; } else { var root = this.$refs.root; + if (root) { root[this.directionKey] = offset; } @@ -816,14 +840,15 @@ // set current scroll position to bottom scrollToBottom: function scrollToBottom() { var _this = this; + var shepherd = this.$refs.shepherd; + if (shepherd) { var offset = shepherd[this.isHorizontal ? 'offsetLeft' : 'offsetTop']; - this.scrollToOffset(offset); - - // check if it's really scrolled to the bottom + this.scrollToOffset(offset); // check if it's really scrolled to the bottom // maybe list doesn't render and calculate to last range // so we need retry in next event loop until it really at bottom + setTimeout(function () { if (_this.getOffset() + _this.getClientSize() + 1 < _this.getScrollSize()) { _this.scrollToBottom(); @@ -835,6 +860,7 @@ // taking root offset relative to the browser as slot header size updatePageModeFront: function updatePageModeFront() { var root = this.$refs.root; + if (root) { var rect = root.getBoundingClientRect(); var defaultView = root.ownerDocument.defaultView; @@ -858,9 +884,8 @@ buffer: Math.round(this.keeps / 3), // recommend for a third of keeps uniqueIds: this.getUniqueIdFromDataSources() - }, this.onRangeChanged); + }, this.onRangeChanged); // sync initial range - // sync initial range this.range = this.virtual.getRange(); }, getUniqueIdFromDataSources: function getUniqueIdFromDataSources() { @@ -881,6 +906,7 @@ } else if (type === SLOT_TYPE.FOOTER) { this.virtual.updateParam('slotFooterSize', size); } + if (hasInit) { this.virtual.handleSlotSizeChange(); } @@ -892,22 +918,27 @@ onScroll: function onScroll(evt) { var offset = this.getOffset(); var clientSize = this.getClientSize(); - var scrollSize = this.getScrollSize(); + var scrollSize = this.getScrollSize(); // iOS scroll-spring-back behavior will make direction mistake - // iOS scroll-spring-back behavior will make direction mistake if (offset < 0 || offset + clientSize > scrollSize + 1 || !scrollSize) { return; } + this.virtual.handleScroll(offset); this.emitEvent(offset, clientSize, scrollSize, evt); }, // emit event in special position emitEvent: function emitEvent(offset, clientSize, scrollSize, evt) { this.$emit('scroll', evt, this.virtual.getRange()); - if (this.virtual.isFront() && !!this.dataSources.length && offset - this.topThreshold <= 0) { + + if (this.virtual.isFront() && !!this.dataSources.length && offset <= 0) { this.$emit('totop'); - } else if (this.virtual.isBehind() && offset + clientSize + this.bottomThreshold >= scrollSize) { + } else if (this.virtual.isFront() && !!this.dataSources.length && offset - this.topThreshold <= 0) { + this.$emit('toThresholdTop'); + } else if (this.virtual.isBehind() && offset + clientSize >= scrollSize) { this.$emit('tobottom'); + } else if (this.virtual.isBehind() && offset + clientSize + this.bottomThreshold >= scrollSize) { + this.$emit('toThresholdBottom'); } }, // get the real render slots based on range data @@ -916,22 +947,25 @@ getRenderSlots: function getRenderSlots(h) { var slots = []; var _this$range = this.range, - start = _this$range.start, - end = _this$range.end; + start = _this$range.start, + end = _this$range.end; var dataSources = this.dataSources, - dataKey = this.dataKey, - itemClass = this.itemClass, - itemTag = this.itemTag, - itemStyle = this.itemStyle, - isHorizontal = this.isHorizontal, - extraProps = this.extraProps, - dataComponent = this.dataComponent, - itemScopedSlots = this.itemScopedSlots; + dataKey = this.dataKey, + itemClass = this.itemClass, + itemTag = this.itemTag, + itemStyle = this.itemStyle, + isHorizontal = this.isHorizontal, + extraProps = this.extraProps, + dataComponent = this.dataComponent, + itemScopedSlots = this.itemScopedSlots; var slotComponent = this.$scopedSlots && this.$scopedSlots.item; + for (var index = start; index <= end; index++) { var dataSource = dataSources[index]; + if (dataSource) { var uniqueKey = typeof dataKey === 'function' ? dataKey(dataSource) : dataSource[dataKey]; + if (typeof uniqueKey === 'string' || typeof uniqueKey === 'number') { slots.push(h(Item, { props: { @@ -956,6 +990,7 @@ console.warn("Cannot get the index '".concat(index, "' from data-sources.")); } } + return slots; } }, @@ -963,23 +998,23 @@ // https://vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth render: function render(h) { var _this$$slots = this.$slots, - header = _this$$slots.header, - footer = _this$$slots.footer; + header = _this$$slots.header, + footer = _this$$slots.footer; var _this$range2 = this.range, - padFront = _this$range2.padFront, - padBehind = _this$range2.padBehind; + padFront = _this$range2.padFront, + padBehind = _this$range2.padBehind; var isHorizontal = this.isHorizontal, - pageMode = this.pageMode, - rootTag = this.rootTag, - wrapTag = this.wrapTag, - wrapClass = this.wrapClass, - wrapStyle = this.wrapStyle, - headerTag = this.headerTag, - headerClass = this.headerClass, - headerStyle = this.headerStyle, - footerTag = this.footerTag, - footerClass = this.footerClass, - footerStyle = this.footerStyle; + pageMode = this.pageMode, + rootTag = this.rootTag, + wrapTag = this.wrapTag, + wrapClass = this.wrapClass, + wrapStyle = this.wrapStyle, + headerTag = this.headerTag, + headerClass = this.headerClass, + headerStyle = this.headerStyle, + footerTag = this.footerTag, + footerClass = this.footerClass, + footerStyle = this.footerStyle; var paddingStyle = { padding: isHorizontal ? "0px ".concat(padBehind, "px 0px ").concat(padFront, "px") : "".concat(padFront, "px 0px ").concat(padBehind, "px") }; @@ -989,8 +1024,7 @@ on: { '&scroll': !pageMode && this.onScroll } - }, [ - // header slot + }, [// header slot header ? h(Slot, { "class": headerClass, style: headerStyle, @@ -999,16 +1033,14 @@ event: EVENT_TYPE.SLOT, uniqueKey: SLOT_TYPE.HEADER } - }, header) : null, - // main list + }, header) : null, // main list h(wrapTag, { "class": wrapClass, attrs: { role: 'group' }, style: wrapperStyle - }, this.getRenderSlots(h)), - // footer slot + }, this.getRenderSlots(h)), // footer slot footer ? h(Slot, { "class": footerClass, style: footerStyle, @@ -1017,8 +1049,7 @@ event: EVENT_TYPE.SLOT, uniqueKey: SLOT_TYPE.FOOTER } - }, footer) : null, - // an empty element use to scroll to bottom + }, footer) : null, // an empty element use to scroll to bottom h('div', { ref: 'shepherd', style: { diff --git a/src/index.js b/src/index.js index a3bf684..713043d 100644 --- a/src/index.js +++ b/src/index.js @@ -269,10 +269,14 @@ const VirtualList = Vue.component('virtual-list', { emitEvent (offset, clientSize, scrollSize, evt) { this.$emit('scroll', evt, this.virtual.getRange()) - if (this.virtual.isFront() && !!this.dataSources.length && (offset - this.topThreshold <= 0)) { + if (this.virtual.isFront() && !!this.dataSources.length && (offset <= 0)) { this.$emit('totop') - } else if (this.virtual.isBehind() && (offset + clientSize + this.bottomThreshold >= scrollSize)) { + } else if (this.virtual.isFront() && !!this.dataSources.length && (offset - this.topThreshold <= 0)) { + this.$emit('toThresholdTop') + } else if (this.virtual.isBehind() && (offset + clientSize >= scrollSize)) { this.$emit('tobottom') + } else if (this.virtual.isBehind() && (offset + clientSize + this.bottomThreshold >= scrollSize)) { + this.$emit('toThresholdBottom') } },