介绍
PullDownRefresh 提供下拉刷新的交互操作。
PullDownRefresh 可以在页面、scroll-view 或其他容器中使用, 当满足下拉刷新条件(滚动到顶部)且进行下拉操作时,会接替容器的滚动行为。
因此当滚动到顶部时调用 PullDownRefreshExpose['enableToRefresh'] 方法并传递true告诉组件可以进行下拉刷新操作。
当用户下拉到指定阈值时会触发 refresh 事件,此时要设置 loading 属性为 true 以便向用户展示加载状态,并发送网络请求。
在获取到数据后设置 loading 属性为 false 来关闭加载状态。
PullDownRefresh 组件被设计为与滚动容器弱关联关系,因此,如果有需要,你可以将此组件与滚动容器以及上拉加载组件组合使用来实现复杂的效果。
引入
import PullDownRefresh from 'sard-uniapp/components/pull-down-refresh/pull-down-refresh.vue'代码演示
基于页面的刷新
在页面生命周期 onPageScroll 中获取当前 scrollTop 的值,当为 0 时启用下拉刷新。
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="基于页面的刷新">
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
root-style="min-height: 100vh"
@refresh="onRefresh"
>
<view
v-for="item in 5"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 0 32rpx 10rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</doc-page>
</template>
<script setup lang="ts">
import { useCurrentPageLock, usePageTopPopup } from 'sard-uniapp'
import { onBackPress } from '@dcloudio/uni-app'
import { toast } from 'sard-uniapp'
import { ref } from 'vue'
import { onPageScroll } from '@dcloudio/uni-app'
const loading = ref(false)
const pullDownRefresh = ref()
onPageScroll(({ scrollTop }) => {
pullDownRefresh.value?.enableToRefresh(scrollTop === 0)
})
const fetchApi = () => {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
const onRefresh = () => {
loading.value = true
fetchApi()
.then(() => {
toast('刷新成功')
})
.catch(() => {
toast('刷新失败')
})
.finally(() => {
loading.value = false
})
}
const { isLocked } = useCurrentPageLock()
const { shouldStopBack, hidePopup } = usePageTopPopup()
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup()
return true
}
})
</script><template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="基于页面的刷新">
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
root-style="min-height: 100vh"
@refresh="onRefresh"
>
<view
v-for="item in 5"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 0 32rpx 10rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</doc-page>
</template>
<script setup lang="js">
import { useCurrentPageLock, usePageTopPopup } from "sard-uniapp";
import { onBackPress } from "@dcloudio/uni-app";
import { toast } from "sard-uniapp";
import { ref } from "vue";
import { onPageScroll } from "@dcloudio/uni-app";
const loading = ref(false);
const pullDownRefresh = ref();
onPageScroll(({ scrollTop }) => {
pullDownRefresh.value?.enableToRefresh(scrollTop === 0);
});
const fetchApi = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1e3);
});
};
const onRefresh = () => {
loading.value = true;
fetchApi().then(() => {
toast("\u5237\u65B0\u6210\u529F");
}).catch(() => {
toast("\u5237\u65B0\u5931\u8D25");
}).finally(() => {
loading.value = false;
});
};
const { isLocked } = useCurrentPageLock();
const { shouldStopBack, hidePopup } = usePageTopPopup();
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup();
return true;
}
});
</script>基于 scroll-view 的刷新
监听 scroll-view 组件的 scroll 事件来获取 scrollTop 值,当为 0 时启用下拉刷新。
因为小程序端 scroll-view 组件内置了节流功能,当快速滚动到顶部时,可能不会触发 scroll 事件, 造成 scrollTop 不为 0 的情况。
因此需要同时监听 scrolltoupper 事件,当此事件触发时也启用下拉刷新。
即便如此,在极少数的情况下,滚动到顶部也不会触发 scrolltoupper 事件,因此保守起见, 还要设置 scroll-view 组件的 throttle 属性为 false 来关闭节流(官方文档中没有关于throttle 属性的描述,但此属性确实存在)。
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="基于 scroll-view 的刷新" padding="20rpx">
<sar-button @click="loading = !loading">toggle loading</sar-button>
<scroll-view
scroll-y
style="
height: 300px;
margin: 20rpx 0;
border: 1px solid var(--sar-border-color);
"
:throttle="false"
@scroll="onScroll"
@scrolltoupper="onScrolltoupper"
>
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
@refresh="onRefresh"
>
<view
v-for="item in 10"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 10rpx 32rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</scroll-view>
</doc-page>
</template>
<script setup lang="ts">
import { useCurrentPageLock, usePageTopPopup } from 'sard-uniapp'
import { onBackPress } from '@dcloudio/uni-app'
import { toast } from 'sard-uniapp'
import { ref } from 'vue'
const loading = ref(false)
const pullDownRefresh = ref()
const onScroll = (event: any) => {
pullDownRefresh.value?.enableToRefresh(event.detail.scrollTop === 0)
}
const onScrolltoupper = () => {
pullDownRefresh.value?.enableToRefresh(true)
}
const fetchApi = () => {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
const onRefresh = () => {
loading.value = true
fetchApi()
.then(() => {
toast('刷新成功')
})
.catch(() => {
toast('刷新失败')
})
.finally(() => {
loading.value = false
})
}
const { isLocked } = useCurrentPageLock()
const { shouldStopBack, hidePopup } = usePageTopPopup()
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup()
return true
}
})
</script><template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="基于 scroll-view 的刷新" padding="20rpx">
<sar-button @click="loading = !loading">toggle loading</sar-button>
<scroll-view
scroll-y
style="
height: 300px;
margin: 20rpx 0;
border: 1px solid var(--sar-border-color);
"
:throttle="false"
@scroll="onScroll"
@scrolltoupper="onScrolltoupper"
>
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
@refresh="onRefresh"
>
<view
v-for="item in 10"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 10rpx 32rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</scroll-view>
</doc-page>
</template>
<script setup lang="js">
import { useCurrentPageLock, usePageTopPopup } from "sard-uniapp";
import { onBackPress } from "@dcloudio/uni-app";
import { toast } from "sard-uniapp";
import { ref } from "vue";
const loading = ref(false);
const pullDownRefresh = ref();
const onScroll = (event) => {
pullDownRefresh.value?.enableToRefresh(event.detail.scrollTop === 0);
};
const onScrolltoupper = () => {
pullDownRefresh.value?.enableToRefresh(true);
};
const fetchApi = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1e3);
});
};
const onRefresh = () => {
loading.value = true;
fetchApi().then(() => {
toast("\u5237\u65B0\u6210\u529F");
}).catch(() => {
toast("\u5237\u65B0\u5931\u8D25");
}).finally(() => {
loading.value = false;
});
};
const { isLocked } = useCurrentPageLock();
const { shouldStopBack, hidePopup } = usePageTopPopup();
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup();
return true;
}
});
</script>自定义插槽
通过使用插槽可以自定义不同状态的提示信息。
其中 unready 插槽接收一个 progress 属性用来实现下拉进度展示的效果。
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="自定义插槽" padding="20rpx">
<scroll-view
scroll-y
style="
height: 300px;
margin: 20rpx 0;
border: 1px solid var(--sar-border-color);
"
:throttle="false"
@scroll="onScroll"
@scrolltoupper="onScrolltoupper"
>
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
:done-duration="500"
@refresh="onRefresh"
>
<template #unready="{ progress }">
<sar-loading size="48rpx" :animated="false" :progress="progress">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
下拉刷新
</sar-loading>
</template>
<template #ready>
<sar-loading size="48rpx" :animated="false">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
释放刷新
</sar-loading>
</template>
<template #loading>
<sar-loading size="48rpx">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
加载中...
</sar-loading>
</template>
<template #done>{{ doneText }}</template>
<view
v-for="item in 10"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 10rpx 32rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</scroll-view>
</doc-page>
</template>
<script setup lang="ts">
import { useCurrentPageLock, usePageTopPopup } from 'sard-uniapp'
import { onBackPress } from '@dcloudio/uni-app'
import { ref } from 'vue'
const loading = ref(false)
const pullDownRefresh = ref()
const onScroll = (event: any) => {
pullDownRefresh.value?.enableToRefresh(event.detail.scrollTop === 0)
}
const onScrolltoupper = () => {
pullDownRefresh.value?.enableToRefresh(true)
}
const fetchApi = () => {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
const doneText = ref('')
const onRefresh = () => {
loading.value = true
fetchApi()
.then(() => {
doneText.value = '刷新成功'
})
.catch(() => {
doneText.value = '刷新失败'
})
.finally(() => {
loading.value = false
})
}
const { isLocked } = useCurrentPageLock()
const { shouldStopBack, hidePopup } = usePageTopPopup()
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup()
return true
}
})
</script><template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="自定义插槽" padding="20rpx">
<scroll-view
scroll-y
style="
height: 300px;
margin: 20rpx 0;
border: 1px solid var(--sar-border-color);
"
:throttle="false"
@scroll="onScroll"
@scrolltoupper="onScrolltoupper"
>
<sar-pull-down-refresh
ref="pullDownRefresh"
:loading="loading"
:done-duration="500"
@refresh="onRefresh"
>
<template #unready="{ progress }">
<sar-loading size="48rpx" :animated="false" :progress="progress">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
下拉刷新
</sar-loading>
</template>
<template #ready>
<sar-loading size="48rpx" :animated="false">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
释放刷新
</sar-loading>
</template>
<template #loading>
<sar-loading size="48rpx">
<template #circular>
<sar-icon family="demo-icons" name="arrow-clockwise"></sar-icon>
</template>
加载中...
</sar-loading>
</template>
<template #done>{{ doneText }}</template>
<view
v-for="item in 10"
:key="item"
style="
display: flex;
justify-content: center;
align-items: center;
margin: 10rpx 32rpx;
height: 40px;
border: 1px solid var(--sar-border-color);
border-radius: var(--sar-rounded);
"
>
{{ item }}
</view>
</sar-pull-down-refresh>
</scroll-view>
</doc-page>
</template>
<script setup lang="js">
import { useCurrentPageLock, usePageTopPopup } from "sard-uniapp";
import { onBackPress } from "@dcloudio/uni-app";
import { ref } from "vue";
const loading = ref(false);
const pullDownRefresh = ref();
const onScroll = (event) => {
pullDownRefresh.value?.enableToRefresh(event.detail.scrollTop === 0);
};
const onScrolltoupper = () => {
pullDownRefresh.value?.enableToRefresh(true);
};
const fetchApi = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1e3);
});
};
const doneText = ref("");
const onRefresh = () => {
loading.value = true;
fetchApi().then(() => {
doneText.value = "\u5237\u65B0\u6210\u529F";
}).catch(() => {
doneText.value = "\u5237\u65B0\u5931\u8D25";
}).finally(() => {
loading.value = false;
});
};
const { isLocked } = useCurrentPageLock();
const { shouldStopBack, hidePopup } = usePageTopPopup();
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup();
return true;
}
});
</script>API
PullDownRefreshProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
| threshold | 触发下拉刷新的阈值,单位 px | number | 50 |
| header-height | 顶部内容高度,单位 px | number | 50 |
| loading | 是否处于加载中状态 | boolean | false |
| transition-duration | 回弹动画时长,单位 ms | number | 300 |
| done-duration | 加载完成状态持续时长,单位 ms | number | 0 |
| disabled | 是否禁止用户进行下拉操作 | boolean | false |
PullDownRefreshSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
| unready | 自定义未预备加载状态内容,progress 属性表示从下拉开始到达到 threshold 的进度值 | { progress: number } |
| ready | 自定义预备加载状态内容 | - |
| loading | 自定义加载中状态内容 | - |
| done | 自定义加载完成状态内容 | - |
PullDownRefreshEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| refresh | 下拉到指定阈值并松开手指后触发 | () => void |
PullDownRefreshExpose
| 属性 | 描述 | 类型 |
|---|---|---|
| enableToRefresh | 是否启用下拉刷新,通常在容器滚动到顶部时设为真 | (canRefresh: boolean) => void |
主题定制
CSS 变量
page,
.sar-portal {
--sar-pull-down-refresh-header-font-size: var(--sar-text-sm);
--sar-pull-down-refresh-header-color: var(--sar-tertiary-color);
--sar-pull-down-refresh-loading-font-size: 36rpx;
}