一:在components文件下创建refresh文件夹(refresh/index)
<template>
<scroll-view style="height: 100%;" :style="{'background-color': bgColor}"
:scroll-y="allow_scroll_y" :refresher-enabled="false"
@scroll="scroll" @scrolltolower="bottomTolower"
>
<!-- <view style="height: var(--status-bar-height);"></view> -->
<view class="refresh-moudle"
@touchstart="touchStart($event)"
@touchmove="touchMove($event)"
@touchend="touchEnd($event)"
:style="{'margin-top': showTopLoad ? `calc(${top}px - 180rpx)` : '0'}">
<!-- :style="{transform: 'translate3d(0,' + top + 'px, 0)'}"
> -->
<view class="pull-refresh" :style="{'display': showTopLoad? 'block': 'none'}">
<slot name="pull-refresh">
<view class="down-tip" v-if="dropDownState==1">
<uv-icon :color="contentColor" class="down-img" name="arrow-downward"></uv-icon>
<!-- <image v-if="dropDownInfo.downImg" class="down-img" :src="dropDownInfo.downImg"></image> -->
<view class="down-text" :style="{'color': contentColor}">{{downText}}</view>
</view>
<view class="up-tip" v-if="dropDownState==2">
<uv-icon :color="contentColor" class="up-img" name="arrow-downward"></uv-icon>
<!-- <image v-if="dropDownInfo.upImg" class="up-img" :src="dropDownInfo.upImg"></image> -->
<view class="up-text" :style="{'color': contentColor}">{{upText}}</view>
</view>
<view class="refresh-tip" v-if="dropDownState==3">
<!-- <div class="loader"></div> -->
<uv-loading-icon mode="spinner" :color="contentColor" :textColor="contentColor"></uv-loading-icon>
<!-- <image v-if="dropDownInfo.refreshImg" class="refresh-img" :src="dropDownInfo.refreshImg"></image> -->
<view class="refresh-text" style="margin-left: 15px;" :style="{'color': contentColor}">{{refreshText}}</view>
</view>
</slot>
</view>
<slot></slot>
<view v-if="!showTopLoad" style="width: 100%; margin: 10rpx 0;">
<slot name="push-refresh">
<view class="flex" v-if="!showMore">
<view class="refresh-text" style="margin-left: 15px;" :style="{'color': contentColor}">没有更多数据了</view>
</view>
<view class="flex" v-else-if="pushLoading">
<uv-loading-icon mode="spinner" :color="contentColor" ></uv-loading-icon>
<view class="refresh-text" style="margin-left: 15px;" :style="{'color': contentColor}">加载中...</view>
</view>
<view class="flex" v-else>
<uv-icon :color="contentColor" class="up-img" name="arrow-upward"></uv-icon>
<view class="up-text" :style="{'color': contentColor}">上拉显示更多</view>
</view>
</slot>
</view>
</view>
</scroll-view>
</template>
<script>
export default {
name: "refreshCustom",
props: {
bgColor: {
type: String,
default: '#A03AFF'
},
loadColor: {
type: String,
default: 'rgb(60,15,95)'
},
contentColor: {
type: String,
default: 'rgb(60,15,95)'
},
downText: {
type: String,
default: '下拉刷新'
},
upText: {
type: String,
default: '释放立即刷新'
},
refreshText: {
type: String,
default: '刷新中...'
},
/**
* 显示下拉功能,true:下拉刷新,false:上拉刷新
*/
showTopLoad: {
type: Boolean,
default: true
},
/**
* 下拉刷新中,是否可以继续加载数据
*/
showMore: {
type: Boolean,
default: false
}
},
data () {
return {
defaultOffset: 80, // 下拉偏移高度, 如果要改建议相应的修改.releshMoudle的margin-top和.down-tip, .up-tip, .refresh-tip的height
top: 0,
startY: 0,
isDropDown: false, // 是否下拉
isRefreshing: false, // 是否正在刷新
dropDownState: 1, // 显示1:下拉可以刷新, 2:松开立即刷新, 3:正在刷新数据中...
scrollTop: 0,
allow_scroll_y: true,//解决因ios环境下scrollview的scrolltop可下拉为负数带来的体验问题
allowTouchMove: true, //控制是否允许touchMove事件
pushLoading: false,
}
},
methods: {
scroll: function(e) {
this.$nextTick(function() {
this.scrollTop = e.detail.scrollTop
});
},
/**
* 触摸开始,手指点击屏幕时
* @param {object} e Touch 对象包含的属性
*/
touchStart (e) {
if (!this.showTopLoad) {
return
}
this.startY = e.changedTouches[0].pageY
this.startScrollTop = this.scrollTop
this.allowTouchMove = true //控制是否允许touchMove事件
},
/**
* 接触点改变,滑动时
* @param {object} e Touch 对象包含的属性
*/
touchMove (e) {
//控制是否允许touchMove事件
if(!this.allowTouchMove) {
return
}
//解决因ios环境下scrollview的scrolltop可下拉为负数带来的体验问题
this.$nextTick(function() {
if(this.scrollTop<0) {
this.allow_scroll_y = false
} else {
this.allow_scroll_y = true
}
});
if(this.scrollTop <= 0) {
if (e.changedTouches[0].pageY > this.startY) {
// 下拉
this.isDropDown = true
if (!this.isRefreshing) {
// 获取拉取的间隔差 当前移动的y点 初始的y点 初始顶部距离
let diff = e.changedTouches[0].pageY - this.startY - this.startScrollTop
this.top = Math.pow(diff, 0.8) + (this.dropDownState === 3 ? this.defaultOffset : 0);
if (this.top >= this.defaultOffset) {
this.dropDownState = 2
e.preventDefault();
} else {
this.dropDownState = 1
// 去掉会导致ios无法刷新
e.preventDefault();
}
}
} else {
this.isDropDown = false
this.dropDownState = 1
}
}
},
/**
* 触摸结束,手指离开屏幕时
* @param {object} e Touch 对象包含的属性
*/
touchEnd (e) {
//解决因ios环境下scrollview的scrolltop可下拉为负数带来的体验问题
this.$nextTick(function() {
this.allow_scroll_y = true
});
if (this.isDropDown && !this.isRefreshing) {
if (this.top >= this.defaultOffset) {
// do refresh
this.isRefreshing = true
this.refresh();
} else {
// cancel refresh
this.isRefreshing = false
this.isDropDown = false
this.dropDownState = 1
this.top = 0
}
}
},
/**
* 刷新
*/
refresh () {
this.dropDownState = 3
this.top = this.defaultOffset
// 获取数据,结束正在刷新动画
this.$emit('onRefresh', { data: 'data123', cb: () => {
this.refreshDone()
}});
setTimeout(() => {
this.refreshDone()
}, 30000);
},
/**
* 刷新完成
*/
refreshDone () {
this.isRefreshing = false
this.isDropDown = false
this.dropDownState = 1
this.top = 0
},
/**
* 上拉方法
*/
bottomTolower() {
console.log('--- bottomTolowerbottomTolower')
this.pushLoading = true
this.$emit('onBottomRefresh', { cb: () => {
this.pushLoading = false
}})
setTimeout(() => {
this.pushLoading = false
}, 30000);
}
}
}
</script>
<style>
/* HTML: <div class="loader"></div> */
.loader {
width: 25px;
aspect-ratio: 1;
display: grid;
border-radius: 50%;
/* background:
linear-gradient(0deg ,rgb(0 0 0/50%) 30%,#0000 0 70%,rgb(0 0 0/100%) 0) 50%/8% 100%,
linear-gradient(90deg,rgb(0 0 0/25%) 30%,#0000 0 70%,rgb(0 0 0/75% ) 0) 50%/100% 8%; */
/* background:
linear-gradient(0deg ,rgb(60 15 95/50%) 30%,#0000 0 70%,rgb(60 15 95/100%) 0) 50%/8% 100%,
linear-gradient(90deg,rgb(60 15 95/25%) 30%,#0000 0 70%,rgb(60 15 95/75% ) 0) 50%/100% 8%; */
background:
linear-gradient(0deg ,rgb(60 15 95/50%) 30%,#0000 0 70%,rgb(60 15 95/100%) 0) 50%/8% 100%,
linear-gradient(90deg,rgb(60 15 95/25%) 30%,#0000 0 70%,rgb(60 15 95/75% ) 0) 50%/100% 8%;
background-repeat: no-repeat;
animation: l23 1s infinite steps(12);
}
.loader::before,
.loader::after {
content: "";
grid-area: 1/1;
border-radius: 50%;
background: inherit;
opacity: 0.915;
transform: rotate(30deg);
}
.loader::after {
opacity: 0.83;
transform: rotate(60deg);
}
@keyframes l23 {
100% {transform: rotate(1turn)}
}
</style>
<style lang="scss" scoped>
$height: 180rpx;
.refresh-moudle {
width: 100%;
margin-top: -$height;
-webkit-overflow-scrolling: touch; /* ios5+ */
.pull-refresh {
width: 100%;
color: #999;
transition-duration: 200ms;
font-size: 28upx;
.down-tip,
.up-tip,
.refresh-tip {
display: flex;
align-items: center;
justify-content: center;
height: $height;
}
.down-img,
.up-img,
.refresh-img{
width: 50upx;
height: 50upx;
margin-right: 30upx;
}
.down-img {
transform: rotate(0deg);
animation: anticlockwise 0.8s ease;
}
@keyframes anticlockwise {
0% {
transform: rotate(-180deg);
}
100% {
transform: rotate(0deg);
}
}
.up-img {
transform: rotate(180deg);
animation: clockwise 0.8s ease;
}
@keyframes clockwise {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-180deg);
}
}
.refresh-img {
animation: rotating 1.5s linear infinite;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(1turn);
}
}
}
}
</style>
一:如何使用
bgColor loadColor contentColor这三个颜色自定义
<refresh @onRefresh="onRefresh" :bgColor="null" :loadColor="'RGBA(41, 184, 118, 1)'"
:contentColor="'RGBA(160, 237, 193, 1)'">
/*列表内容*/
</refresh>
<script setup>
import Refresh from '@/components/refresh/index.vue'
async function onRefresh(option) {
console.log('onRefresh ---', option);
const {
cb,
data
} = option;
try {
await getData('1', '1', '10', token), //将要刷新的数据放这里,确保异步的请求完成
} catch (error) {
console.error('Error fetching backpack list:', error);
} finally {
cb && cb(); // 在完成后调用回调
}
}
</script>