// referenced: https://github.com/bedlate/vue-data-loading
<template>
<div class="vue-data-loading">
<div class="loading-header" :px'}" v-show="PULL_DOWN === pull.type">
<div class="header-text" v-show="!pull.available">
<slot :name="PULL_DOWN + '-before'">下拉刷新数据</slot>
</div>
<div class="header-text" v-show="pull.available && PULL_DOWN !== loadingType">
<slot :name="PULL_DOWN + '-ready'">松开刷新数据</slot>
</div>
<div class="header-text" v-show="PULL_DOWN === loadingType">
<slot :name="PULL_DOWN + '-loading'">刷新中...</slot>
</div>
</div>
<div class="loading-content">
<slot></slot>
</div>
<div class="loading-footer" :px'}" v-show="PULL_UP === pull.type">
<div class="footer-text" v-show="!pull.available">
<slot :name="PULL_UP + '-before'">上拉加载数据</slot>
</div>
<div class="footer-text" v-show="pull.available && PULL_UP !== loadingType">
<slot :name="PULL_UP + '-ready'">松开加载数据</slot>
</div>
<div class="footer-text" v-show="PULL_UP === loadingType">
<slot :name="PULL_UP + '-loading'">加载中...</slot>
</div>
</div>
<transition name="fade">
<div class="loading-footer" v-show="loading && INFINITE_SCROLL === loadingType">
<div class="footer-text">
<slot :name="INFINITE_SCROLL + '-loading'">加载中...</slot>
</div>
</div>
</transition>
<div class="loading-footer" v-show="!loading && completed">
<div class="footer-text">
<slot name="completed">加载完毕</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
loading: {
type: Boolean,
default: false
},
completed: {
type: Boolean,
default: false
},
distance: {
type: Number,
default: 60
},
offset: {
type: Number,
default: 0
},
listens: {
type: Array,
default() {
return ['infinite-scroll', 'pull-down', 'pull-up']
}
},
container: {
type: String,
},
initScroll: {
type: Boolean,
default: false,
},
},
data() {
return {
margin: {
top: 0,
bottom: 0,
},
pull: {
from: -1,
to: -1,
distance: 0,
type: null,
available: false,
},
loadingType: null,
PULL_UP: 'pull-up',
PULL_DOWN: 'pull-down',
INFINITE_SCROLL: 'infinite-scroll',
}
},
computed: {
_container() {
return this.container ? this.$parent.$refs[this.container] : window.window
},
pullHeight() {
return this.pull.distance > this.distance ? this.distance : this.pull.distance
},
},
watch: {
loading(val, oldVal) {
if (oldVal && !val) {
this.resetPull()
this.setLoadingType()
}
}
},
methods: {
updateView() {
let {top, height} = this.$el.getBoundingClientRect()
this.margin = {
top,
bottom: window.innerHeight - (height + top + this.offset)
}
},
setLoadingType(type = null) {
this.loadingType = type
},
handleScroll() {
this.updateView()
if (this.loading || this.completed) {
return
}
if (this.margin.bottom >= 0) {
this.$emit(this.INFINITE_SCROLL)
this.setLoadingType(this.INFINITE_SCROLL)
}
},
handleTouchStart(e) {
if (this.loading || !(this.hasListen(this.PULL_UP) || this.hasListen(this.PULL_DOWN)) || (this.margin.top < 0 && this.margin.bottom < 0)) {
return
}
this.pull.from = e.touches.item(0).pageY
},
handleTouchMove(e) {
if (this.loading || this.pull.from < 0) {
return
}
this.pull.to = e.touches.item(0).pageY
let distance = this.pull.to - this.pull.from
if (distance > 0 && this.margin.top >0 && this.hasListen(this.PULL_DOWN)) { // pull down
this.pull.type = this.PULL_DOWN
} else if (distance < 0 && this.margin.bottom > 0 && this.hasListen(this.PULL_UP)) { // pull up
this.pull.type = this.PULL_UP
} else {
this.pull.type = null
}
this.pull.distance = Math.abs(distance)
this.pull.available = this.pull.distance >= this.distance
},
handleTouchEnd() {
if (this.pull.distance >= this.distance) {
if (this.PULL_UP === this.pull.type || this.PULL_DOWN === this.pull.type) {
this.$emit(this.pull.type)
this.setLoadingType(this.pull.type)
}
} else {
this.resetPull()
}
},
resetPull() {
this.pull = {
from: -1,
to: -1,
distance: 0,
type: null,
available: false,
}
},
bindEvents() {
// scroll
if (this.hasListen(this.INFINITE_SCROLL)) {
['scroll', 'resize'].forEach(e => {
this._container.addEventListener(e, this.handleScroll)
})
}
// touch
if (this.hasListen(this.PULL_UP) || this.hasListen(this.PULL_DOWN)) {
this._container.addEventListener('touchstart', this.handleTouchStart)
this._container.addEventListener('touchmove', this.handleTouchMove)
this._container.addEventListener('touchend', this.handleTouchEnd)
}
},
hasListen(event) {
return this.listens.indexOf(event) >= 0
},
init() {
this.bindEvents()
this.updateView()
if (this.initScroll) {
this.handleScroll()
}
},
},
mounted() {
this.$nextTick(() => {
this.init()
})
},
}
</script>