ScrollArea 滚动区域

使用浮层滚动条的滚动区域。

第 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>
  )
}
tsx

用法

import { ScrollArea } from '@hina-ui/react'
ts

给区域一个高度,超出的部分即可滚动。滚动条浮在内容之上,不占用内容的宽度,并且在各个平台上外观一致。

第 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>
  )
}
tsx

组件库中所有需要内部滚动的地方都经由这个组件:下拉列表、菜单、Dialog 和 Drawer 的正文、AppShell 的主区域。其他组件不各自引入滚动库,因此滚动行为不会出现分歧。

示例

方向

direction 取 vertical、horizontal 或 both。横向区域中滚轮会横向滚动,因为鼠标没有对应的横向手势,而浮层滚动条大多数时候是隐藏的。

1
2
3
4
5
6
7
8
9
10
11
12
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>
  )
}
tsx

内容方向继承外层,也可用 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>
  )
}
tsx

滚动条的显示时机

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>
  )
}
tsx

键盘访问

区域内可以滚动但没有可聚焦元素时,键盘无法到达它。设置 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>
  )
}
tsx

行为

  • 滚动条覆盖在内容之上,内容开始溢出时布局宽度不变。
  • 组件接管之前即可原生滚动。自定义滚动条在挂载后的下一帧初始化,并保留原生滚动位置;浮层首次打开或重新打开时,都不会等待浏览器空闲或滚动停止。
  • 内容因动画改变尺寸时滚动条自行更新,不需要额外接入观察器。
  • 对话框、菜单这类锁定页面的浮层打开时,区域立即停止响应滚轮与触摸;浮层里的滚动区域不受影响,浮层关闭后恢复。

无障碍

  • 不设置 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
滚动元素,在挂载后的下一帧完成初始化后可用。