<template>
|
|
<view class="yingbing-scroller"
|
|
:refreshState="refreshState" :change:refreshState="pulldownwxs.refreshStateWatcher"
|
|
:pulldownable="pulldownable" :change:pulldownable="pulldownwxs.pulldownableWatcher"
|
|
:pullupable="pullupable" :change:pullupable="pulldownwxs.pullupableWatcher"
|
|
@touchstart="pulldownwxs.touchstart"
|
|
@touchmove="pulldownwxs.touchmove"
|
|
@touchend="pulldownwxs.touchend"
|
|
@touchcancel="pulldownwxs.touchcancel">
|
|
<view class="yingbing-scroller-wrapper">
|
|
<view class="yingbing-scroller-refresh">
|
|
<refresh-loading size="30" :visible="pulldownStatus == 'end' ? false : Boolean(pulldownStatus)" :color="loadingColor"></refresh-loading>
|
|
<text class="yingbing-scroller-refresh-text" :style="{color: loadingColor}">{{ pulldownText }}</text>
|
|
</view>
|
|
<view class="yingbing-scroll">
|
|
<scroll-view
|
|
scroll-anchoing
|
|
class="yingbing-scroll-view"
|
|
scroll-y
|
|
:refresher-enabled="false"
|
|
:scroll-into-view="scrollIntoViewId"
|
|
:scroll-with-animation="scrollWithAnimation"
|
|
:show-scrollbar="false"
|
|
:scroll-top="scrollTop"
|
|
:lower-threshold="100"
|
|
@scroll="handleScroll" @scrolltolower="handleScrolltolower"
|
|
@scrolltoupper="handleScrolltoupper">
|
|
<view :id="'yingbing-scroll-item_' + index" v-for="(item, index) in data" :key="item.index + '_' + item.current">
|
|
<!-- #ifdef MP -->
|
|
<slot :name="'wx:' + index"></slot>
|
|
<!-- #endif -->
|
|
<!-- #ifndef MP -->
|
|
<slot :item="item" :index="index"></slot>
|
|
<!-- #endif -->
|
|
</view>
|
|
<view class="yingbing-scroller-refresh" v-if="loadmoreable" @tap="handleScrolltolower">
|
|
<refresh-loading size="30" :visible="loadmoreStatus == 'end' ? false : Boolean(loadmoreStatus)" :color="loadingColor"></refresh-loading>
|
|
<text class="yingbing-scroller-refresh-text">{{loadmoreText}}</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
<view class="yingbing-scroller-refresh">
|
|
<refresh-loading size="30" :visible="pullupStatus == 'end' ? false : Boolean(pullupStatus)" :color="loadingColor"></refresh-loading>
|
|
<text class="yingbing-scroller-refresh-text" :style="{color: loadingColor}">{{ pullupText }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import RefreshLoading from '../loading/loading.vue'
|
|
const readyHeight = 80
|
|
export default {
|
|
options: {
|
|
addGlobalClass: true,
|
|
virtualHost: true,//将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定
|
|
},
|
|
components: {
|
|
RefreshLoading
|
|
},
|
|
inject: ['getPrevChapterDefaultText', 'getNextChapterDefaultText', 'getChapterReadyText', 'getChapterLoadingText', 'getChapterSuccessText', 'getChapterFailText', 'getPrevChapterEndText', 'getNextChapterEndText'],
|
|
props: {
|
|
data: {
|
|
type: Array,
|
|
default () {
|
|
return new Array
|
|
}
|
|
},
|
|
loadingColor: {
|
|
type: String,
|
|
default: '#333'
|
|
},
|
|
autoplay: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
pulldownable: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
pullupable: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
loadmoreable: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
computed: {
|
|
chapterReadyText () {
|
|
return this.getChapterReadyText()
|
|
},
|
|
chapterLoadingText () {
|
|
return this.getChapterLoadingText()
|
|
},
|
|
chapterSuccessText () {
|
|
return this.getChapterSuccessText()
|
|
},
|
|
chapterFailText () {
|
|
return this.getChapterFailText()
|
|
},
|
|
prevChapterDefaultText () {
|
|
return this.getPrevChapterDefaultText()
|
|
},
|
|
prevChapterEndText() {
|
|
return this.getPrevChapterEndText()
|
|
},
|
|
nextChapterDefaultText() {
|
|
return this.getNextChapterDefaultText()
|
|
},
|
|
nextChapterEndText() {
|
|
return this.getNextChapterEndText()
|
|
},
|
|
pulldownText () {
|
|
return this.pulldownStatus == 'ready' ? this.chapterReadyText : this.pulldownStatus == 'success' ? this.chapterSuccessText : this.pulldownStatus == 'fail' ? this.chapterFailText : this.pulldownStatus == 'end' ? this.prevChapterEndText : this.prevChapterDefaultText
|
|
},
|
|
pullupText () {
|
|
return this.pullupStatus == 'ready' ? this.chapterReadyText : this.pullupStatus == 'success' ? this.chapterSuccessText : this.pullupStatus == 'fail' ? this.chapterFailText : this.pullupStatus == 'end' ? this.nextChapterEndText : this.nextChapterDefaultText
|
|
},
|
|
loadmoreText () {
|
|
return this.loadmoreStatus == 'ready' ? this.chapterLoadingText : this.loadmoreStatus == 'success' ? this.chapterSuccessText : this.loadmoreStatus == 'fail' ? this.chapterFailText : this.loadmoreStatus == 'end' ? this.nextChapterEndText : this.nextChapterDefaultText
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
refreshState: '',
|
|
pulldownStatus: '',
|
|
pullupStatus: '',
|
|
loadmoreStatus: '',
|
|
isLoadmore: false,
|
|
scrollIntoViewId: '',
|
|
scrollWithAnimation: false,
|
|
scrollTop: 0,
|
|
scrollTopRecord: 0
|
|
}
|
|
},
|
|
created() {
|
|
this.startAutoplay()
|
|
},
|
|
beforeDestroy() {
|
|
this.stopAutoplay()
|
|
},
|
|
methods: {
|
|
handleScroll (e) {
|
|
this.scrollTopRecord = e.detail.scrollTop
|
|
this.$emit('scroll', e)
|
|
},
|
|
handleScrolltolower (e) {
|
|
if ( !this.loadmoreable || this.isLoadmore || this.loadmoreStatus == 'end' ) return
|
|
this.isLoadmore = true
|
|
this.loadmoreStatus = 'ready'
|
|
this.$emit('loadmore', (state) => {
|
|
this.loadmoreStatus = state
|
|
this.isLoadmore = false
|
|
})
|
|
},
|
|
handleScrolltoupper (e) {
|
|
this.$emit('scrolltoupper', e)
|
|
},
|
|
pulldown () {
|
|
if ( this.pulldownStatus == 'end' ) {
|
|
this.stopRefresh()
|
|
return
|
|
}
|
|
this.$emit('pulldown', (state) => {
|
|
this.pulldownStatus = state
|
|
this.stopRefresh()
|
|
})
|
|
},
|
|
pullup () {
|
|
if ( this.pullupStatus == 'end' ) {
|
|
this.stopRefresh()
|
|
return
|
|
}
|
|
this.$emit('pullup', (state) => {
|
|
this.pullupStatus = state
|
|
this.stopRefresh()
|
|
})
|
|
},
|
|
startPulldown () {
|
|
this.pulldownStatus = 'ready'
|
|
this.refreshState = ''
|
|
this.$nextTick(function () {
|
|
this.refreshState = 'pulldown'
|
|
})
|
|
},
|
|
startPullup () {
|
|
this.pullupStatus = 'ready'
|
|
this.refreshState = ''
|
|
this.$nextTick(function () {
|
|
this.refreshState = 'pullup'
|
|
})
|
|
},
|
|
stopRefresh () {
|
|
this.refreshState = ''
|
|
this.$nextTick(function () {
|
|
this.refreshState = 'stop'
|
|
})
|
|
},
|
|
resetRefresh () {
|
|
this.pulldownStatus = ''
|
|
this.pullupStatus = ''
|
|
this.refreshState = ''
|
|
this.loadmoreState = ''
|
|
this.isLoadmore = false
|
|
},
|
|
scrollToIndex (index, animated = false) {
|
|
this.stopAutoplay()
|
|
this.scrollIntoViewId = ''
|
|
this.scrollWithAnimation = animated
|
|
this.$nextTick(function () {
|
|
this.scrollIntoViewId = 'yingbing-scroll-item_' + index
|
|
this.$nextTick(function () {
|
|
this.scrollWithAnimation = false
|
|
setTimeout(() => {
|
|
this.startAutoplay()
|
|
}, 400)
|
|
})
|
|
})
|
|
},
|
|
getItemRect (index) {
|
|
return new Promise(resolve => {
|
|
uni.createSelectorQuery().in(this).select('#yingbing-scroll-item_' + index).boundingClientRect(data => {
|
|
resolve(data)
|
|
}).exec();
|
|
})
|
|
},
|
|
pullingdown (threshold) {
|
|
if ( this.pulldownStatus != 'end' ) {
|
|
// #ifndef APP-NVUE
|
|
if ( threshold >= readyHeight ) {
|
|
this.pulldownStatus = 'ready'
|
|
} else {
|
|
this.pulldownStatus = 'pull'
|
|
}
|
|
// #endif
|
|
// #ifdef APP-NVUE
|
|
if ( threshold >= 195 ) {
|
|
this.pulldownStatus = 'ready'
|
|
} else {
|
|
this.pulldownStatus = 'pull'
|
|
}
|
|
// #endif
|
|
}
|
|
},
|
|
pullingup (threshold) {
|
|
if ( this.pullupStatus != 'end' ) {
|
|
// #ifndef APP-NVUE
|
|
if ( threshold >= readyHeight ) {
|
|
this.pullupStatus = 'ready'
|
|
} else {
|
|
this.pullupStatus = 'pull'
|
|
}
|
|
// #endif
|
|
// #ifdef APP-NVUE
|
|
if ( threshold >= 195 ) {
|
|
this.pullupStatus = 'ready'
|
|
} else {
|
|
this.pullupStatus = 'pull'
|
|
}
|
|
// #endif
|
|
}
|
|
},
|
|
setScrollTop (top) {
|
|
this.scrollTop = top
|
|
this.scrollTopRecord = top
|
|
},
|
|
startAutoplay () {
|
|
this.stopAutoplay()
|
|
this.scrollTop = this.scrollTopRecord
|
|
if ( this.autoplay && this.data.length > 0 ) {
|
|
this.autoplayTimer = setInterval(() => {
|
|
if ( this.scrollTop - this.scrollTopRecord > 2 ) this.scrollTop = this.scrollTopRecord//如果scrollTop比滚动距离大2像素,说明发生过等待章节加载,需要重新赋值scrollTop真实的滚动距离
|
|
this.scrollTop += 1
|
|
}, 20)
|
|
}
|
|
},
|
|
stopAutoplay () {
|
|
if ( this.autoplayTimer ) {
|
|
clearInterval(this.autoplayTimer)
|
|
this.autoplayTimer = null
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
autoplay () {
|
|
this.startAutoplay()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<!-- #ifdef APP-VUE || H5 || MP-WEIXIN || MP-QQ -->
|
|
<script module="pulldownwxs" lang="wxs" src="./pulldown.wxs"></script>
|
|
<!-- #endif -->
|
|
|
|
<style scoped>
|
|
/* #ifdef APP-VUE || H5 */
|
|
/deep/ .yingbing-scroll-view .uni-scroll-view::-webkit-scrollbar {
|
|
display: none;
|
|
width: 0 !important;
|
|
height: 0 !important;
|
|
-webkit-appearance: none;
|
|
background: transparent;
|
|
}
|
|
/* #endif */
|
|
/* #ifdef MP */
|
|
/deep/ ::-webkit-scrollbar {
|
|
display: none;
|
|
width: 0 !important;
|
|
height: 0 !important;
|
|
-webkit-appearance: none;
|
|
background: transparent;
|
|
}
|
|
/* #endif */
|
|
.yingbing-scroller {
|
|
/* #ifndef APP-NVUE */
|
|
height: 100%;
|
|
/* #endif */
|
|
/* #ifdef APP-NVUE */
|
|
flex: 1;
|
|
/* #endif */
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.yingbing-scroller-wrapper {
|
|
height: calc(100% + 160px);
|
|
position: absolute;
|
|
top: -80px;
|
|
left: 0;
|
|
right: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.yingbing-scroller-refresh {
|
|
height: 80px;
|
|
padding: 20px 0;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.yingbing-scroller-refresh-text {
|
|
font-size: 13px;
|
|
margin-left: 10px;
|
|
}
|
|
.yingbing-scroll {
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
.yingbing-scroll-view {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
}
|
|
</style>
|