12 KiB
Rate 评分
概述
评分(Rate)用于对事物进行快速的评级操作,默认以星星图标展示。支持整星和半星两种精度,支持自定义图标/字符,支持清除评分、只读模式和键盘操作。评分控件广泛用于电商商品评价、服务满意度调查、内容质量打分等场景。
AtomUI 的 Rate 控件复刻了 Ant Design 5.0 Rate 的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
设计原理
Ant Design 的评分设计哲学
Ant Design 对评分的定位是:「对事物进行快速的评级操作」。其核心原则包括:
- 快速评级 — 通过鼠标点击/悬浮实现直觉化的评级操作,无需额外操作步骤
- 半星精度 — 通过
allowHalf支持更精细的 0.5 步长评级,适用于需要更高分辨率评分的场景 - 可清除 — 再次点击已选星级可清除评分,回到初始状态
- 自定义字符 — 不限于星星,可替换为心形、字母、文字等任意字符,适应不同业务语境
- 提示文案 — 每颗星可对应不同的描述文案(如 "terrible"、"bad"、"normal"、"good"、"wonderful"),增强评分的语义明确性
Avalonia 基础能力
与其他 AtomUI 控件不同,Rate 没有继承自 Avalonia 的任何具体控件——它直接继承自 TemplatedControl(通过 AbstractRate 中间层)。这是因为 Avalonia 没有提供原生的评分控件,Rate 是 AtomUI 完全自研的控件。
Control → TemplatedControl → AbstractRate → Rate
Rate 控件内部组合使用了 ItemsControl(RateItemsControl)来管理多个 RateItem 子控件,每个 RateItem 代表一颗星。这种组合模式使得星星的数量可以动态变化,且每颗星独立管理自己的选中/悬浮/半选状态。
AtomUI 的扩展设计
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| 半星评分 | IsAllowHalf 属性 + 裁剪区域计算 |
对齐 Ant Design allowHalf,通过检测鼠标在星星左半/右半的位置实现精细评级 |
| 自定义字符 | Character 属性(支持 Icon / char / string) |
支持 PathIcon 图标、单字符或文字替换默认星星,适应不同业务场景 |
| 可清除 | IsAllowClear 属性 |
再次点击相同值可归零,默认启用 |
| 三种尺寸 | ISizeTypeAware + SizeType |
Large/Middle/Small 三种尺寸,通过 Token 映射到不同的星星大小 |
| 悬浮缩放 | StarHoverScale Token + TransformOperationsTransition |
悬浮时星星放大至 120%,带过渡动画 |
| 提示文案 | ToolTips 属性 |
为每颗星设置 ToolTip,鼠标悬浮时显示对应文案 |
| 键盘操作 | IsKeyboardEnabled + 方向键 |
左/右方向键调整评分,支持半星步长和整星步长 |
| 悬浮预览 | 内部 EffectiveValue + 全局指针监听 |
鼠标移入时实时预览评分效果,移出后恢复到实际值 |
| 表单集成 | IFormItemAware 接口 |
可参与 FormItem 验证流程,表单值类型为 double |
| Design Token | RateToken + RegisterTokenResourceScope |
星星颜色、大小、缩放比例全部从 Token 派生 |
功能详解
星星字符渲染
Rate 控件的每颗星由内部 RateItem 控件渲染。每个 RateItem 通过 VisualBrush 技术将字符(图标或文字)渲染为两层:
- 背景层(
CharacterBgBrush)— 使用StarBgColor颜色渲染,始终可见 - 前景层(
CharacterBrush)— 使用StarColor颜色渲染,通过裁剪区域控制可见范围
支持三种字符类型:
- Icon 图标(默认
StarFilled)— 通过FillBrush/StrokeBrush着色 - 单字符
char— 通过RateCharacter内部控件自绘制 - 字符串
string— 取首字符,同样通过RateCharacter渲染
半星评分机制
当 IsAllowHalf = true 时,每颗星被水平等分为左右两半:
- 鼠标位于星星左半区域:评分为
index + 0.5(半星) - 鼠标位于星星右半区域:评分为
index + 1(整星)
半星状态通过 RectangleGeometry 裁剪实现——将前景层裁剪至星星宽度的一半,仅显示左半部分。
悬浮预览与点击确认
Rate 采用「悬浮预览 + 点击确认」的交互模型:
- 悬浮预览:鼠标在星星上移动时,内部
EffectiveValue实时更新,触发HoverValueChanged事件,星星即时反映预览效果 - 移出恢复:鼠标移出 Rate 区域时,
EffectiveValue恢复为Value(已确认的值) - 点击确认:鼠标在星星上点击释放时,如果位置未变化,将
EffectiveValue写入Value,触发ValueChanged事件
全局指针监听(通过 IInputManager.Process)确保即使鼠标快速移出也能正确恢复状态。
清除评分
当 IsAllowClear = true(默认)时:
- 再次点击当前已选中的星级,
Value被清零 - 半星模式下需精确点击同一半星才会清除
- 整星模式下点击同一星即清除
键盘操作
当 IsKeyboardEnabled = true(默认)且 Rate 获得焦点时:
- ← 左方向键:减少评分(半星模式减 0.5,整星模式减 1)
- → 右方向键:增加评分(半星模式加 0.5,整星模式加 1)
- 评分值被限制在
[0, Count]范围内 - 键盘操作时会在当前焦点星上显示虚线边框(
IsFocusStartItem),提供视觉反馈
悬浮缩放动画
每颗 RateItem 在悬浮时触发缩放动画:
- 悬浮时以中心点为原点缩放至
StarHoverScale(默认 1.2 倍) - 通过
TransformOperationsTransition实现平滑过渡 - 动画受
IsMotionEnabled控制,可全局禁用
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 基本评分 | ✅ <Rate /> |
✅ <atom:Rate /> |
✅ 完全对齐 |
半星 allowHalf |
✅ 布尔 | ✅ IsAllowHalf |
✅ 完全对齐 |
可清除 allowClear |
✅ 布尔 | ✅ IsAllowClear |
✅ 完全对齐 |
自定义字符 character |
✅ ReactNode / 函数 | ✅ Character(Icon / char / string) |
⚠️ 不支持按 index 动态字符函数 |
总数 count |
✅ 数字 | ✅ Count |
✅ 完全对齐 |
默认值 defaultValue |
✅ 数字 | ✅ DefaultValue |
✅ 完全对齐 |
当前值 value |
✅ 受控 | ✅ Value |
✅ 完全对齐 |
禁用 disabled |
✅ 布尔 | ✅ IsEnabled |
✅ 完全对齐 |
提示 tooltips |
✅ 字符串数组 | ✅ ToolTips |
✅ 完全对齐 |
onChange |
✅ 回调 | ✅ ValueChanged 事件 |
✅ 完全对齐 |
onHoverChange |
✅ 回调 | ✅ HoverValueChanged 事件 |
✅ 完全对齐 |
| 键盘支持 | ✅ 方向键 | ✅ IsKeyboardEnabled + 方向键 |
✅ 完全对齐 |
继承关系
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Controls.Commons.AbstractRate
├── implements IMotionAwareControl
├── implements ISizeTypeAware
└── implements IFormItemAware
└── AtomUI.Desktop.Controls.Rate
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
TemplatedControl |
模板化控件基础设施、OnApplyTemplate、样式系统 |
AbstractRate |
全部评分核心逻辑:属性定义、星星数量管理、悬浮/点击/键盘交互、EffectiveValue 预览机制、半星计算、清除逻辑、ToolTip 配置、表单集成 |
AtomUI.Desktop.Controls.Rate |
桌面端 Token 注册 (RateToken.ScopeProvider) |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
IMotionAwareControl |
AtomUI.Controls.Shared |
过渡动画开关(控制悬浮缩放动画) |
ISizeTypeAware |
AtomUI.Controls.Shared |
三种尺寸切换(Small / Middle / Large) |
IFormItemAware |
AtomUI.Controls.Shared |
可作为 FormItem 的子控件参与表单验证 |
内部控件架构
Rate 控件由以下内部控件组成(均为 internal,不对外暴露):
| 内部控件 | 基类 | 作用 |
|---|---|---|
RateItemsControl |
ItemsControl |
管理 RateItem 子项集合,传递属性绑定 |
RateItem |
TemplatedControl |
单颗星的渲染与交互,管理选中状态、悬浮缩放、裁剪 |
RateCharacter |
Control |
文字字符的自绘制控件(当 Character 为 char/string 时使用) |
RateItem 的三种选中状态:
| 枚举值 | 说明 |
|---|---|
RateItemSelectedState.None |
未选中,仅显示背景层 |
RateItemSelectedState.FullSelected |
整星选中,前景层完全覆盖 |
RateItemSelectedState.HalfSelected |
半星选中,前景层裁剪至左半 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 基类 | src/AtomUI.Controls/Rate/AbstractRate.cs |
跨平台基类(547 行核心逻辑) |
| 内部控件 | src/AtomUI.Controls/Rate/RateItem.cs |
单颗星控件 |
| 内部控件 | src/AtomUI.Controls/Rate/RateItemsControl.cs |
星星集合容器 |
| 内部控件 | src/AtomUI.Controls/Rate/RateCharacter.cs |
文字字符自绘制控件 |
| 事件参数 | src/AtomUI.Controls/Rate/RateValueChangedEventArgs.cs |
值变化事件参数 |
| 控件类 | src/AtomUI.Desktop.Controls/Rate/Rate.cs |
桌面端 Rate |
| Token 定义 | src/AtomUI.Desktop.Controls/Rate/RateToken.cs |
组件级 Design Token |
| 主题模板 | src/AtomUI.Desktop.Controls/Rate/Themes/RateTheme.axaml |
Rate ControlTheme |
| 主题模板 | src/AtomUI.Desktop.Controls/Rate/Themes/RateItemTheme.axaml |
RateItem ControlTheme |
| 主题模板 | src/AtomUI.Desktop.Controls/Rate/Themes/RateItemsControlTheme.axaml |
RateItemsControl ControlTheme |
| 主题注册 | src/AtomUI.Desktop.Controls/Rate/Themes/RateThemes.axaml |
ResourceDictionary 合并入口 |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/DataEntry/RateShowCase.axaml |
使用范例 |
模板结构
Rate 模板
Border#Frame
└── RateItemsControl (PART_RateItems)
└── StackPanel (Orientation=Horizontal, Spacing=SpacingXS)
├── RateItem[0] ← 每颗星一个内部控件
├── RateItem[1]
├── ...
└── RateItem[Count-1]
RateItem 模板
Border (Background=Transparent)
└── Panel
├── DashedBorder (BorderThickness=0.75, StrokeDashArray=4,2) ← 键盘焦点虚线框(IsFocusStartItem 时可见)
├── Rectangle (Fill=CharacterBgBrush) ← 背景层(未选中颜色)
└── Rectangle#ActiveItem (Clip=StarClip) ← 前景层(选中颜色,半星时被裁剪)
RateItem 模板分层设计:
- 虚线焦点框:使用
DashedBorder,仅在键盘操作时(IsFocusStartItem = true)显示,提供无障碍视觉反馈 - 背景层:始终显示,使用
StarBgColor着色的字符/图标,作为未选中状态的视觉 - 前景层:选中时显示,使用
StarColor着色的字符/图标。半星模式下通过StarClip(RectangleGeometry)裁剪至左半
模板部件常量
| 部件名 | 控件类型 | 说明 |
|---|---|---|
PART_RateItems |
RateItemsControl (内部 ItemsControl) |
星星集合容器 |
Frame |
Border |
Rate 根框架 |
ActiveItem |
Rectangle |
RateItem 中的前景层(选中色) |
PART_ItemsPresenter |
ItemsPresenter |
RateItemsControl 中的子项呈现器 |
ItemsLayout |
StackPanel |
星星水平排列容器 |
PART_WaveSpirit |
— | Rate 不使用波纹效果 |