#ScrollArea 滚动区域
扩展原生滚动功能,支持跨浏览器的自定义样式,并提供针对大型列表的虚拟滚动支持。
#组件特性
- 跨浏览器的自定义滚动条样式。
- 支持垂直、水平或双向滚动。
- 支持平滑滚动 (smooth scrolling)。
- 提供滚动位置和触边检测事件。
- 虚拟滚动 支持,高效渲染海量数据列表。
#双向滚动
<script setup lang="ts">
import { BrScrollArea } from '@breezeui/vue'
const artworks = [
{
artist: 'Ornella Binni',
art: 'https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80',
},
{
artist: 'Tom Byrom',
art: 'https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80',
},
{
artist: 'Vladimir Malyavko',
art: 'https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80',
},
]
</script>
<template>
<BrScrollArea class="h-[200px] w-full max-w-[350px] whitespace-nowrap rounded-md border" direction="both" type="always">
<div class="flex w-max space-x-4 p-4">
<figure v-for="artwork in artworks" :key="artwork.artist" class="shrink-0">
<div class="overflow-hidden rounded-md">
<img
:src="artwork.art"
:alt="artwork.artist"
class="aspect-[3/4] h-fit w-fit object-cover"
width="150"
height="200"
>
</div>
<figcaption class="pt-2 text-xs text-muted-foreground">
Photo by <span class="font-semibold text-foreground">{{ artwork.artist }}</span>
</figcaption>
</figure>
</div>
</BrScrollArea>
</template>#平滑滚动与事件
<script setup lang="ts">
import { BrScrollArea, BrButton } from '@breezeui/vue'
const scrollTo = (id: string) => {
const el = document.getElementById(id)
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}
const handleScrollTop = () => { console.log('Scrolled to top') }
const handleScrollBottom = () => { console.log('Scrolled to bottom') }
const handleScrollPosition = (pos: { x: number; y: number }) => {
// console.log('Position:', pos)
}
</script>
<template>
<div>
<div class="flex gap-2 mb-4">
<BrButton size="sm" @click="scrollTo('section-1')">Section 1</BrButton>
<BrButton size="sm" @click="scrollTo('section-2')">Section 2</BrButton>
<BrButton size="sm" @click="scrollTo('section-3')">Section 3</BrButton>
</div>
<BrScrollArea
smooth
class="h-[250px] w-full max-w-[400px] rounded-md border p-4"
@scroll-top="handleScrollTop"
@scroll-bottom="handleScrollBottom"
@scroll-position="handleScrollPosition"
>
<div class="space-y-8">
<div id="section-1" class="h-[200px] rounded bg-primary/10 p-4 flex items-center justify-center">Section 1</div>
<div id="section-2" class="h-[200px] rounded bg-secondary/10 p-4 flex items-center justify-center">Section 2</div>
<div id="section-3" class="h-[200px] rounded bg-destructive/10 p-4 flex items-center justify-center">Section 3</div>
</div>
</BrScrollArea>
</div>
</template>#与表格集成
<script setup lang="ts">
import { BrScrollArea, BrCard, BrCardHeader, BrCardTitle, BrCardContent, BrTable } from '@breezeui/vue'
const tableColumns = [
{ key: 'invoice', title: 'Invoice', width: 100 },
{ key: 'status', title: 'Status' },
{ key: 'method', title: 'Method' },
{ key: 'amount', title: 'Amount', align: 'right' },
]
const tableData = Array.from({ length: 15 }).map((_, i) => ({
invoice: `INV00${i + 1}`,
status: 'Paid',
method: 'Credit Card',
amount: `$${(Math.random() * 1000).toFixed(2)}`,
}))
</script>
<template>
<BrCard class="w-full max-w-[800px]">
<BrCardHeader>
<BrCardTitle>Recent Orders</BrCardTitle>
</BrCardHeader>
<BrCardContent>
<BrScrollArea class="h-[300px] w-full rounded-md border" direction="both">
<div class="min-w-[800px]">
<BrTable :columns="tableColumns" :data="tableData" />
</div>
</BrScrollArea>
</BrCardContent>
</BrCard>
</template>#无限滚动
<script setup lang="ts">
import { ref } from 'vue'
import { BrScrollArea } from '@breezeui/vue'
const infiniteList = ref(Array.from({ length: 20 }).map((_, i) => `Item ${i + 1}`))
const isLoadingMore = ref(false)
const handleInfiniteScroll = () => {
if (isLoadingMore.value) return
isLoadingMore.value = true
// Simulate network request
setTimeout(() => {
const currentLength = infiniteList.value.length
const moreItems = Array.from({ length: 10 }).map((_, i) => `Item ${currentLength + i + 1}`)
infiniteList.value.push(...moreItems)
isLoadingMore.value = false
}, 1000)
}
</script>
<template>
<BrScrollArea
class="h-[300px] w-full max-w-[400px] rounded-md border p-4"
:scroll-threshold="50"
@scroll-bottom="handleInfiniteScroll"
>
<div class="space-y-4">
<div
v-for="item in infiniteList"
:key="item"
class="rounded bg-primary/5 p-4 flex items-center justify-between"
>
<span>{{ item }}</span>
</div>
<div v-if="isLoadingMore" class="py-4 flex justify-center items-center text-sm text-muted-foreground">
<span class="animate-pulse">Loading more...</span>
</div>
</div>
</BrScrollArea>
</template>#虚拟滚动
启用虚拟滚动,可以毫无性能压力地渲染包含成千上万条数据的列表。
<script setup lang="ts">
import { BrScrollArea } from '@breezeui/vue'
const virtualItems = Array.from({ length: 10000 }).map((_, i) => ({
id: i,
text: `Virtual Item ${i + 1}`,
}))
</script>
<template>
<BrScrollArea
class="h-[300px] w-full max-w-[400px] rounded-md border bg-card"
virtual-scroll
:items="virtualItems"
:item-height="40"
>
<template #item="{ item, index }">
<div class="flex items-center px-4 h-[40px] text-sm border-b border-border/50 last:border-b-0 hover:bg-muted/50 transition-colors">
<span class="text-muted-foreground/70 w-12 text-xs">{{ index + 1 }}</span>
<span class="font-normal text-foreground">{{ item.text }}</span>
</div>
</template>
</BrScrollArea>
</template>#API
#属性 (Props)
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type | 'auto' | 'always' | 'scroll' | 'hover' | 'hover' | 描述滚动条的可见性行为。 |
direction | 'vertical' | 'horizontal' | 'both' | 'vertical' | 滚动区域的方向。 |
smooth | boolean | false | 是否启用平滑滚动行为。 |
scrollThreshold | number | 0 | 距离边缘多少像素时触发滚动触边事件。 |
virtualScroll | boolean | false | 是否启用虚拟滚动模式。 |
items | any[] | [] | 启用虚拟滚动时需要渲染的数据数组。 |
itemHeight | number | 40 | 虚拟滚动模式下,每一项的固定高度(像素)。 |
#事件 (Events)
| 名称 | 参数 | 说明 |
|---|---|---|
scroll-position | (position: { x: number; y: number }) | 滚动发生时触发,提供当前滚动的坐标。 |
scroll-top | - | 滚动到顶部边缘时触发(受 threshold 影响)。 |
scroll-bottom | - | 滚动到底部边缘时触发(受 threshold 影响)。 |
scroll-left | - | 滚动到左侧边缘时触发(受 threshold 影响)。 |
scroll-right | - | 滚动到右侧边缘时触发(受 threshold 影响)。 |
#插槽 (Slots)
| 名称 | 作用域 | 说明 |
|---|---|---|
default | - | 需要滚动的主要内容。 |
item | { item: any, index: number } | 虚拟滚动模式下,自定义每一项渲染的模板插槽。 |