uniapp,下拉刷新组件,也可以上拉
封装
<template>
<view
class="pull-refresh-wrap"
ref="scrollBox"
:style="style"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
<!-- 下拉 -->
<view class="pull-refreshing-box">
<view v-if="moveState < 2">{{
moveState === 0 ? downStartText : downEndText
}}</view>
<view v-else><i class="loading" /> {{ loadingText }}</view>
</view>
<!-- 插入的内容 -->
<view class="pull-present-box">
<slot />
</view>
<!-- 上拉 -->
<view class="pull-refreshing-box">
<view v-if="moveState < 2">{{
moveState === 0 ? upStartText : upEndText
}}</view>
<view v-else><i class="loading" /> {{ loadingText }}</view>
</view>
</view>
</template>
<script>
export default {
name: 'pullRefresh',
props: {
// 上拉还是下拉:up:上拉,down:下拉,默认两边都能拉
direction: {
type: String,
},
// 上拉开始的时候文字
upStartText: {
type: String,
default: '上拉即可刷新...',
},
// 上拉结束的时候文字
upEndText: {
type: String,
default: '释放即可刷新...',
},
// 下拉开始的时候文字
downStartText: {
type: String,
default: '下拉即可刷新...',
},
// 下拉结束的时候文字
downEndText: {
type: String,
default: '释放即可刷新...',
},
// 加载中文字
loadingText: {
type: String,
default: '加载中...',
},
},
data() {
return {
startY: '', //保存touch时的Y坐标
moveDistance: 0, //保存向下滑动的距离
moveState: 0, //开始滑动到结束后状态的变化 0:下拉即可刷新 1:释放即可刷新 2:加载中
duration: 0, //动画持续时间,0就是没有动画
pullDirection: false, // 拉取的方向:ture:下拉,false:上拉
};
},
computed: {
style() {
// 设置了只能下拉
if (this.direction === 'down') {
return {
transition: `${this.duration}ms`,
transform: `translate3d(0,${this.moveDistance}px, 0)`,
};
}
// 设置了只能上拉
if (this.direction === 'up') {
return {
transition: `${this.duration}ms`,
transform: `translate3d(0,${-this.moveDistance}px, 0)`,
};
}
// 默认两端能拉
const dis = this.pullDirection ? this.moveDistance : -this.moveDistance;
return {
transition: `${this.duration}ms`,
transform: `translate3d(0,${dis}px, 0)`,
};
},
},
methods: {
touchStart(e) {
this.duration = 0; // 关闭动画
this.moveDistance = 0; // 滑动距离归0
this.startY = e.touches[0].clientY; // 获得开始Y坐标
},
touchMove(e) {
//这里是整个下拉刷新的核心
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
//首先判断我们有没有滚动条,如果有,我们下拉刷新就不能启用。
if (scrollTop > 0) return;
const move = e.touches[0].clientY - this.startY;
//阻止默认事件,在微信浏览器中尤为有用,至于为什么,你去试就知道了。
e.preventDefault();
// 处理拉取的状态
const handleStatus = () => {
//增加滑动阻力的感觉
const absMove = Math.abs(move);
this.moveDistance = Math.pow(absMove, 0.8);
if (this.moveDistance > 50) {
//如果滑动距离大于50 那我就告诉你,释放即可刷新
if (this.moveState === 1) return;
this.moveState = 1;
} else {
//否则 恢复原样
if (this.moveState === 0) return;
this.moveState = 0;
}
};
if (move > 0 && this.direction !== 'up') {
//判断手指滑动的距离,只有为正数才代表用户下拉了。
this.pullDirection = true;
handleStatus();
} else if (move < 0 && this.direction !== 'down') {
// 用户上拉了
this.pullDirection = false;
handleStatus();
}
},
touchEnd(e) {
// 只要手指拿开,我都需要加上结束时的动画,这里为300ms
this.duration = 300;
if (this.moveDistance > 50) {
//这里逻辑跟touchMove一样,但是需要真的加载数据了,那moveState变为2 所以加载动画在这出现
this.moveState = 2;
//因为还没加载完,我得让加载动画显示着,所以这里移动距离为50
this.moveDistance = 50;
// 上下都能拉,抛出两个事件
if (!this.direction) {
if (this.pullDirection) {
// 上拉
this.$emit('down-refresh', () => {
this.moveState = 0;
});
} else {
// 下拉
this.$emit('up-refresh', () => {
this.moveState = 0;
});
}
} else {
this.$emit('refresh', () => {
//这里就是成功后的回调了,如果该函数被调用,那就以为着加载数据完成,所以状态要回到0,当然需要在父组件调用。
this.moveState = 0;
});
}
} else {
//否则 给我老老实实恢复原样
this.moveDistance = 0;
}
},
},
watch: {
//这里是给用户操作返回的核心
moveState(state) {
//我们监听moveState的状态,
//0意味着开始也意味着结束,这里是结束,并且只有动画生效我们才能 moveDistance 设为0,
//为什么动画生效才行,因为动画生效意味着手指离开了屏幕,如果不懂去看touchEnd方法,这时
//我们让距离变为0才会有动画效果。
if (state === 0 && this.duration === 300) {
this.moveDistance = 0;
}
},
},
};
</script>
<style scoped lang="scss">
.pull-refresh-wrap {
display: flex;
height: calc(100vh - 50px);
flex-direction: column;
margin-top: -50px;
.pull-refreshing-box {
line-height: 50px;
height: 50px;
font-size: 14px;
color: rgba(69, 90, 100, 0.6);
text-align: center;
// margin-bottom: 20px;
}
.pull-present-box {
background-color: lighten(#fff, 10%);
}
}
</style>
使用
<refresh @refresh="refresh" direction="down">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Culpa
tempora, officiis reprehenderit ipsa odio ad magnam. Perspiciatis
numquam quia quod optio error qui iusto tempora, ad eius cupiditate
minima consequuntur officia facere, ducimus velit libero
voluptatibus eaque iste inventore ipsam similique saepe est. Quas,
corporis optio. Veritatis molestiae consequatur quae consequuntur,
praesentium quisquam nihil in unde facere ut repudiandae provident
dolores quis ipsa ab ad, odio maxime ducimus animi nesciunt deserunt
eius soluta. Ad possimus eveniet accusamus ducimus rerum provident
corporis distinctio iure similique. Maxime tempora accusantium nihil
ea, asperiores neque culpa architecto similique, vero ut iusto eaque
sed rem?
</refresh>
methods: {
refresh(done) {
console.log('change..');
setTimeout(() => {
this.num++;
// 一系列 ajax请求。。。
done(); // 请求结束,标记一下
}, 800);
},
}
注意:
默认是两个方向都能拉的,如果不设置direction
,要设置两个触发函数,上下拉触发
<refresh @up-refresh="upRefresh" @down-refresh="downRefresh">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Culpa
tempora, officiis reprehenderit ipsa odio ad magnam. Perspiciatis
numquam quia quod optio error qui iusto tempora, ad eius cupiditate
minima consequuntur officia facere,
</refresh>
API 简单描述一下:
// 上拉还是下拉:up:上拉,down:下拉,默认两边都能拉
direction: {
type: String,
},
// 上拉开始的时候文字
upStartText: {
type: String,
default: '上拉即可刷新...',
},
// 上拉结束的时候文字
upEndText: {
type: String,
default: '释放即可刷新...',
},
// 下拉开始的时候文字
downStartText: {
type: String,
default: '下拉即可刷新...',
},
// 下拉结束的时候文字
downEndText: {
type: String,
default: '释放即可刷新...',
},
// 加载中文字
loadingText: {
type: String,
default: '加载中...',
},