第 1 章
第 2 章
第 3 章
第 4 章
第 5 章
第 6 章
第 7 章
第 8 章
第 9 章
第 10 章
第 11 章
第 12 章
第 13 章
第 14 章
第 15 章
第 16 章
第 17 章
第 18 章
第 19 章
第 20 章
第 21 章
第 22 章
第 23 章
第 24 章
import { Card, ScrollArea, Stack, Text } from '@hina-ui/react'
const chapters = Array.from({ length: 24 }, (_, i) => `第 ${i + 1} 章`)
export default function Demo() {
return (
<Card className="w-full max-w-xs" padded={false}>
<ScrollArea className="h-64">
<Stack gap="none" className="p-2">
{chapters.map(chapter => (
<Text key={chapter} size="sm" className="px-3 py-2">
{chapter}
</Text>
))}
</Stack>
</ScrollArea>
</Card>
)
}
用法
import { ScrollArea } from '@hina-ui/react'
给区域一个高度,超出的部分即可滚动。滚动条浮在内容之上,不占用内容的宽度,并且在各个平台上外观一致。
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
第 9 行
第 10 行
第 11 行
第 12 行
import { ScrollArea, Stack, Text } from '@hina-ui/react'
const lines = Array.from({ length: 12 }, (_, i) => i + 1)
export default function Demo() {
return (
<ScrollArea className="bg-inset h-40 w-full max-w-sm rounded-md">
<Stack className="p-4">
{lines.map(i => (
<Text key={i}>第 {i} 行</Text>
))}
</Stack>
</ScrollArea>
)
}
组件库中所有需要内部滚动的地方都经由这个组件:下拉列表、菜单、Dialog 和 Drawer 的正文、AppShell 的主区域。其他组件不各自引入滚动库,因此滚动行为不会出现分歧。
示例
方向
direction 取 vertical、horizontal 或 both。横向区域中滚轮会横向滚动,因为鼠标没有对应的横向手势,而浮层滚动条大多数时候是隐藏的。
import { Card, Inline, ScrollArea } from '@hina-ui/react'
const cards = Array.from({ length: 12 }, (_, i) => i + 1)
export default function Demo() {
return (
<ScrollArea direction="horizontal" className="bg-inset w-full max-w-md rounded-md">
<Inline gap="sm" wrap={false} className="p-4">
{cards.map(i => (
<Card
key={i}
className="bg-surface grid size-20 shrink-0 place-items-center"
padded={false}
>
{i}
</Card>
))}
</Inline>
</ScrollArea>
)
}
内容方向继承外层,也可用 dir="rtl" 显式设置。横向阴影的位置和渐变方向一起镜像,始终从边缘向内容内侧淡出。
边缘阴影
某一侧还有未显示的内容时,该侧出现阴影,滚动到尽头后消失。边界本身已经足够清楚时,把 shadow 设为假。
带边缘阴影
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
第 9 行
第 10 行
关闭边缘阴影
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
第 9 行
第 10 行
import { ScrollArea, Stack, Text } from '@hina-ui/react'
const lines = Array.from({ length: 10 }, (_, i) => i + 1)
export default function Demo() {
return (
<Stack className="w-full max-w-sm">
<Stack gap="xs">
<Text tone="muted" size="sm">
带边缘阴影
</Text>
<ScrollArea className="bg-inset h-32 rounded-md">
<Stack className="p-4">
{lines.map(i => (
<Text key={i}>第 {i} 行</Text>
))}
</Stack>
</ScrollArea>
</Stack>
<Stack gap="xs">
<Text tone="muted" size="sm">
关闭边缘阴影
</Text>
<ScrollArea shadow={false} className="bg-inset h-32 rounded-md">
<Stack className="p-4">
{lines.map(i => (
<Text key={i}>第 {i} 行</Text>
))}
</Stack>
</ScrollArea>
</Stack>
</Stack>
)
}
滚动条的显示时机
autoHide 决定滚动条何时淡出:never 始终显示,scroll 滚动时显示,leave 在指针离开前一直显示,move 在区域内移动指针时显示。
never
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
scroll
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
leave
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
move
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
import { ScrollArea, Stack, Text } from '@hina-ui/react'
const modes = ['never', 'scroll', 'leave', 'move'] as const
const lines = Array.from({ length: 8 }, (_, i) => i + 1)
export default function Demo() {
return (
<Stack className="w-full max-w-sm">
{modes.map(mode => (
<Stack key={mode} gap="xs">
<Text tone="muted" size="sm">
{mode}
</Text>
<ScrollArea autoHide={mode} className="bg-inset h-24 rounded-md">
<Stack className="p-4">
{lines.map(i => (
<Text key={i}>第 {i} 行</Text>
))}
</Stack>
</ScrollArea>
</Stack>
))}
</Stack>
)
}
键盘访问
区域内可以滚动但没有可聚焦元素时,键盘无法到达它。设置 focusable 后区域进入 Tab 顺序,渲染为 role="region",并用 label 提供名称。
第 1 行
第 2 行
第 3 行
第 4 行
第 5 行
第 6 行
第 7 行
第 8 行
第 9 行
第 10 行
import { ScrollArea, Stack, Text } from '@hina-ui/react'
const lines = Array.from({ length: 10 }, (_, i) => i + 1)
export default function Demo() {
return (
<ScrollArea focusable label="更新说明" className="bg-inset h-32 w-full max-w-sm rounded-md">
<Stack className="p-4">
{lines.map(i => (
<Text key={i}>第 {i} 行</Text>
))}
</Stack>
</ScrollArea>
)
}
行为
- 滚动条覆盖在内容之上,内容开始溢出时布局宽度不变。
- 组件接管之前即可原生滚动。自定义滚动条在挂载后的下一帧初始化,并保留原生滚动位置;浮层首次打开或重新打开时,都不会等待浏览器空闲或滚动停止。
- 内容因动画改变尺寸时滚动条自行更新,不需要额外接入观察器。
- 对话框、菜单这类锁定页面的浮层打开时,区域立即停止响应滚轮与触摸;浮层里的滚动区域不受影响,浮层关闭后恢复。
无障碍
- 不设置
focusable时,区域是普通容器:键盘可以到达其中的可聚焦元素,浏览器随之滚动。 - 设置
focusable后,区域以role="region"进入 Tab 顺序,名称取自label,方向键可以滚动它。
API
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
direction | 'vertical' | 'horizontal' | 'both' | 'vertical' | 滚动的方向 |
dir | 'ltr' | 'rtl' | 'auto' | 继承 | 视口与阴影层的内容方向 |
autoHide | 'never' | 'scroll' | 'leave' | 'move' | 'leave' | 滚动条何时淡出 |
scrollbar | boolean | true | 是否显示滚动条 |
wheelRedirect | boolean | true | 横向区域中滚轮是否横向滚动 |
shadow | boolean | true | 是否显示边缘阴影 |
focusable | boolean | false | 是否让区域进入 Tab 顺序 |
label | string | — | 无障碍名称, focusable 时使用 |
className | string | — | 追加至外层元素的类名 |
| 属性 | 说明 |
|---|---|
children | 滚动区域的内容 |
| 暴露 | 类型 | 说明 |
|---|---|---|
viewport | HTMLElement | undefined | 滚动元素,在挂载后的下一帧完成初始化后可用。 |