15 KiB
ScrollViewer 滚动视图
概述
ScrollViewer(滚动视图)是用于在有限的显示区域内查看超出边界内容的容器控件。当内容尺寸超过 ScrollViewer 的可视区域时,用户可以通过滚动条(水平和/或垂直)来浏览全部内容。ScrollViewer 是构建可滚动列表、长表单、文档阅读器等场景的基础设施组件。
AtomUI 的 ScrollViewer 是对 Avalonia 原生 ScrollViewer 的主题化增强,通过 Design Token 系统提供了统一的滚动条视觉风格,并增加了极简模式(LiteMode)、自动隐藏(AllowAutoHide)以及平滑过渡动画等能力。
注意:ScrollViewer 是 AtomUI 的扩展控件,在 Ant Design 中没有直接对应的组件。其设计灵感来自 macOS 系统滚动条和现代 UI 框架的滚动条风格。
设计原理
滚动视图的设计目标
滚动视图是 UI 框架中最基础的布局容器之一。AtomUI 对滚动视图的设计目标是:
- 视觉一致性:所有使用 ScrollViewer 的地方(列表、树、菜单、内容面板等)呈现统一的滚动条风格。
- 不干扰内容:滚动条应尽可能低调,不抢占内容空间,不遮挡关键信息。
- 智能显隐:根据使用场景,滚动条可以常驻显示,也可以在鼠标悬浮时自动出现。
- 主题化:滚动条的颜色、粗细、圆角等视觉属性均由 Design Token 控制,随主题(亮色/暗色)自动适配。
两种显示模式
AtomUI ScrollViewer 提供两种显示模式:
| 模式 | IsLiteMode |
行为描述 |
|---|---|---|
| 标准模式(Normal) | false(默认) |
滚动条常驻显示,占据独立空间(不覆盖内容),滑块粗细为 NormalModeThumbThickness(默认 SizeXS) |
| 极简模式(Lite) | true |
滚动条默认隐藏,鼠标进入 ScrollViewer 区域时淡入显示;滑块较细(LiteModeThumbThickness = LineWidthBold),悬浮或拖拽时膨胀为标准粗细;内容区域跨越滚动条空间,不为滚动条预留位置 |
自动隐藏机制
当 AllowAutoHide = true 时,ScrollViewer 会监听全局指针移动事件(通过 IInputManager),判断指针是否在 ScrollViewer 区域内:
- 鼠标进入区域:滚动条不透明度渐变至 1.0(可见)
- 鼠标离开区域:滚动条不透明度渐变至 0.0(隐藏)
- 滑块拖拽中:即使鼠标离开区域,滚动条保持可见;拖拽结束后,如果鼠标不在区域内则隐藏
滚动条分隔符
当同时存在水平和垂直滚动条时,右下角会出现一个分隔面板(ScrollBarsSeparator),使用 ColorBgContainer 背景色。该面板在 IsExpanded = true 时显示,否则隐藏。
Avalonia ScrollViewer 基础能力
AtomUI 的 ScrollViewer 继承自 Avalonia 框架的 Avalonia.Controls.ScrollViewer。理解 Avalonia ScrollViewer 的基础能力有助于理解 AtomUI 在其之上做了哪些扩展。
Avalonia ScrollViewer 的核心职责:
Avalonia 的 ScrollViewer 是一个专用容器控件,内部通过 ScrollContentPresenter 呈现子内容,并根据内容尺寸与可视区域的关系自动显示水平/垂直 ScrollBar。它支持滚动惯性(IsScrollInertiaEnabled)、延迟滚动(IsDeferredScrollingEnabled)、SnapPoints 对齐等高级特性。
Avalonia ScrollViewer 提供的基础属性:
| 属性 | 说明 |
|---|---|
HorizontalScrollBarVisibility |
水平滚动条显示策略:Disabled、Auto(按需)、Visible(始终)、Hidden(隐藏但可滚动) |
VerticalScrollBarVisibility |
垂直滚动条显示策略,选项同上 |
AllowAutoHide |
是否允许滚动条在无交互时自动隐藏 |
IsScrollInertiaEnabled |
是否启用触控/滚轮的滚动惯性效果 |
IsDeferredScrollingEnabled |
延迟滚动模式,拖拽滑块时不实时滚动内容,释放后才更新 |
Offset |
当前滚动偏移量(Vector 类型) |
Extent |
内容的总尺寸 |
Viewport |
可视区域尺寸 |
IsExpanded |
滚动条是否处于展开状态(鼠标悬浮/拖拽时) |
BringIntoViewOnFocusChange |
焦点变化时是否自动滚动到焦点元素 |
AtomUI 的扩展设计
AtomUI ScrollViewer 在 Avalonia ScrollViewer 的基础上做了以下扩展:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| 极简模式 | IsLiteMode 附加属性 + 主题样式切换 |
提供更轻量、不干扰内容的滚动条体验 |
| 自定义 ScrollBar | atom:ScrollBar 替代原生 ScrollBar |
统一滚动条视觉风格,支持 LiteMode 和 Token 系统 |
| 自定义 ScrollBarThumb | atom:ScrollBarThumb 替代原生 Thumb |
支持方向感知、粗细动画、Token 驱动的背景色 |
| 滚动条透明度动画 | ScrollBarOpacity 内部属性 + DoubleTransition |
平滑的显隐过渡效果 |
| 全局指针监听 | 通过 IInputManager 订阅指针事件 |
精准判断指针是否在 ScrollViewer 区域内,控制滚动条显隐 |
| Design Token | ScrollViewerToken + RegisterTokenResourceScope |
所有视觉值从全局/组件级 Token 派生,支持主题切换 |
| Avalonia 原生兼容 | AvaScrollViewerTheme + AvaScrollBarTheme |
同时为 Avalonia 原生 ScrollViewer / ScrollBar 提供主题,确保三方控件也有一致的滚动条风格 |
| 过渡动画 | IMotionAwareControl 接口 + IsMotionEnabled 属性 |
支持全局动画开关,可禁用滚动条的过渡效果 |
功能详解
极简模式(IsLiteMode)
极简模式是 AtomUI ScrollViewer 的特色功能,适用于侧边栏、导航菜单等不希望滚动条占据空间的场景:
- 滑块细线显示:默认状态下,滑块以
LiteModeThumbThickness(=SharedToken.LineWidthBold,约 2-3px)的细线呈现 - 悬浮膨胀:鼠标悬浮到滑块上时,滑块宽度/高度过渡到
NormalModeThumbThickness(=SharedToken.SizeXS,约 8px),方便拖拽操作 - 内容全覆盖:内容区域的 Grid.ColumnSpan / Grid.RowSpan 被设为 2,使内容延伸到滚动条区域下方,滚动条叠加在内容之上
IsLiteMode 是一个附加属性(AttachedProperty),可以设置在 ScrollViewer 本身或使用 ScrollViewer 的其他控件上(例如 atom:NavMenu):
<!-- 设置在 ScrollViewer 上 -->
<atom:ScrollViewer IsLiteMode="True">...</atom:ScrollViewer>
<!-- 通过附加属性设置在其他控件上 -->
<atom:NavMenu atom:ScrollViewer.IsLiteMode="True">...</atom:NavMenu>
自动隐藏(AllowAutoHide)
AllowAutoHide 控制滚动条是否在无交互时自动隐藏:
AllowAutoHide = true(默认):滚动条根据鼠标位置自动显隐,内容区域覆盖滚动条空间AllowAutoHide = false:滚动条常驻显示,滚动条占据独立空间
过渡动画(Transitions)
ScrollViewer 系列控件实现了 IMotionAwareControl 接口,支持以下过渡动画:
| 控件 | 过渡属性 |
|---|---|
ScrollViewer |
ScrollBarsSeparatorOpacity、ScrollBarOpacity |
ScrollBarThumb |
Background(颜色过渡)、Width(垂直方向粗细变化)、Height(水平方向粗细变化) |
动画在 OnLoaded 时配置,OnUnloaded 时清除。通过 IsMotionEnabled 属性可全局开关:
<atom:ScrollViewer IsMotionEnabled="False"><!-- 禁用动画 --></atom:ScrollViewer>
Avalonia 原生控件主题兼容
AtomUI 不仅为自己的 atom:ScrollViewer 和 atom:ScrollBar 提供主题,还为 Avalonia 原生的 ScrollViewer 和 ScrollBar 提供了兼容主题(AvaScrollViewerTheme.axaml 和 AvaScrollBarTheme.axaml)。这意味着:
- 即使三方控件内部使用原生 Avalonia
ScrollViewer,也会呈现 AtomUI 的滚动条风格 - 原生滚动条使用标准粗细,不支持 LiteMode
- 同样通过
ScrollViewerToken获取颜色和间距值
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 滚动视图 | Ant Design 无独立 ScrollViewer 组件 | ✅ 独立控件 | — 不适用 |
| 自定义滚动条样式 | 部分组件(如 Table、List)内置虚拟滚动 | ✅ 统一的 Token 驱动滚动条风格 | ✅ 风格一致性更好 |
| 自动隐藏滚动条 | 由浏览器/操作系统控制 | ✅ AllowAutoHide + 极简模式 |
✅ 增强能力 |
| 滚动条颜色随主题 | CSS 有限支持 | ✅ Token 系统自动适配亮色/暗色主题 | ✅ 增强能力 |
继承关系
ScrollViewer
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ContentControl
└── Avalonia.Controls.ScrollViewer
└── AtomUI.Controls.Commons.AbstractScrollViewer (IMotionAwareControl)
└── AtomUI.Desktop.Controls.ScrollViewer
ScrollBar
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.Primitives.RangeBase
└── Avalonia.Controls.Primitives.ScrollBar
└── AtomUI.Controls.Commons.AbstractScrollBar (IMotionAwareControl)
└── AtomUI.Desktop.Controls.ScrollBar
ScrollBarThumb
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.Primitives.Thumb
└── AtomUI.Controls.Commons.AbstractScrollBarThumb (IMotionAwareControl)
└── AtomUI.Desktop.Controls.ScrollBarThumb
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
Avalonia.Controls.ScrollViewer |
滚动逻辑、ScrollContentPresenter、滚动条可见性控制、惯性滚动、SnapPoints、AllowAutoHide |
AbstractScrollViewer (AtomUI.Controls) |
过渡动画配置(IMotionAwareControl)、全局指针监听实现自动显隐、拖拽状态追踪、ScrollBarOpacity / ScrollBarsSeparatorOpacity 内部属性 |
ScrollViewer (AtomUI.Desktop.Controls) |
IsLiteMode 附加属性、Token 作用域注册 |
Avalonia.Controls.Primitives.ScrollBar |
滚动条基础逻辑、Value / Minimum / Maximum、ViewportSize、AllowAutoHide、IsExpanded |
AbstractScrollBar (AtomUI.Controls) |
IsEffectiveExpanded 内部状态、IMotionAwareControl 接口、自动隐藏状态管理 |
ScrollBar (AtomUI.Desktop.Controls) |
IsLiteMode 属性、LiteMode 下的展开逻辑覆写、Token 作用域注册 |
Avalonia.Controls.Primitives.Thumb |
拖拽行为(DragStarted / DragDelta / DragCompleted 事件) |
AbstractScrollBarThumb (AtomUI.Controls) |
Orientation 属性、过渡动画(Background/Width/Height)、IMotionAwareControl、IsExpanded 内部属性 |
ScrollBarThumb (AtomUI.Desktop.Controls) |
IsLiteMode 属性 |
实现的共享接口:
| 接口 | 定义位置 | 实现控件 | 作用 |
|---|---|---|---|
IMotionAwareControl |
AtomUI.Controls |
AbstractScrollViewer、AbstractScrollBar、AbstractScrollBarThumb |
支持 IsMotionEnabled 属性,全局动画开关 |
源码位置
基类层(AtomUI.Controls)—— 设备无关
| 文件路径 | 说明 |
|---|---|
src/AtomUI.Controls/ScrollViewer/AbstractScrollViewer.cs |
滚动视图抽象基类:动画、自动隐藏逻辑、指针监听 |
src/AtomUI.Controls/ScrollViewer/AbstractScrollBar.cs |
滚动条抽象基类:展开状态管理、动画开关 |
src/AtomUI.Controls/ScrollViewer/AbstractScrollBarThumb.cs |
滚动条滑块抽象基类:方向感知、粗细/背景色过渡动画 |
src/AtomUI.Controls/ScrollViewer/ScrollBarReflectionExtensions.cs |
内部工具:通过反射访问 Avalonia ScrollBar 的私有成员 |
src/AtomUI.Controls/ScrollViewer/ScrollBarRepeatButton.cs |
滚动条翻页按钮(透明、不可聚焦) |
平台层(AtomUI.Desktop.Controls)—— 桌面端具体实现
| 文件路径 | 说明 |
|---|---|
src/AtomUI.Desktop.Controls/ScrollViewer/ScrollViewer.cs |
桌面端 ScrollViewer:IsLiteMode 附加属性、Token 注册 |
src/AtomUI.Desktop.Controls/ScrollViewer/ScrollBar.cs |
桌面端 ScrollBar:IsLiteMode 属性、LiteMode 展开逻辑覆写 |
src/AtomUI.Desktop.Controls/ScrollViewer/ScrollBarThumb.cs |
桌面端 ScrollBarThumb:IsLiteMode 属性 |
src/AtomUI.Desktop.Controls/ScrollViewer/ScrollViewerToken.cs |
组件级 Design Token |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollViewerTheme.axaml |
AtomUI ScrollViewer 主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollBarTheme.axaml |
AtomUI ScrollBar 主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollBarThumbTheme.axaml |
AtomUI ScrollBarThumb 主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollBarThumbTheme.cs |
ScrollBarThumb 主题 code-behind |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/AvaScrollViewerTheme.axaml |
Avalonia 原生 ScrollViewer 兼容主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/AvaScrollBarTheme.axaml |
Avalonia 原生 ScrollBar 兼容主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollBarRepeatButtonTheme.axaml |
ScrollBarRepeatButton 主题 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollViewerThemeConstants.cs |
模板部件名称常量 |
src/AtomUI.Desktop.Controls/ScrollViewer/Themes/ScrollViewerThemes.axaml |
主题资源字典汇总注册 |
模板结构
ScrollViewer 模板
Grid (ColumnDefinitions="*,Auto", RowDefinitions="*,Auto")
├── ScopeAwareOverlayLayerPanel (Grid.Row=0, Grid.Column=0)
│ └── ScrollContentPresenter (PART_ContentPresenter) ← 内容呈现器
│ └── ScrollGestureRecognizer ← 触控/滚轮手势识别
├── ScrollBar#PART_HorizontalScrollBar ← 水平滚动条 (Grid.Row=1, Grid.Column=0)
├── ScrollBar#PART_VerticalScrollBar ← 垂直滚动条 (Grid.Row=0, Grid.Column=1)
└── Panel#ScrollBarsSeparator ← 右下角分隔面板 (Grid.Row=1, Grid.Column=1)
布局说明:
- 内容区域位于左上(0,0),水平滚动条位于下方(1,0),垂直滚动条位于右侧(0,1),分隔面板位于右下角(1,1)
- 当
AllowAutoHide = true或IsLiteMode = true时,内容区域的Grid.ColumnSpan和Grid.RowSpan被设为 2,使内容延伸到滚动条区域
ScrollBar 模板
Border#Frame
└── Grid#RootLayout
└── Track (PART_Track)
├── Track.DecreaseButton → ScrollBarRepeatButton (PART_PageUpButton) ← 向上翻页区域
├── Track.Thumb → ScrollBarThumb (PART_Thumb) ← 滑块
└── Track.IncreaseButton → ScrollBarRepeatButton (PART_PageDownButton) ← 向下翻页区域
ScrollBarThumb 模板
Border#Frame (Background, CornerRadius, Margin) ← 滑块可视外观
模板部件常量
| 常量名 | 值 | 说明 |
|---|---|---|
ScrollViewerThemeConstants.PageUpButtonPart |
"PART_PageUpButton" |
向上/向左翻页按钮 |
ScrollViewerThemeConstants.PageDownButtonPart |
"PART_PageDownButton" |
向下/向右翻页按钮 |
ScrollViewerThemeConstants.TrackPart |
"PART_Track" |
滚动条轨道 |
ScrollViewerThemeConstants.ThumbPart |
"PART_Thumb" |
滚动条滑块 |