Skip to content

ScrollArea 滚动区域 ​

内容照常用浏览器原生滚动(滚轮、触控板、触屏惯性、键盘、页内查找定位都不打折扣),只把原生滚动条藏掉、 换成一条跟随主题的细滚动条。各平台原生滚动条的宽窄、颜色、有无都不一样,放在卡片、侧栏、面板这类小容器里时, 用它让滚动条看起来一致。

基础用法

给宿主一个高度(或 max-height),内容超出后出现细滚动条;默认 type="hover",指针移上来才显示。滚动本身走浏览器原生,滚轮、触控板、键盘都能用。

<pt-scroll-area label="更新日志" style="height: 200px; width: 260px; border: 1px solid var(--pt-border-default); border-radius: 8px">
  <div style="padding: 12px 16px; font-size: 14px; line-height: 28px">
    <div>v1.1.0 · 第 1 条更新说明</div>
    <div>v1.2.0 · 第 2 条更新说明</div>
    <div>v1.3.0 · 第 3 条更新说明</div>
    <div>v1.4.0 · 第 4 条更新说明</div>
    <div>v1.5.0 · 第 5 条更新说明</div>
    <div>v1.6.0 · 第 6 条更新说明</div>
    <div>v1.7.0 · 第 7 条更新说明</div>
    <div>v1.8.0 · 第 8 条更新说明</div>
    <div>v1.9.0 · 第 9 条更新说明</div>
    <div>v1.10.0 · 第 10 条更新说明</div>
    <div>v1.11.0 · 第 11 条更新说明</div>
    <div>v1.12.0 · 第 12 条更新说明</div>
    <div>v1.13.0 · 第 13 条更新说明</div>
    <div>v1.14.0 · 第 14 条更新说明</div>
    <div>v1.15.0 · 第 15 条更新说明</div>
    <div>v1.16.0 · 第 16 条更新说明</div>
    <div>v1.17.0 · 第 17 条更新说明</div>
    <div>v1.18.0 · 第 18 条更新说明</div>
    <div>v1.19.0 · 第 19 条更新说明</div>
    <div>v1.20.0 · 第 20 条更新说明</div>
  </div>
</pt-scroll-area>
横向滚动

orientation="horizontal" 只允许横向滚动,横条贴底;"both" 两个方向都能滚。RTL 下横条从右往左,竖条换到左侧。

<pt-scroll-area orientation="horizontal" type="auto" style="width: 100%; max-width: 420px; border: 1px solid var(--pt-border-default); border-radius: 8px">
  <div style="display: flex; gap: 12px; padding: 12px; width: max-content">
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 1</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 2</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 3</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 4</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 5</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 6</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 7</div>
    <div style="display: grid; place-items: center; width: 120px; height: 80px; border-radius: 8px; background: var(--pt-bg-secondary)">卡片 8</div>
  </div>
</pt-scroll-area>
显示时机

type:hover 悬停时显示(默认)、scroll 滚动时显示、auto 溢出就显示、always 轨道始终占位。hover / scroll 在停止后 scrollHideDelay(默认 600ms)毫秒隐藏。

<pt-scroll-area type="scroll" label="type=scroll" style="height: 160px; width: 200px; border: 1px solid var(--pt-border-default); border-radius: 8px">
  <div style="padding: 12px 16px; font-size: 14px; line-height: 28px">
    <strong>type="scroll"</strong>
    <div>第 1 行</div>
    <div>第 2 行</div>
    <div>第 3 行</div>
    <div>第 4 行</div>
    <div>第 5 行</div>
    <div>第 6 行</div>
    <div>第 7 行</div>
    <div>第 8 行</div>
    <div>第 9 行</div>
    <div>第 10 行</div>
    <div>第 11 行</div>
    <div>第 12 行</div>
  </div>
</pt-scroll-area>
<pt-scroll-area type="auto" label="type=auto" style="height: 160px; width: 200px; border: 1px solid var(--pt-border-default); border-radius: 8px">
  <div style="padding: 12px 16px; font-size: 14px; line-height: 28px">
    <strong>type="auto"</strong>
    <div>第 1 行</div>
    <div>第 2 行</div>
    <div>第 3 行</div>
    <div>第 4 行</div>
    <div>第 5 行</div>
    <div>第 6 行</div>
    <div>第 7 行</div>
    <div>第 8 行</div>
    <div>第 9 行</div>
    <div>第 10 行</div>
    <div>第 11 行</div>
    <div>第 12 行</div>
  </div>
</pt-scroll-area>
<pt-scroll-area type="always" label="type=always" style="height: 160px; width: 200px; border: 1px solid var(--pt-border-default); border-radius: 8px">
  <div style="padding: 12px 16px; font-size: 14px; line-height: 28px">
    <strong>type="always"</strong>
    <div>第 1 行</div>
    <div>第 2 行</div>
    <div>第 3 行</div>
    <div>第 4 行</div>
    <div>第 5 行</div>
    <div>第 6 行</div>
    <div>第 7 行</div>
    <div>第 8 行</div>
    <div>第 9 行</div>
    <div>第 10 行</div>
    <div>第 11 行</div>
    <div>第 12 行</div>
  </div>
</pt-scroll-area>

尺寸 ​

高度(横向是宽度)由使用方给宿主:

  • 给 height:区域固定这么高,内容超出就滚动。
  • 只给 max-height:内容少时区域随内容收缩,超过上限才开始滚动。
  • 都不给:内容多长区域就多长,也就不会滚动。

宿主作为 flex / grid 子项时默认可以缩到比内容小(min-inline-size / min-block-size: 0), 放进侧栏、弹窗正文这类「剩多少给多少」的位置时不需要再写 min-height: 0。

滚动条 ​

type什么时候显示
hover(默认)指针在区域上时;移开 scrollHideDelay 毫秒后隐藏。键盘聚焦到区域上时也显示
scroll滚动时;停下 scrollHideDelay 毫秒后隐藏,指针停在滚动条上时不隐藏
auto内容溢出就一直显示
always轨道始终占位,thumb 只在溢出时出现
  • 滚动条浮在内容上方、不占版面:竖条 10px 宽,贴在行内结束侧(LTR 右、RTL 左);横条 10px 高,贴底。 两条都显示时各让出一个角。内容紧贴边缘的,给内容留出 10px 的内边距免得被盖住。
  • 按住 thumb 拖动;点轨道空白处朝那一侧翻一页(可见尺寸的 87.5%)。触屏上 thumb 的命中区扩到 44px。
  • thumb 颜色用 --pt-scroll-area-thumb-color 改;::part(scrollbar) / ::part(thumb) 可以进一步定制 (竖条横条共用这两个 part)。

可达性 ​

  • 内容溢出时,滚动视口 tabindex="0":键盘 Tab 聚焦后,方向键、PageUp / PageDown、Home / End、空格走浏览器原生滚动。
  • 视口带读屏名称:给了 label 时是 role="region" 地标(出现在读屏的地标列表里),没给时是以「可滚动区域」 (跟随 locale,scrollArea.label)命名的 role="group" —— 不给页面上每个无名区域都造一个同名地标。
  • 自绘滚动条对读屏隐藏,它只是指针的操作把手。
  • 内容不溢出时视口不可聚焦,也没有角色与名称。

程序化滚动 ​

宿主本身不滚动,host.scrollTop 总是 0、host.scrollTo() 不起作用。滚动用 scrollToPosition(),参数同原生的 scrollTo(options):

js
await area.scrollToPosition({ top: 0, behavior: 'smooth' });

要读滚动位置或监听 scroll 事件,取影子树里的视口:area.shadowRoot.querySelector('[part="viewport"]')。

API ​

属性

属性Attribute类型默认值说明
labellabelstring | undefined—滚动视口的读屏名称。给了就把视口标成 `role="region"` 地标;不给时用内置文案「可滚动区域」、角色是 `group`。 只在内容溢出(视口可聚焦)时生效
orientationorientation"both" | "horizontal" | "vertical"'vertical'滚动方向:只竖向(默认)、只横向,还是两个方向都能滚
scrollHideDelayscroll-hide-delaynumber600`hover` / `scroll` 模式下,指针移开或停止滚动后再过多少毫秒隐藏滚动条
typetype"always" | "auto" | "hover" | "scroll"'hover'滚动条什么时候显示:`hover` 悬停时(默认,Radix 默认)、`scroll` 滚动时、`auto` 溢出就显示、`always` 轨道始终占位。 取值说明见组件描述

方法

方法说明
scrollToPosition(options?: ScrollToOptions) => Promise<void>把视口滚动到指定位置,参数同 `Element.scrollTo(options)`(`{ top, left, behavior }`,`left` 按浏览器的 scrollLeft 口径, RTL 下向结束侧是负数)。宿主本身不滚动,直接调 `host.scrollTo()` / 改 `host.scrollTop` 没有效果 —— 不叫 `scrollTo` 是因为它是元素原生方法,同名

插槽

名称说明
(默认)可滚动的内容

可定制的内部元素(::part())

part说明
scrollbar滚动条轨道(竖向与横向共用这个 part,`data-orientation` 区分)
thumb滚动条的拖动块
viewport滚动视口(真正滚动的元素,原生滚动条已隐藏)

Apache-2.0 协议开源