四零语境前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

358 lines
9.9 KiB

<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>