DirectSurface UIDirectSurface UI
开始使用
文档/布局系统

ScrollViewer 滚动容器

RenderScrollViewer 为单个子节点提供滚动能力,支持垂直、水平和双向滚动。它还会把可视区域信息同步给实现滚动视口接口的子组件。

LIVE CANVAS可运行组件示例
ON-DEMAND RUNTIME交互式示例将在进入视区时启动避免文档首屏同时初始化多个 Canvas Runtime

API 总览

主类:

  • RenderScrollViewer

相关 public type:

  • ScrollDirection

导入:

import { RenderScrollViewer, type ScrollDirection } from 'ds-ui'

何时使用

  • 普通内容区、设置面板、文档侧栏需要滚动。
  • 子组件是自然尺寸内容,外层只负责裁剪和滚动条。
  • 子组件实现了滚动视口接口,需要接收当前可视区域。

何时不要使用

  • 大表格、大树、大文本编辑器已经内置滚动和虚拟化,不应再用同方向 ScrollViewer 包一层。
  • 页面根部已经由工作区管理滚动时,不要再嵌套同方向滚动容器。
  • 需要复杂虚拟化时,ScrollViewer 只负责滚动,不替代数据组件自身的可视范围计算。

最小示例

import { RenderScrollViewer, RenderStackPanel, RenderText } from 'ds-ui' const list = new RenderStackPanel({  orientation: 'vertical',  spacing: 6,}) list.addChild(new RenderText('第一行'))list.addChild(new RenderText('第二行')) const scroll = new RenderScrollViewer({  direction: 'vertical',  child: list,})

滚动方向

  • vertical:垂直滚动。
  • horizontal:水平滚动。
  • both:双向滚动。

构造参数

参数 类型 默认值 说明
direction 'vertical' | 'horizontal' | 'both' 'vertical' 滚动方向。
child RenderBox undefined 被滚动的唯一子节点。

属性和方法

API 类型 / 返回值 说明
child RenderBox | undefined 当前唯一子节点。
direction ScrollDirection 滚动方向。运行时修改后应触发布局或由父级重建。
scrollX number 横向滚动偏移。赋值时会被限制到合法范围。
scrollY number 纵向滚动偏移。赋值时会被限制到合法范围。
scrollBarSize number 当前主题下滚动条尺寸。
setChild(child) void 替换子节点,解除旧子节点父子关系并触发布局。
revealDescendant(target, margin?) boolean 尽量让后代节点进入视窗。滚动位置变化时返回 true。

滚动到目标

revealDescendant(target, margin) 可以让某个后代节点尽量进入可视区域。

import { RenderScrollViewer, RenderStackPanel, RenderText } from 'ds-ui' const list = new RenderStackPanel({  orientation: 'vertical',  spacing: 6,})list.addChild(new RenderText('第一行')) const scroll = new RenderScrollViewer({ direction: 'vertical', child: list })scroll.revealDescendant(list, 12)

业务组件在执行定位、搜索跳转、选中项跳转时,可以调用这个方法。但如果目标已经在视窗内,应优先保持当前滚动位置,避免点击后界面跳动。

布局规则

  • ScrollViewer 自身尺寸由父约束决定;无界宽度默认按 300,无界高度默认按 200。
  • 垂直滚动时,子节点先按完整视窗宽度布局;只有内容确实溢出时才为纵向滚动条预留宽度并重新布局。
  • 横向滚动时,子节点先按完整视窗高度布局;只有内容确实溢出时才为横向滚动条预留高度并重新布局。
  • 响应式子节点会迭代到“子节点约束、滚动条和 viewport”一致的状态;如果预留滚动条会让内容恰好不再溢出并形成二状态振荡,则保守保留滚动条区域。
  • 双向滚动时,子节点在两个方向都可以自然伸展。
  • 稳定布局下只有内容尺寸超过视窗才绘制对应方向滚动条;二状态振荡的保守状态会显示不可拖动的完整 thumb,明确表示该方向无需滚动但已保留布局区域。
  • 双向滚动条会联动求稳定状态:纵向条占用宽度后如果导致横向溢出,横向条也会显示,反之亦然。
  • 子节点 offset 会设置为 -scrollX/-scrollY,绘制时通过 clip 限制在视窗内。

滚动条外观与占位

ScrollViewer 与 DataGrid、TreeGrid、ItemsControl、TreeView、Table、PropertyGrid、MarkdownViewer、ClinicalFlowSheet、Scheduler、PlainTextEditor、MedicalRecordEditor、ComboBox 等组件复用统一的滚动条几何、绘制和指针交互契约:默认滑块较细,指针进入滚动槽后加粗,拖拽时使用 pressed 状态。滑块的最小长度和两端留白也由统一样式配方决定。

scrollbarSize 表示稳定的滚动槽和命中区域,不等于滑块当前可见粗细。hover 或拖拽只会让滑块在既有滚动槽内居中变粗,不会改变 viewport、子节点约束或内容排版。因此交互状态切换只触发重绘,不触发布局,也不会出现内容宽度跳动。

事件行为

操作 行为
鼠标滚轮 根据方向滚动内容。滚动后返回 true 消费事件。
滚动条拖拽 拖拽对应方向滚动条 thumb。
滚动槽点击 将 thumb 一次跳转到点击位置,不开始拖拽,也不捕获指针。
内容拖拽 内容可滚动时,按方向和拖拽意图接管手势。
滚动到边界 如果当前方向内容可滚动,即使偏移未变化也会按设计消费同方向 wheel,避免滚动穿透。
指针取消 / 释放 只有启动拖拽的同一个指针可更新或结束交互;结束时清理 pressed 和 pointer capture,并按最终位置恢复 hover。

视口同步

如果子组件实现滚动视口客户端接口,ScrollViewer 会在布局和滚动时同步:

{
  viewWidth,
  viewHeight,
  scrollX,
  scrollY,
}

这适合 Masonry、MarkdownViewer 等需要知道可视区域来跳过不可见内容的组件。普通子组件无需关心这个接口。

常见错误

同方向嵌套滚动

内外两个垂直 ScrollViewer 会让 wheel 归属、边界消费和虚拟化判断变复杂。页面结构应明确“谁拥有滚动”。

用 ScrollViewer 包大表格

DataGrid、TreeView、PlainTextEditor 等复杂组件通常自己管理滚动。外层再包 ScrollViewer 会破坏命中、滚动条和可视范围。

期望自动回到顶部

替换内容后 ScrollViewer 不会自动把滚动位置归零。业务如果需要,应显式设置 scrollX = 0、scrollY = 0。

使用建议

  • 不要在一个滚动方向上嵌套多个 ScrollViewer,滚轮和虚拟化会互相干扰。
  • DataGrid、TreeView、PlainTextEditor、MedicalRecordEditor 这类复杂组件通常应自己管理滚动。
  • 普通内容容器、设置面板、文档侧栏可以使用 ScrollViewer。
  • 需要大数据性能时,滚动容器只解决裁剪和滚动条,不替代数据虚拟化。

相关文档