InfiniteScroll 无限滚动
可以与 PullToRefresh 下拉刷新组件组合使用。
目前仅支持 touch 事件,请切换到移动端调试模式尝试。
注意:InfiniteScroll 一定要有明确或潜在的固定高度。比如可以通过直接设置 height,或者在某个有固定高度的容器内。
示例
基础用法
<script lang="ts">
let data = $state<string[]>([])
let hasMore = $derived(data.length < 26 * 2)
// 异步加载数据
async function loadMore() {
await delay(1000)
data = [...data, ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
}
</script>
{#snippet list()}
{#each data as item}
<ListItem>{item}</ListItem>
{/each}
{/snippet}
<InfiniteScroll {hasMore} {loadMore}>
<List class="bg-white">
{@render list()}
</List>
</InfiniteScroll>
下拉刷新
<script lang="ts">
// 刷新
async function handleRefresh() {
await delay(1000)
data = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
hasError = false
}
</script>
<PullToRefresh onRefresh={handleRefresh}>
<InfiniteScroll class="h-full" {hasMore} {loadMore}>
<List class="bg-white">
{@render list()}
</List>
</InfiniteScroll>
</PullToRefresh>
加载失败
<script lang="ts">
let hasError = $state(false)
// 异步加载数据
async function loadMore(retry: boolean) {
// 正常加载
if (!retry) {
await delay(1000)
data = [...data, ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
}
// 加载失败重新加载
else {
await delay(1000)
data = [...data, ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
}
}
</script>
<InfiniteScroll class="h-full" bind:hasError {hasMore} {loadMore}>
<List class="bg-white">
{@render list()}
</List>
</InfiniteScroll>
自定义
<PullToRefresh class="-mt-[1px] h-full" onRefresh={handleRefresh} completeDuration={0}>
<!-- 自定义下拉头部 -->
{#snippet header(status, offset)}
<div class="flex items-center justify-center p-2">
{#if status === 'refreshing'}
<Icon
class="animate-spin rounded-full bg-white p-1 text-blue-600"
name="refresh"
size={32}
/>
{:else}
<Icon
class="rounded-full bg-white p-1 {status === 'loosing'
? 'text-blue-600'
: 'text-gray-400'}"
style="rotate: {offset * 5}deg"
name="reset-right"
size={32}
/>
{/if}
</div>
{/snippet}
<!-- 无限滚动 -->
<InfiniteScroll class="h-full" bind:hasError {hasMore} loadMore={loadMoreError}>
<List class="bg-white">
{@render list()}
</List>
<!-- 自定义底部 -->
{#snippet footer(status, reload)}
<div class="flex items-center justify-center" style="height: 50px">
{#if status === 'idle' || status === 'loading'}
<Icon
class="animate-spin rounded-full bg-white p-1 text-blue-600"
name="refresh"
size={32}
/>
{:else if status === 'finished'}
<Divider
class="flex-auto"
style="color: #1677ff; border-color: #1677ff; border-style: dashed"
>我是有底线的</Divider
>
{:else if status == 'error'}
<Button color="danger-plain" block --sunp-border="0px" onclick={reload}
>加载失败,点击重试</Button
>
{/if}
</div>
{/snippet}
</InfiniteScroll>
</PullToRefresh>
PullToRefresh
属性
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
hasMore | 是否还有更多内容 | boolean | 必填 |
loadMore | 加载更多回调函数(retry: 重试) | (retry: boolean) => Promise |
必填 |
footer | 自定义底部加载状态区域 | Snippet<[status: InfiniteScrollStatus, reload: () => Promise |
组件默认样式 |
threshold | 触发加载事件的滚动触底距离阈值,单位为 px。不包含 footer 的高度 | number | 0 |
completeDuration | 完成状态持续时间(毫秒)。填 0 表示不会进入等待状态 | number | 500 |
hasError | 加载错误。组件向调用者提供的状态 | boolean | 只读 |
Footer Snippet
Snippet<[status: InfiniteScrollStatus, reload: () => Promise<void>]>
属性 | 说明 | 类型 |
---|---|---|
status | 当前组件状态 | PullToRefreshStatus |
reload | 重新加载按钮 onclick 事件回调 | () => Promise |
InfiniteScrollStatus
/** 无限滚动状态 */
export type InfiniteScrollStatus =
/** 空闲状态 */
| 'idle'
/** 加载中 */
| 'loading'
/** 加载完成 */
| 'finished'
/** 加载失败 */
| 'error'