// [z-paging]scroll相关模块 import u from '.././z-paging-utils' import Enum from '.././z-paging-enum' // #ifdef APP-NVUE const weexDom = weex.requireModule('dom'); // #endif export default { props: { //使用页面滚动,默认为否,当设置为是时则使用页面的滚动而非此组件内部的scroll-view的滚动,使用页面滚动时z-paging无需设置确定的高度且对于长列表展示性能更高,但配置会略微繁琐 usePageScroll: { type: Boolean, default: u.gc('usePageScroll', false) }, //是否可以滚动,使用内置scroll-view和nvue时有效,默认为是 scrollable: { type: Boolean, default: u.gc('scrollable', true) }, //控制是否出现滚动条,默认为是 showScrollbar: { type: Boolean, default: u.gc('showScrollbar', true) }, //是否允许横向滚动,默认为否 scrollX: { type: Boolean, default: u.gc('scrollX', false) }, //iOS设备上滚动到顶部时是否允许回弹效果,默认为否。关闭回弹效果后可使滚动到顶部与下拉刷新更连贯,但是有吸顶view时滚动到顶部时可能出现抖动。 scrollToTopBounceEnabled: { type: Boolean, default: u.gc('scrollToTopBounceEnabled', false) }, //iOS设备上滚动到底部时是否允许回弹效果,默认为是。 scrollToBottomBounceEnabled: { type: Boolean, default: u.gc('scrollToBottomBounceEnabled', true) }, //在设置滚动条位置时使用动画过渡,默认为否 scrollWithAnimation: { type: Boolean, default: u.gc('scrollWithAnimation', false) }, //值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素 scrollIntoView: { type: String, default: u.gc('scrollIntoView', '') }, }, data() { return { scrollTop: 0, oldScrollTop: 0, scrollViewStyle: {}, scrollViewContainerStyle: {}, scrollViewInStyle: {}, pageScrollTop: -1, scrollEnable: true, privateScrollWithAnimation: -1, cacheScrollNodeHeight: -1 } }, watch: { oldScrollTop(newVal) { !this.usePageScroll && this._scrollTopChange(newVal,false); }, pageScrollTop(newVal) { this.usePageScroll && this._scrollTopChange(newVal,true); }, usePageScroll: { handler(newVal) { this.loaded && this.autoHeight && this._setAutoHeight(!newVal) // #ifdef H5 if (newVal) { this.$nextTick(()=>{ const mainScrollRef = this.$refs['zp-scroll-view'].$refs.main; if (mainScrollRef) { mainScrollRef.style = {}; } }) } // #endif }, immediate: true }, finalScrollTop(newVal) { if (!this.useChatRecordMode) { this.renderPropScrollTop = newVal < 6 ? 0 : 10; } }, }, computed: { finalScrollWithAnimation() { if (this.privateScrollWithAnimation !== -1) { const scrollWithAnimation = this.privateScrollWithAnimation === 1; this.privateScrollWithAnimation = -1; return scrollWithAnimation; } return this.scrollWithAnimation; }, finalScrollViewStyle() { if (this.superContentZIndex != 1) { this.scrollViewStyle['z-index'] = this.superContentZIndex; this.scrollViewStyle['position'] = 'relative'; } return this.scrollViewStyle; }, finalScrollTop() { return this.usePageScroll ? this.pageScrollTop : this.oldScrollTop; }, finalIsOldWebView() { return this.isOldWebView && !this.usePageScroll; } }, methods: { //滚动到顶部,animate为是否展示滚动动画,默认为是 scrollToTop(animate, checkReverse = true) { // #ifdef APP-NVUE if (checkReverse && this.useChatRecordMode) { if(!this.nIsFirstPageAndNoMore){ this.scrollToBottom(animate, false); return; } } // #endif this.$nextTick(() => { this._scrollToTop(animate, false); // #ifdef APP-NVUE if (this.nvueFastScroll && animate) { setTimeout(() => { this._scrollToTop(false, false); }, 150); } // #endif }) }, //滚动到底部,animate为是否展示滚动动画,默认为是 scrollToBottom(animate, checkReverse = true) { // #ifdef APP-NVUE if (checkReverse && this.useChatRecordMode) { if(!this.nIsFirstPageAndNoMore){ this.scrollToTop(animate, false); return; } } // #endif this.$nextTick(() => { this._scrollToBottom(animate); // #ifdef APP-NVUE if (this.nvueFastScroll && animate) { setTimeout(() => { this._scrollToBottom(false); }, 150); } // #endif }) }, //滚动到指定view(vue中有效)。sel为需要滚动的view的id值,不包含"#";offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否 scrollIntoViewById(sel, offset, animate) { this._scrollIntoView(sel, offset, animate); }, //滚动到指定view(vue中有效)。nodeTop为需要滚动的view的top值(通过uni.createSelectorQuery()获取);offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否 scrollIntoViewByNodeTop(nodeTop, offset, animate) { this.scrollTop = this.oldScrollTop; this.$nextTick(() => { this._scrollIntoViewByNodeTop(nodeTop, offset, animate); }) }, //滚动到指定位置(vue中有效)。y为与顶部的距离,单位为px;offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否 scrollToY(y, offset, animate) { this.scrollTop = this.oldScrollTop; this.$nextTick(() => { this._scrollToY(y, offset, animate); }) }, //滚动到指定view(nvue中有效)。index为需要滚动的view的index(第几个);offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否 scrollIntoViewByIndex(index, offset, animate) { this._scrollIntoView(index, offset, animate); }, //滚动到指定view(nvue中有效)。view为需要滚动的view(通过`this.$refs.xxx`获取),不包含"#";offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否 scrollIntoViewByView(view, offset, animate) { this._scrollIntoView(view, offset, animate); }, //当使用页面滚动并且自定义下拉刷新时,请在页面的onPageScroll中调用此方法,告知z-paging当前的pageScrollTop,否则会导致在任意位置都可以下拉刷新 updatePageScrollTop(value) { this.pageScrollTop = value; }, //当使用页面滚动并且设置了slot="top"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="top"的view高度动态改变时,在其高度需要更新时调用此方法 updatePageScrollTopHeight() { this._updatePageScrollTopOrBottomHeight('top'); }, //当使用页面滚动并且设置了slot="bottom"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="bottom"的view高度动态改变时,在其高度需要更新时调用此方法 updatePageScrollBottomHeight() { this._updatePageScrollTopOrBottomHeight('bottom'); }, //更新slot="left"和slot="right"宽度,当slot="left"或slot="right"宽度动态改变时调用 updateLeftAndRightWidth() { this.$nextTick(() => { this._updateLeftAndRightWidth(); }) }, //更新z-paging内置scroll-view的scrollTop updateScrollViewScrollTop(scrollTop, animate = true) { this.privateScrollWithAnimation = animate ? 1 : 0; this.scrollTop = this.oldScrollTop; this.$nextTick(() => { this.scrollTop = scrollTop; this.oldScrollTop = this.scrollTop; }); }, //当滚动到顶部时 _scrollToUpper() { this.$emit('scrolltoupper'); this.$emit('scrollTopChange', 0); this.$nextTick(() => { this.oldScrollTop = 0; }) if (!this.useChatRecordMode) return; if (this.loadingStatus === Enum.More.NoMore) return; this._onLoadingMore('click'); }, //滚动到顶部 _scrollToTop(animate = true, isPrivate = true) { // #ifdef APP-NVUE const el = this.$refs['zp-n-list-top-tag']; if (this.usePageScroll) { this._getNodeClientRect('zp-page-scroll-top', false).then((node) => { const nodeHeight = node ? node[0].height : 0; weexDom.scrollToElement(el, { offset: -nodeHeight, animated: animate }); }); } else { if (!this.isIos && this.nvueListIs === 'scroller') { this._getNodeClientRect('zp-n-refresh-container', false).then((node) => { const nodeHeight = node ? node[0].height : 0; weexDom.scrollToElement(el, { offset: -nodeHeight, animated: animate }); }); } else { weexDom.scrollToElement(el, { offset: 0, animated: animate }); } } return; // #endif if (this.usePageScroll) { this.$nextTick(() => { uni.pageScrollTo({ scrollTop: 0, duration: animate ? 100 : 0, }); }); return; } this.privateScrollWithAnimation = animate ? 1 : 0; this.scrollTop = this.oldScrollTop; this.$nextTick(() => { this.scrollTop = 0; this.oldScrollTop = this.scrollTop; }); }, //滚动到底部 async _scrollToBottom(animate = true) { // #ifdef APP-NVUE const el = this.$refs['zp-n-list-bottom-tag']; if (el) { weexDom.scrollToElement(el, { offset: 0, animated: animate }); } else { u.consoleErr('滚动到底部失败,因为您设置了hideNvueBottomTag为true'); } return; // #endif if (this.usePageScroll) { this.$nextTick(() => { uni.pageScrollTo({ scrollTop: Number.MAX_VALUE, duration: animate ? 100 : 0, }); }); return; } try { this.privateScrollWithAnimation = animate ? 1 : 0; const pagingContainerNode = await this._getNodeClientRect('.zp-paging-container'); const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view'); const pagingContainerH = pagingContainerNode ? pagingContainerNode[0].height : 0; const scrollViewH = scrollViewNode ? scrollViewNode[0].height : 0; if (pagingContainerH > scrollViewH) { this.scrollTop = this.oldScrollTop; this.$nextTick(() => { this.scrollTop = pagingContainerH - scrollViewH + this.virtualPlaceholderTopHeight; this.oldScrollTop = this.scrollTop; }); } } catch (e) {} }, //滚动到指定view _scrollIntoView(sel, offset = 0, animate = false, finishCallback) { try { this.scrollTop = this.oldScrollTop; this.$nextTick(() => { // #ifdef APP-NVUE const refs = this.$parent.$refs; if (!refs) return; const dataType = Object.prototype.toString.call(sel); let el = null; if (dataType === '[object Number]') { const els = refs[`z-paging-${sel}`]; el = els ? els[0] : null; } else if (dataType === '[object Array]') { el = sel[0]; } else { el = sel; } if (el) { weexDom.scrollToElement(el, { offset: -offset, animated: animate }); } else { u.consoleErr('在nvue中滚动到指定位置,cell必须设置 :ref="`z-paging-${index}`"'); } return; // #endif if (sel.indexOf('#') != -1) { sel = sel.replace('#', ''); } this._getNodeClientRect('#' + sel, false).then((node) => { if (node) { let nodeTop = node[0].top; this._scrollIntoViewByNodeTop(nodeTop, offset, animate); if (finishCallback) { finishCallback(); } } }); }); } catch (e) {} }, //通过nodeTop滚动到指定view _scrollIntoViewByNodeTop(nodeTop, offset = 0, animate = false) { this._scrollToY(nodeTop, offset, animate, true); }, //滚动到指定位置 _scrollToY(y, offset = 0, animate = false, addScrollTop = false) { this.privateScrollWithAnimation = animate ? 1 : 0; if (this.usePageScroll) { uni.pageScrollTo({ scrollTop: y - offset, duration: animate ? 100 : 0 }); } else { if(addScrollTop){ y += this.oldScrollTop; } this.scrollTop = y - offset; this.oldScrollTop = this.scrollTop; } }, //scroll-view滚动中 _scroll(e) { this.$emit('scroll', e); const scrollTop = e.detail.scrollTop; // #ifndef APP-NVUE this.finalUseVirtualList && this._updateVirtualScroll(scrollTop, this.oldScrollTop - scrollTop); // #endif this.oldScrollTop = scrollTop; const scrollDiff = e.detail.scrollHeight - this.oldScrollTop; !this.isIos && this._checkScrolledToBottom(scrollDiff); }, //scrollTop改变时触发 _scrollTopChange(newVal, isPageScrollTop){ this.$emit('scrollTopChange', newVal); this.$emit('update:scrollTop', newVal); this._checkShouldShowBackToTop(newVal); const scrollTop = this.isIos ? (newVal > 5 ? 6 : 0) : newVal; if (isPageScrollTop) { this.wxsPageScrollTop = scrollTop; } else { this.wxsScrollTop = scrollTop; } }, //更新使用页面滚动时slot="top"或"bottom"插入view的高度 _updatePageScrollTopOrBottomHeight(type) { // #ifndef APP-NVUE if (!this.usePageScroll) return; // #endif this._doCheckScrollViewShouldFullHeight(this.realTotalData); const node = `.zp-page-${type}`; const marginText = `margin${type.slice(0,1).toUpperCase() + type.slice(1)}`; let safeAreaInsetBottomAdd = this.safeAreaInsetBottom; this.$nextTick(() => { let delayTime = 0; // #ifdef MP-BAIDU || APP-NVUE delayTime = 50; // #endif setTimeout(() => { this._getNodeClientRect(node).then((res) => { if (res) { let pageScrollNodeHeight = res[0].height; if (type === 'bottom') { if (safeAreaInsetBottomAdd) { pageScrollNodeHeight += this.safeAreaBottom; } } else { this.cacheTopHeight = pageScrollNodeHeight; } this.$set(this.scrollViewStyle, marginText, `${pageScrollNodeHeight}px`); } else if (safeAreaInsetBottomAdd) { this.$set(this.scrollViewStyle, marginText, `${this.safeAreaBottom}px`); } }); }, delayTime) }) }, //获取slot="left"和slot="right"宽度并且更新布局 _updateLeftAndRightWidth(){ if (!this.finalIsOldWebView) return; this.$nextTick(() => { let delayTime = 0; // #ifdef MP-BAIDU delayTime = 10; // #endif setTimeout(() => { ['left','right'].map(position => { this._getNodeClientRect(`.zp-page-${position}`).then((res) => { this.$set(this.scrollViewContainerStyle, position, res ? res[0].width + 'px' : '0px'); }); }) }, delayTime) }) } } }