mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-21 16:36:27 +08:00
250 lines
13 KiB
Markdown
250 lines
13 KiB
Markdown
# SplitView 分割视图
|
||
|
||
## 概述
|
||
|
||
分割视图(SplitView)是一个带有可折叠侧边栏(Pane)的布局容器控件。常用于应用的主框架布局——侧边栏承载导航菜单、工具面板等辅助内容,主内容区域展示核心业务页面。用户可以通过切换 `IsPaneOpen` 属性来展开或收起侧边栏。
|
||
|
||
AtomUI 的 `SplitView` 是对 Avalonia 原生 `Avalonia.Controls.SplitView` 的增强封装。它在继承 Avalonia 所有基础能力的同时,集成了 AtomUI Design Token 系统和平滑过渡动画,使侧边栏的展开/收起过程更加流畅自然。
|
||
|
||
> 这是 AtomUI 的扩展控件,在 Ant Design 中没有直接对应的组件。其设计灵感来源于 UWP/WinUI 的 NavigationView 模式,同时遵循 AtomUI 的 Design Token 视觉规范。
|
||
|
||
---
|
||
|
||
## 设计原理
|
||
|
||
### 分割视图的设计哲学
|
||
|
||
SplitView 解决的核心问题是:**在有限的屏幕空间内,同时展示导航/工具内容与主体内容,并提供灵活的空间分配策略**。其设计围绕以下几个关键概念:
|
||
|
||
**四种显示模式**(DisplayMode),决定了侧边栏与主内容的空间关系:
|
||
|
||
| 模式 | 设计意图 | 典型用途 |
|
||
|---|---|---|
|
||
| 📌 **Inline** | 侧边栏与主内容并排,展开时主内容区域被压缩 | 宽屏应用,侧边栏常驻时不遮挡内容 |
|
||
| 📐 **CompactInline** | 收起时保留紧凑条(CompactPaneLength),展开时压缩主内容 | 侧导航图标常驻 + 展开显示文字 |
|
||
| 🔲 **Overlay** | 侧边栏覆盖在主内容之上,展开时不影响主内容布局 | 窄屏应用,临时弹出导航面板 |
|
||
| 📏 **CompactOverlay** | 收起时保留紧凑条,展开时覆盖在主内容之上 | 移动端/窄屏的图标导航 + 弹出详情 |
|
||
|
||
**四种面板放置位置**(PanePlacement):
|
||
|
||
| 位置 | 说明 |
|
||
|---|---|
|
||
| `Left` | 侧边栏在左侧(默认,最常用的导航布局) |
|
||
| `Right` | 侧边栏在右侧(如属性面板、详情面板) |
|
||
| `Top` | 面板在顶部 |
|
||
| `Bottom` | 面板在底部 |
|
||
|
||
### Avalonia SplitView 基础能力
|
||
|
||
AtomUI 的 `SplitView` 直接继承自 `Avalonia.Controls.SplitView`。了解 Avalonia SplitView 的基础能力有助于理解 AtomUI 在其之上做了哪些增强。
|
||
|
||
**Avalonia SplitView 的核心职责:**
|
||
|
||
Avalonia 的 `SplitView` 是一个将布局区域一分为二的容器控件——一侧为可折叠的 Pane(面板),另一侧为 Content(主内容)。它通过 `SplitViewTemplateSettings` 内部管理面板的列宽/行高等模板参数,支持四种显示模式和四种面板位置。其继承链为:
|
||
|
||
```
|
||
Control → TemplatedControl → ContentControl → SplitView
|
||
```
|
||
|
||
**Avalonia SplitView 提供的基础属性:**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| `Content` | 主内容区域,可以是任意控件 |
|
||
| `Pane` | 侧边栏内容,可以是任意控件 |
|
||
| `PaneTemplate` / `ContentTemplate` | 面板/主内容的数据模板 |
|
||
| `IsPaneOpen` | 面板是否处于展开状态 |
|
||
| `DisplayMode` | 显示模式(`Overlay`、`Inline`、`CompactInline`、`CompactOverlay`) |
|
||
| `PanePlacement` | 面板放置位置(`Left`、`Right`、`Top`、`Bottom`) |
|
||
| `OpenPaneLength` | 面板完全展开时的宽度(水平)或高度(垂直) |
|
||
| `CompactPaneLength` | 紧凑模式下面板的宽度/高度 |
|
||
| `PaneBackground` | 面板背景画刷 |
|
||
| `UseLightDismissOverlayMode` | 是否在 Overlay 模式下启用点击遮罩自动关闭 |
|
||
|
||
**Avalonia SplitView 提供的基础伪类:**
|
||
|
||
| 伪类 | 说明 |
|
||
|---|---|
|
||
| `:open` | 面板处于展开状态 |
|
||
| `:closed` | 面板处于收起状态 |
|
||
| `:left` | 面板在左侧 |
|
||
| `:right` | 面板在右侧 |
|
||
| `:top` | 面板在顶部 |
|
||
| `:bottom` | 面板在底部 |
|
||
| `:overlay` | Overlay 显示模式 |
|
||
| `:inline` | Inline 显示模式 |
|
||
| `:compactinline` | CompactInline 显示模式 |
|
||
| `:compactoverlay` | CompactOverlay 显示模式 |
|
||
| `:lightDismiss` | 启用了轻量关闭遮罩 |
|
||
|
||
**Avalonia SplitView 提供的事件:**
|
||
|
||
| 事件 | 说明 |
|
||
|---|---|
|
||
| `PaneClosing` | 面板即将关闭时触发,可取消 |
|
||
| `PaneClosed` | 面板关闭完成后触发 |
|
||
|
||
### AtomUI 的扩展设计
|
||
|
||
AtomUI `SplitView` 在 Avalonia SplitView 的基础上做了以下增强:
|
||
|
||
| 扩展能力 | 实现方式 | 设计动机 |
|
||
|---|---|---|
|
||
| **Design Token 集成** | `SplitViewToken` + `RegisterTokenResourceScope` | 面板尺寸、动画参数等可通过 Token 统一管理,支持主题切换 |
|
||
| **平滑过渡动画** | `PaneOpenTransitions` / `PaneCloseTransitions` + 自动配置 | 面板展开/收起时使用缓动动画,而非瞬间切换,提升交互体验 |
|
||
| **分离的展开/收起动画时长** | `PaneOpenMotionDuration` / `PaneCloseMotionDuration` | 允许展开和收起使用不同的动画时长,精细控制交互节奏 |
|
||
| **自定义缓动曲线** | `PaneMotionEasing` 属性 | 支持自定义贝塞尔缓动曲线,默认使用 `cubic-bezier(0.1, 0.9, 0.2, 1.0)` 实现自然的减速效果 |
|
||
| **动画开关** | `IMotionAwareControl` 接口 + `IsMotionEnabled` | 全局或单控件级别禁用动画,适用于性能敏感场景或无障碍需求 |
|
||
| **方向感知的过渡** | 根据 `PanePlacement` 自动选择 Width 或 Height 过渡 | 水平面板过渡宽度,垂直面板过渡高度,动画方向始终正确 |
|
||
| **Token 驱动的面板背景** | `PaneBackground` 默认绑定 `SharedToken.ColorBgContainer` | 面板背景色跟随主题自动切换(亮色/暗色模式) |
|
||
| **Token 驱动的边框颜色** | `HCPaneBorder` 使用 `SharedToken.ColorBorder` | 面板分隔线颜色跟随主题 |
|
||
|
||
---
|
||
|
||
## 功能详解
|
||
|
||
### 显示模式(DisplayMode)
|
||
|
||
显示模式通过 `DisplayMode` 属性设置,控制侧边栏展开/收起时与主内容区域的空间关系。
|
||
|
||
| 模式 | 收起时行为 | 展开时行为 |
|
||
|---|---|---|
|
||
| `Overlay` | 面板完全隐藏(宽度/高度为 0) | 面板覆盖在主内容之上,主内容不移动 |
|
||
| `Inline` | 面板完全隐藏(宽度/高度为 0) | 面板与主内容并排,主内容被压缩 |
|
||
| `CompactInline` | 面板保留紧凑宽度(`CompactPaneLength`) | 面板展开至 `OpenPaneLength`,主内容被压缩 |
|
||
| `CompactOverlay` | 面板保留紧凑宽度(`CompactPaneLength`) | 面板展开并覆盖主内容 |
|
||
|
||
### 面板放置位置(PanePlacement)
|
||
|
||
面板位置通过 `PanePlacement` 属性设置。AtomUI 支持四个方向:
|
||
|
||
- **Left / Right**:面板在水平方向,使用 `Grid.ColumnDefinitions` 布局,过渡 `Width` 属性
|
||
- **Top / Bottom**:面板在垂直方向,使用 `Grid.RowDefinitions` 布局,过渡 `Height` 属性
|
||
|
||
当 `PanePlacement` 改变时,SplitView 会自动重新配置过渡动画(从 Width 过渡切换到 Height 过渡,反之亦然)。
|
||
|
||
### 过渡动画
|
||
|
||
SplitView 的面板展开/收起动画是 AtomUI 的核心增强之一。动画行为如下:
|
||
|
||
1. **OnLoaded 时配置**:控件加载后自动根据 `PanePlacement` 创建对应的 `DoubleTransition`
|
||
2. **OnUnloaded 时清除**:控件卸载时清空过渡,避免不可见控件消耗资源
|
||
3. **方向自适应**:水平面板(Left/Right)过渡 `Width`,垂直面板(Top/Bottom)过渡 `Height`
|
||
4. **展开/收起独立控制**:`PaneOpenTransitions` 和 `PaneCloseTransitions` 分别应用于展开和收起过程,支持不同时长
|
||
|
||
动画参数通过 Token 或属性控制:
|
||
|
||
| 参数 | Token 来源 | 默认值 | 说明 |
|
||
|---|---|---|---|
|
||
| `PaneOpenMotionDuration` | `SplitViewToken.PaneOpenMotionDuration` | 200ms | 展开动画时长 |
|
||
| `PaneCloseMotionDuration` | `SplitViewToken.PaneCloseMotionDuration` | 100ms | 收起动画时长 |
|
||
| `PaneMotionEasing` | `SplitViewToken.PaneMotionEasing` | `cubic-bezier(0.1, 0.9, 0.2, 1.0)` | 缓动曲线 |
|
||
|
||
> 缓动曲线 `(0.1, 0.9, 0.2, 1.0)` 实现了「快启动、缓停止」的自然减速效果,符合 Material Design 和 Ant Design 的动效原则。
|
||
|
||
### 轻量关闭遮罩(Light Dismiss)
|
||
|
||
在 `Overlay` 和 `CompactOverlay` 模式下,当面板展开时,主内容区域会覆盖一层 `LightDismissLayer`(半透明遮罩),颜色由 `SharedToken.ColorBgMask` 控制。点击遮罩可自动关闭面板(需启用 `UseLightDismissOverlayMode`)。
|
||
|
||
### 面板边框分隔线
|
||
|
||
每个面板模板中都包含一条 `HCPaneBorder` 分隔线(`Rectangle`),颜色由 `SharedToken.ColorBorder` 控制:
|
||
- Left 面板:分隔线在右边缘
|
||
- Right 面板:分隔线在左边缘
|
||
- Top 面板:分隔线在底部
|
||
- Bottom 面板:分隔线在顶部
|
||
|
||
---
|
||
|
||
## 与 Ant Design 规范的对齐程度
|
||
|
||
SplitView 是 AtomUI 的**扩展控件**,在 Ant Design 5.0 中没有直接对应的组件。但它遵循了 AtomUI Design Token 系统,确保视觉风格与其他 Ant Design 组件保持一致:
|
||
|
||
| 方面 | 对齐情况 | 说明 |
|
||
|---|---|---|
|
||
| 颜色系统 | ✅ | 面板背景使用 `ColorBgContainer`,边框使用 `ColorBorder`,遮罩使用 `ColorBgMask`——均来自 SharedToken |
|
||
| 动画系统 | ✅ | 缓动曲线和动画时长遵循 Ant Design 动效规范 |
|
||
| Token 架构 | ✅ | 使用 `SplitViewToken` 组件级 Token,支持主题定制 |
|
||
| 对应 Ant Design 组件 | — | 无直接对应,Ant Design 的 `Layout.Sider` 提供类似功能但 API 不同 |
|
||
|
||
---
|
||
|
||
## 继承关系
|
||
|
||
```
|
||
Avalonia.Controls.Primitives.TemplatedControl
|
||
└── Avalonia.Controls.ContentControl
|
||
└── Avalonia.Controls.SplitView
|
||
└── AtomUI.Desktop.Controls.SplitView
|
||
└── implements IMotionAwareControl
|
||
```
|
||
|
||
`SplitView` 通过 `using AvaloniaSplitView = Avalonia.Controls.SplitView;` 别名引用 Avalonia 原生 SplitView,避免类名冲突。
|
||
|
||
**各层级职责划分:**
|
||
|
||
| 层级 | 提供的能力 |
|
||
|---|---|
|
||
| `ContentControl` | 主内容容纳、`Content` / `ContentTemplate`、`ContentPresenter` 模板化呈现 |
|
||
| `Avalonia.Controls.SplitView` | 面板/主内容双区域布局、四种显示模式、四种面板位置、`IsPaneOpen` 状态管理、`PaneClosing` / `PaneClosed` 事件、`SplitViewTemplateSettings` 模板参数计算 |
|
||
| `AtomUI.Desktop.Controls.SplitView` | Design Token 集成、平滑过渡动画(展开/收起独立时长)、自定义缓动曲线、动画开关、Token 驱动的背景/边框颜色 |
|
||
|
||
**实现的共享接口:**
|
||
|
||
| 接口 | 定义位置 | 作用 |
|
||
|---|---|---|
|
||
| `IMotionAwareControl` | `AtomUI.Core` | 支持 `IsMotionEnabled` 动画开关,全局或单控件级别禁用过渡动画 |
|
||
|
||
---
|
||
|
||
## 源码位置
|
||
|
||
| 层级 | 文件路径 | 说明 |
|
||
|---|---|---|
|
||
| 控件类 | `src/AtomUI.Desktop.Controls/SplitView/SplitView.cs` | 桌面端 SplitView 实现 |
|
||
| Token 定义 | `src/AtomUI.Desktop.Controls/SplitView/SplitViewToken.cs` | 组件级 Design Token |
|
||
| 主题模板 | `src/AtomUI.Desktop.Controls/SplitView/Themes/SplitViewTheme.axaml` | ControlTheme AXAML(包含四种面板方向的模板定义) |
|
||
|
||
> 注意:SplitView 没有对应的 `AbstractSplitView` 基类在 `AtomUI.Controls` 中——它直接继承 Avalonia 的 `SplitView`,因为分割视图的行为在各平台上基本一致,不需要抽象基类层。
|
||
|
||
---
|
||
|
||
## 模板结构
|
||
|
||
SplitView 为四种面板位置分别定义了独立的 ControlTemplate。以 Left 面板为例:
|
||
|
||
```
|
||
Grid#Container (Background = TemplateBinding Background)
|
||
├── ColumnDefinition (PaneColumnGridLength) ← 面板列(宽度由 TemplateSettings 计算)
|
||
├── ColumnDefinition (*) ← 主内容列(填充剩余空间)
|
||
│
|
||
├── Panel#PART_PaneRoot (Column=0) ← 面板容器(ClipToBounds, ZIndex=100)
|
||
│ ├── ContentPresenter#PART_PanePresenter ← 面板内容展示器
|
||
│ └── Rectangle#HCPaneBorder ← 面板右边缘分隔线(1px, ColorBorder)
|
||
│
|
||
└── Panel#ContentRoot (Column=1) ← 主内容容器
|
||
├── ContentPresenter#PART_ContentPresenter ← 主内容展示器
|
||
└── Rectangle#LightDismissLayer ← 遮罩层(Overlay 模式展开时可见)
|
||
```
|
||
|
||
**四种方向的布局差异:**
|
||
|
||
| 面板位置 | 布局方式 | 面板对齐 | 分隔线位置 |
|
||
|---|---|---|---|
|
||
| Left | `Grid.ColumnDefinitions` | `HorizontalAlignment="Left"` | 右边缘(`HorizontalAlignment="Right"`) |
|
||
| Right | `Grid.ColumnDefinitions` | `HorizontalAlignment="Right"` | 左边缘(`HorizontalAlignment="Left"`) |
|
||
| Top | `Grid.RowDefinitions` | `VerticalAlignment="Top"` | 底部(`VerticalAlignment="Bottom"`) |
|
||
| Bottom | `Grid.RowDefinitions` | `VerticalAlignment="Bottom"` | 顶部(`VerticalAlignment="Top"`) |
|
||
|
||
**显示模式对布局的影响:**
|
||
|
||
不同的 `DisplayMode` 通过伪类选择器调整面板的 `Grid.Column` / `Grid.ColumnSpan`(或 `Grid.Row` / `Grid.RowSpan`)和尺寸绑定,实现不同的空间分配策略。
|
||
|
||
**过渡动画的应用位置:**
|
||
|
||
动画通过伪类组合选择器精确控制:
|
||
- `:left:open` / `:right:open` → 使用 `PaneOpenTransitions`,目标 Width = `OpenPaneLength`
|
||
- `:left:closed` / `:right:closed` → 使用 `PaneCloseTransitions`,目标 Width = `ClosedPaneWidth`
|
||
- `:top:open` / `:bottom:open` → 使用 `PaneOpenTransitions`,目标 Height = `OpenPaneLength`
|
||
- `:top:closed` / `:bottom:closed` → 使用 `PaneCloseTransitions`,目标 Height = `ClosedPaneHeight`
|