Created
April 2, 2019 11:44
-
-
Save supernovel/6eb86700f435a06ef6b1588933261437 to your computer and use it in GitHub Desktop.
vue-scroller.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template lang="pug"> | |
div(ref='container' :class='$style.container') | |
div(ref='content' :class='$style.content') | |
div(:class='$style.listWrapper') | |
ul(:class='$style.list') | |
slot( | |
name='item' | |
:class='$style.listItem' | |
@click='clickItem($event, item)' | |
v-for='item in items' | |
:item='item' | |
) | |
span {{ item }} | |
div(:class='$style.loadingLayer' v-if='pullUpLoad') | |
div(:class='$style.loadingBefore' v-if='isPullUpLoad') | |
slot(name='loading-before') | |
span {{ loadingText }} | |
div(:class='$style.loadingAfter' v-else) | |
slot(name='loading-after') | |
loading(:class='$style.loading') | |
div(ref='refresh' :class='$style.refreshLayer' v-if='pullDownRefresh') | |
div(:class='$style.refreshBefore' v-if='beforePullDown') | |
slot(name='refresh-before') | |
arrow(:class='$style.arrow' :style="{ 'transform': `rotate(${arrowDegree}deg)` }") | |
div(:class='$style.refreshAfter' v-else) | |
div(:class='$style.refreshLoading' v-if='isPullingDown') | |
slot(name='refresh-loading') | |
loading(:class='$style.loading') | |
div(:class='$style.refreshDone' v-else) | |
slot(name='refresh-done') | |
span {{ refreshText }} | |
</template> | |
<script> | |
import BScroll from 'better-scroll'; | |
import Loading from './Loading.vue'; | |
import Arrow from './Arrow.vue'; | |
export default { | |
name: 'scroll', | |
components: { | |
loading: Loading, | |
arrow: Arrow | |
}, | |
props: { | |
items: { | |
type: Array, | |
default: function() { | |
return []; | |
} | |
}, | |
probeType: { | |
type: Number, | |
default: 1 | |
}, | |
click: { | |
type: Boolean, | |
default: true | |
}, | |
scrollX: { | |
type: Boolean, | |
default: false //vertical || 'h' //horizontal | |
}, | |
scrollY: { | |
type: Boolean, | |
default: true | |
}, | |
pullDownRefresh: { | |
type: [Boolean, Object], | |
default: false | |
}, | |
pullUpLoad: { | |
type: [Boolean, Object], | |
default: false | |
}, | |
mouseWheel: { | |
type: [Boolean, Object], | |
default: true | |
}, | |
stopPropagation: { | |
type: Boolean, | |
default: true | |
} | |
}, | |
data() { | |
return { | |
beforePullDown: true, | |
isRebounding: false, | |
isPullingDown: false, | |
isPullUpLoad: false, | |
pullUpDirty: true, | |
pullDownStyle: '', | |
arrowDegree: 0 | |
}; | |
}, | |
watch: { | |
items() { | |
setTimeout(() => { | |
this.forceUpdate(true); | |
}, this.refreshDelay); | |
} | |
}, | |
computed: { | |
loadingText() { | |
const moreTxt = | |
(this.pullUpLoad && | |
this.pullUpLoad.text && | |
this.pullUpLoad.text.more) || 'More'; | |
const noMoreTxt = | |
(this.pullUpLoad && | |
this.pullUpLoad.text && | |
this.pullUpLoad.text.noMore) || 'NoMore'; | |
return this.pullUpDirty ? moreTxt : noMoreTxt; | |
}, | |
refreshText() { | |
return ( | |
(this.pullDownRefresh && this.pullDownRefresh.text) || 'Refresh' | |
); | |
} | |
}, | |
created() { | |
this.pullDownInitTop = -50; | |
}, | |
mounted() { | |
this.initScroll(); | |
}, | |
destroyed() { | |
this.scroll && this.scroll.destroy(); | |
}, | |
methods: { | |
initScroll() { | |
if (!this.$refs.container) { | |
return; | |
} | |
if ( | |
this.$refs.content && | |
(this.pullDownRefresh || this.pullUpLoad) | |
) { | |
this.$refs.content.style.minHeight = `${this.$refs.container.getBoundingClientRect() | |
.height + | |
1 + | |
1}px`; | |
} | |
let options = { | |
probeType: this.probeType, | |
click: this.click, | |
scrollY: this.scrollY, | |
scrollX: this.scrollX, | |
scrollbar: this.scrollbar, | |
pullDownRefresh: this.pullDownRefresh, | |
pullUpLoad: this.pullUpLoad, | |
mouseWheel: this.mouseWheel, | |
stopPropagation: this.stopPropagation | |
}; | |
this.scroll = new BScroll(this.$refs.container, options); | |
if (this.pullDownRefresh) { | |
this._initPullDownRefresh(); | |
} | |
if (this.pullUpLoad) { | |
this._initPullUpLoad(); | |
} | |
}, | |
disable() { | |
this.scroll && this.scroll.disable(); | |
}, | |
enable() { | |
this.scroll && this.scroll.enable(); | |
}, | |
refresh() { | |
this.scroll && this.scroll.refresh(); | |
}, | |
scrollTo() { | |
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments); | |
}, | |
autoPullDownRefresh() { | |
this.scroll && this.scroll.autoPullDownRefresh(); | |
}, | |
scrollToElement() { | |
this.scroll && | |
this.scroll.scrollToElement.apply(this.scroll, arguments); | |
}, | |
clickItem(e, item) { | |
this.$emit('click', item); | |
}, | |
destroy() { | |
this.scroll.destroy(); | |
}, | |
forceUpdate(dirty) { | |
if (this.pullDownRefresh && this.isPullingDown) { | |
this.isPullingDown = false; | |
this._reboundPullDown().then(() => { | |
this._afterPullDown(); | |
}); | |
} else if (this.pullUpLoad && this.isPullUpLoad) { | |
this.isPullUpLoad = false; | |
this.scroll.finishPullUp(); | |
this.pullUpDirty = dirty; | |
this.refresh(); | |
} else { | |
this.refresh(); | |
} | |
}, | |
_initPullDownRefresh() { | |
const { threshold = 90, stop = 40 } = this.pullDownRefresh; | |
this.scroll.on('pullingDown', () => { | |
this.beforePullDown = false; | |
this.isPullingDown = true; | |
this.$emit('pullingDown'); | |
}); | |
this.scroll.on('scroll', pos => { | |
if (!this.pullDownRefresh) { | |
return; | |
} | |
if (this.beforePullDown) { | |
if(pos.y > threshold){ | |
this.arrowDegree = 180; | |
}else{ | |
this.arrowDegree = 0; | |
} | |
this.pullDownStyle = `top:${Math.min( | |
pos.y + this.pullDownInitTop, | |
10 | |
)}px`; | |
}else{ | |
this.arrowDegree = 0; | |
} | |
if (this.isRebounding) { | |
this.pullDownStyle = `top:${10 - (stop - pos.y)}px`; | |
} | |
}); | |
}, | |
_initPullUpLoad() { | |
this.scroll.on('pullingUp', () => { | |
this.isPullUpLoad = true; | |
this.$emit('pullingUp'); | |
}); | |
}, | |
_reboundPullDown() { | |
const { stopTime = 600 } = this.pullDownRefresh; | |
return new Promise(resolve => { | |
setTimeout(() => { | |
this.isRebounding = true; | |
this.scroll.finishPullDown(); | |
resolve(); | |
}, stopTime); | |
}); | |
}, | |
_afterPullDown() { | |
setTimeout(() => { | |
this.pullDownStyle = `top:${this.pullDownInitTop}px`; | |
this.beforePullDown = true; | |
this.isRebounding = false; | |
this.refresh(); | |
}, this.scroll.options.bounceTime); | |
} | |
} | |
}; | |
</script> | |
<style lang="scss" module> | |
.container { | |
position: relative; | |
height: 100%; | |
overflow: hidden; | |
background: #fff; | |
.content { | |
position: relative; | |
z-index: 1; | |
} | |
.listWrapper { | |
position: relative; | |
z-index: 10; | |
background: #fff; | |
.list{ | |
.listItem { | |
height: 60px; | |
line-height: 60px; | |
font-size: 18px; | |
padding-left: 20px; | |
border-bottom: 1px solid #e5e5e5; | |
} | |
} | |
} | |
} | |
.refreshLayer { | |
position: absolute; | |
width: 100%; | |
left: 0; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
.refreshBefore { | |
.arrow{ | |
width: 20px; | |
height: 20px; | |
transition: transform .2s linear; | |
} | |
} | |
.refreshAfter { | |
margin-top: 10px; | |
.refreshLoading{ | |
} | |
.refreshDone{ | |
} | |
} | |
} | |
.loadingLayer { | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
padding: 16px 0; | |
.loadingBefore{ | |
} | |
.loadingAfter{ | |
} | |
} | |
.loading{ | |
width: 20px; | |
height: 20px; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment