mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 14:37:19 +08:00
19 KiB
19 KiB
AtomUI 主题系统 — 主题算法与调色板生成
本文档详细描述 AtomUI 主题算法(Default/Dark/Compact)的实现以及调色板生成算法,对应 Ant Design 5.0 中
theme.algorithm的完整 C# 实现。
1. 主题算法概述
1.1 Ant Design 中的主题算法
Ant Design 5.0 提供三种内置算法:
| 算法 | Ant Design API | 功能 |
|---|---|---|
| 默认算法 | theme.defaultAlgorithm |
亮色主题(默认) |
| 暗色算法 | theme.darkAlgorithm |
暗色主题 |
| 紧凑算法 | theme.compactAlgorithm |
紧凑布局(缩小尺寸和间距) |
算法可以组合使用,例如 algorithm: [theme.darkAlgorithm, theme.compactAlgorithm] 产出暗色紧凑主题。
1.2 AtomUI 的实现
AtomUI 使用 装饰器(Decorator)/ 链式(Chain)模式 实现算法组合:
public enum ThemeAlgorithm
{
Default, // 默认(亮色)
Dark, // 暗色
Compact // 紧凑
}
算法链构建示例(Dark + Compact):
DefaultThemeVariantCalculator
│
│ 作为 baseCalculator 传入
▼
DarkThemeVariantCalculator(baseCalculator)
│
│ 作为 baseCalculator 传入
▼
CompactThemeVariantCalculator(baseCalculator)
│
│ Calculate(designToken)
│ ├── _compositeGenerator.Calculate() ← 先执行 Dark
│ │ └── _compositeGenerator.Calculate() ← Dark 先执行 Default
│ └── 覆盖 Compact 特定的 Token
▼
最终 DesignToken
2. 算法接口与基类
2.1 IThemeVariantCalculator
public interface IThemeVariantCalculator
{
void Calculate(DesignToken designToken);
Color ColorBgBase { get; } // 计算后的背景基色
Color ColorTextBase { get; } // 计算后的文本基色
}
2.2 AbstractThemeVariantCalculator
所有算法的抽象基类,提供通用的颜色梯度计算方法:
public abstract class AbstractThemeVariantCalculator : IThemeVariantCalculator
{
protected IThemeVariantCalculator? _compositeGenerator; // 装饰器链
protected Color _colorBgBase;
protected Color _colorTextBase;
// 子类需实现:生成色板映射
protected virtual ColorMap GenerateColorPalettes(Color baseColor) { ... }
// 子类需实现:计算中性色(文本/填充/背景/边框)
protected virtual void CalculateNeutralColorPalettes(...) { ... }
// 核心方法:将 ColorMap 赋值到 DesignToken 的各色系
protected void CalculateColorMapTokenValues(DesignToken designToken)
{
// 1. 为 Primary/Success/Warning/Error/Info/Link 生成色板
var primaryColors = GenerateColorPalettes(colorPrimaryBase);
var successColors = GenerateColorPalettes(colorSuccessBase);
// ...
// 2. 中性色计算
CalculateNeutralColorPalettes(colorBgBase, colorTextBase, designToken);
// 3. 将色板映射到 DesignToken 属性
designToken.ColorPrimaryBg = primaryColors.Color1; // 1号色
designToken.ColorPrimaryBgHover = primaryColors.Color2; // 2号色
designToken.ColorPrimaryBorder = primaryColors.Color3; // 3号色
designToken.ColorPrimaryBorderHover = primaryColors.Color4; // 4号色
designToken.ColorPrimaryHover = primaryColors.Color5; // 5号色
designToken.ColorPrimary = primaryColors.Color6; // 6号色 (主色)
designToken.ColorPrimaryActive = primaryColors.Color7; // 7号色
// ... 同理 Success/Warning/Error/Info/Link
}
}
2.3 ColorMap
10 级色阶的数据容器:
public record ColorMap
{
public Color Color1 { get; set; } // 1号色 — 最浅
public Color Color2 { get; set; } // 2号色
public Color Color3 { get; set; } // 3号色
public Color Color4 { get; set; } // 4号色
public Color Color5 { get; set; } // 5号色
public Color Color6 { get; set; } // 6号色 — 主色
public Color Color7 { get; set; } // 7号色
public Color Color8 { get; set; } // 8号色
public Color Color9 { get; set; } // 9号色
public Color Color10 { get; set; } // 10号色 — 最深
}
3. DefaultThemeVariantCalculator(亮色算法)
3.1 职责
- 设置默认的
ColorBgBase = White(255,255,255)和ColorTextBase = Black(0,0,0) - 生成所有预设颜色的色板
- 计算完整的 Map Token(颜色/字体/尺寸/高度/样式)
3.2 Calculate() 流程
public override void Calculate(DesignToken designToken)
{
// 1. 用户可能已自定义 ColorBgBase / ColorTextBase
if (designToken.ColorBgBase.HasValue)
_colorBgBase = designToken.ColorBgBase.Value;
if (designToken.ColorTextBase.HasValue)
_colorTextBase = designToken.ColorTextBase.Value;
// 2. 为 14 种预设颜色生成色板
SetupColorPalettes(designToken);
// 3. 计算所有色系的 Map Token(10 级色阶 → DesignToken 属性)
CalculateColorMapTokenValues(designToken);
// 4. 计算非颜色类 Map Token
CalculatorUtils.CalculateFontMapTokenValues(designToken); // 字体梯度
CalculatorUtils.CalculateSizeMapTokenValues(designToken); // 尺寸梯度
CalculatorUtils.CalculateControlHeightMapTokenValues(designToken); // 高度梯度
CalculatorUtils.CalculateStyleMapTokenValues(designToken); // 圆角/动效梯度
}
3.3 亮色模式色板映射
protected override ColorMap GenerateColorPalettes(Color baseColor)
{
var colors = PaletteGenerator.GeneratePalette(baseColor);
// 亮色模式:直接使用色板序列
return new ColorMap
{
Color1 = colors[0], // 最浅
Color2 = colors[1],
Color3 = colors[2],
Color4 = colors[3],
Color5 = colors[4],
Color6 = colors[5], // 主色
Color7 = colors[6], // 最深
Color8 = colors[4], // 文本悬浮 = 5号色
Color9 = colors[5], // 文本色 = 6号色
Color10 = colors[6], // 文本激活 = 7号色
};
}
3.4 亮色模式中性色计算
// 文本色 — 黑色基底加透明度
designToken.ColorText = AlphaColor(colorTextBase, 0.88);
designToken.ColorTextSecondary = AlphaColor(colorTextBase, 0.65);
designToken.ColorTextTertiary = AlphaColor(colorTextBase, 0.45);
designToken.ColorTextQuaternary = AlphaColor(colorTextBase, 0.25);
// 填充色 — 黑色基底加透明度
designToken.ColorFill = AlphaColor(colorTextBase, 0.15);
designToken.ColorFillSecondary = AlphaColor(colorTextBase, 0.06);
designToken.ColorFillTertiary = AlphaColor(colorTextBase, 0.04);
designToken.ColorFillQuaternary = AlphaColor(colorTextBase, 0.02);
// 背景色 — 白色基底降低亮度
designToken.ColorBgLayout = SolidColor(colorBgBase, 4); // 微暗
designToken.ColorBgContainer = SolidColor(colorBgBase, 0); // 白色
designToken.ColorBgElevated = SolidColor(colorBgBase, 0); // 白色
// 边框色 — 白色基底降低亮度
designToken.ColorBorder = SolidColor(colorBgBase, 15);
designToken.ColorBorderSecondary = SolidColor(colorBgBase, 6);
4. DarkThemeVariantCalculator(暗色算法)
4.1 职责
- 装饰 Default 算法(先执行 Default)
- 覆盖为暗色模式的
ColorBgBase = Black(0,0,0)和ColorTextBase = White(255,255,255) - 使用暗色色板算法生成色阶
- 调整选中状态背景色
4.2 Calculate() 流程
public override void Calculate(DesignToken designToken)
{
// 1. 先执行链中的前一个算法(Default)
_compositeGenerator!.Calculate(designToken);
// 2. 覆盖基色
_colorBgBase = Color.FromRgb(0, 0, 0); // 黑色
_colorTextBase = Color.FromRgb(255, 255, 255); // 白色
// 3. 重新生成暗色色板
SetupColorPalettes(designToken);
CalculateColorMapTokenValues(designToken);
// 4. 特殊调整:暗色模式下选中背景色使用 Border 色
designToken.ColorPrimaryBg = designToken.ColorPrimaryBorder;
designToken.ColorPrimaryBgHover = designToken.ColorPrimaryBorderHover;
}
4.3 暗色模式色板映射
暗色模式的 ColorMap 映射与亮色不同(色号顺序有调整):
protected override ColorMap GenerateColorPalettes(Color baseColor)
{
var colors = PaletteGenerator.GeneratePalette(baseColor, new PaletteGenerateOption
{
ThemeVariant = ThemeVariant.Dark // 暗色模式色板
});
return new ColorMap
{
Color1 = colors[0],
Color2 = colors[1],
Color3 = colors[2],
Color4 = colors[3],
Color5 = colors[6], // ← 注意:暗色反转
Color6 = colors[5],
Color7 = colors[4], // ← 注意:暗色反转
Color8 = colors[6],
Color9 = colors[5],
Color10 = colors[4],
};
}
4.4 暗色模式中性色计算
// 文本色 — 白色基底加透明度
designToken.ColorText = AlphaColor(colorTextBase, 0.85);
designToken.ColorTextSecondary = AlphaColor(colorTextBase, 0.65);
designToken.ColorTextTertiary = AlphaColor(colorTextBase, 0.45);
designToken.ColorTextQuaternary = AlphaColor(colorTextBase, 0.25);
// 填充色 — 白色基底加透明度(暗色模式比亮色更高)
designToken.ColorFill = AlphaColor(colorTextBase, 0.18); // vs 亮色 0.15
designToken.ColorFillSecondary = AlphaColor(colorTextBase, 0.12); // vs 亮色 0.06
designToken.ColorFillTertiary = AlphaColor(colorTextBase, 0.08); // vs 亮色 0.04
designToken.ColorFillQuaternary = AlphaColor(colorTextBase, 0.04); // vs 亮色 0.02
// 实心背景色(暗色模式独有)
designToken.ColorBgSolid = AlphaColor(colorTextBase, 1);
designToken.ColorBgSolidHover = AlphaColor(colorTextBase, 0.75);
designToken.ColorBgSolidActive = AlphaColor(colorTextBase, 0.95);
// 背景色 — 黑色基底增加亮度
designToken.ColorBgElevated = SolidColor(colorBgBase, 12); // 浮层比容器更亮
designToken.ColorBgContainer = SolidColor(colorBgBase, 8);
designToken.ColorBgLayout = SolidColor(colorBgBase, 0); // 最暗
designToken.ColorBgSpotlight = SolidColor(colorBgBase, 26);
// 边框色
designToken.ColorBorder = SolidColor(colorBgBase, 26);
designToken.ColorBorderSecondary = SolidColor(colorBgBase, 19);
5. CompactThemeVariantCalculator(紧凑算法)
5.1 职责
- 装饰前一个算法(Default 或 Dark)
- 缩小尺寸梯度和控件高度
- 不影响颜色
5.2 Calculate() 流程
public override void Calculate(DesignToken designToken)
{
// 1. 先执行链中的前一个算法
_compositeGenerator!.Calculate(designToken);
_colorBgBase = _compositeGenerator.ColorBgBase;
_colorTextBase = _compositeGenerator.ColorTextBase;
// 2. 缩小控件高度
var controlHeight = designToken.ControlHeight - 4; // 32 → 28
// 3. 缩小尺寸梯度
CalculateCompactSizeMapTokenValues(designToken);
// 4. 重新计算字体梯度(保持字体大小不变,但间距缩小)
CalculatorUtils.CalculateFontMapTokenValues(designToken);
// 5. 应用缩小的控件高度,重新计算高度梯度
designToken.ControlHeight = controlHeight;
CalculatorUtils.CalculateControlHeightMapTokenValues(designToken);
}
5.3 紧凑尺寸计算
private void CalculateCompactSizeMapTokenValues(DesignToken designToken)
{
var sizeUnit = designToken.SizeUnit; // 4
var sizeStep = designToken.SizeStep; // 4
var compactSizeStep = sizeStep - 2; // 2(关键:步长减 2)
// 对比默认值:
// Default Compact
designToken.SizeXXL = sizeUnit * (compactSizeStep + 10); // 48 → 48
designToken.SizeXL = sizeUnit * (compactSizeStep + 6); // 32 → 32
designToken.SizeLG = sizeUnit * (compactSizeStep + 2); // 24 → 16
designToken.SizeMD = sizeUnit * (compactSizeStep + 2); // 20 → 16
designToken.SizeMS = sizeUnit * (compactSizeStep + 1); // 16 → 12
designToken.Size = sizeUnit * compactSizeStep; // 16 → 8
designToken.SizeSM = sizeUnit * compactSizeStep; // 12 → 8
designToken.SizeXS = sizeUnit * (compactSizeStep - 1); // 8 → 4
designToken.SizeXXS = sizeUnit * (compactSizeStep - 1); // 4 → 4
}
6. 算法组合与主题变体
6.1 四种算法组合
AtomUI 对每个主题定义文件自动生成 4 种变体:
| 算法组合 | ThemeVariant ID | isDarkMode | 算法链 |
|---|---|---|---|
| Default | DaybreakBlue |
false | Default |
| Default + Dark | DaybreakBlue-Dark |
true | Default → Dark |
| Default + Dark + Compact | DaybreakBlue-Dark-Compact |
true | Default → Dark → Compact |
| Default + Compact | DaybreakBlue-Compact |
false | Default → Compact |
6.2 算法链构建
// ThemeManager.CreateThemeVariantCalculator()
IThemeVariantCalculator? baseCalculator = null;
IThemeVariantCalculator? calculator = null;
foreach (var algorithm in algorithms) // [Default, Dark, Compact]
{
calculator = CreateThemeVariantCalculator(algorithm, baseCalculator);
baseCalculator = calculator;
}
// 最终 calculator 是链式最后一个,调用 Calculate() 会依次执行整条链
6.3 自定义算法
通过实现 IThemeVariantCalculatorFactory 接口可替换内置算法:
public interface IThemeVariantCalculatorFactory
{
IThemeVariantCalculator Create(ThemeAlgorithm algorithm, IThemeVariantCalculator? baseAlgorithm);
}
// 使用
builder.WithThemeVariantCalculatorFactory(new CustomCalculatorFactory());
7. 调色板生成算法(PaletteGenerator)
7.1 概述
PaletteGenerator 对标 Ant Design 的 @ant-design/colors 包,实现了基于 HSV 色彩空间 的 10 级色阶生成算法。
7.2 算法参数
public const int HUE_STEP = 2; // 色相阶梯
public const float SATURATION_STEP1 = 0.16f; // 饱和度阶梯(浅色)
public const float SATURATION_STEP2 = 0.05f; // 饱和度阶梯(深色)
public const float BRIGHTNESS_STEP1 = 0.05f; // 亮度阶梯(浅色)
public const float BRIGHTNESS_STEP2 = 0.15f; // 亮度阶梯(深色)
public const int LIGHT_COLOR_COUNT = 5; // 主色上方浅色数量
public const int DARK_COLOR_COUNT = 4; // 主色下方深色数量
7.3 生成流程
输入:基础颜色 (如 #1677ff)
│
├── 转换为 HSV 色彩空间
│
├── 向浅色方向生成 5 级
│ ├── 色相:H ± HUE_STEP * i(根据色温方向决定 +/-)
│ ├── 饱和度:S - SATURATION_STEP1 * i(递减)
│ └── 亮度:V + BRIGHTNESS_STEP1 * i(递增)
│
├── 主色(第 6 级)= 输入颜色
│
├── 向深色方向生成 4 级
│ ├── 色相:H ± HUE_STEP * i(方向相反)
│ ├── 饱和度:S + SATURATION_STEP2 * i(递增)
│ └── 亮度:V - BRIGHTNESS_STEP2 * i(递减)
│
└── 输出:10 级色阶 [浅1, 浅2, 浅3, 浅4, 浅5, 主色, 深1, 深2, 深3, 深4]
7.4 色相方向规则
// 60°-240° 范围内(暖色调):浅色向左旋转,深色向右旋转
// 其他范围(冷色调):浅色向右旋转,深色向左旋转
if (Math.Round(hsvColor.H) >= 60d && Math.Round(hsvColor.H) <= 240d)
{
hue = isLight ? H - HUE_STEP * i : H + HUE_STEP * i;
}
else
{
hue = isLight ? H + HUE_STEP * i : H - HUE_STEP * i;
}
7.5 暗色模式色板
暗色模式不是简单反转,而是使用颜色混合算法:
// 暗色色板映射表(索引 + 混合透明度)
sm_darkColorMap = [
{ Index = 7, Opacity = 0.15f }, // 1号色:深色 + 15% 色板7号
{ Index = 6, Opacity = 0.25f }, // 2号色:深色 + 25% 色板6号
{ Index = 5, Opacity = 0.3f }, // 3号色
{ Index = 5, Opacity = 0.45f }, // 4号色
{ Index = 5, Opacity = 0.65f }, // 5号色
{ Index = 5, Opacity = 0.85f }, // 6号色(主色)
{ Index = 4, Opacity = 0.9f }, // 7号色
{ Index = 3, Opacity = 0.95f }, // 8号色
{ Index = 2, Opacity = 0.97f }, // 9号色
{ Index = 1, Opacity = 0.98f }, // 10号色
];
// 混合公式:RGB 线性插值
// result = bg_color + (palette_color - bg_color) * opacity
// 默认背景色 = #141414
7.6 预设颜色
AtomUI 预定义了 14 种预设颜色(与 Ant Design 一致):
| 枚举值 | 颜色 | HEX |
|---|---|---|
Red |
红色 | #F5222D |
Volcano |
火山色 | #FA541C |
Orange |
橙色 | #FA8C16 |
Gold |
金色 | #FAAD14 |
Yellow |
黄色 | #FADB14 |
Lime |
青柠 | #A0D911 |
Green |
绿色 | #52C41A |
Cyan |
青色 | #13C2C2 |
Blue |
蓝色 | #1677FF |
GeekBlue |
极客蓝 | #2F54EB |
Purple |
紫色 | #722ED1 |
Pink |
粉色 | #EB2F96 |
Magenta |
洋红 | #EB2F96 |
Grey |
灰色 | #666666 |
每种预设颜色在主题加载时会自动生成亮色和暗色两套色板(通过 SetupColorPalettes()),存储在 DesignToken.ColorPalettes 字典中。
8. CalculatorUtils 工具函数
CalculatorUtils 是独立于具体算法的静态工具类,提供非颜色类 Map Token 的计算:
8.1 字体梯度计算
// 基于自然对数曲线的字体大小序列
public static IList<FontSizeInfo> CalculateFontSize(double baseValue)
{
for (var index = 0; index < 10; ++index)
{
var i = index - 1;
var baseSize = baseValue * Math.Pow(2.71828, i / 5.0);
// 偶数对齐
fontSizes.Add((int)(Math.Floor(intSize / 2.0) * 2));
}
fontSizes[1] = baseValue; // 确保 index=1 是基础字号
}
// 行高公式
public static double CalculateLineHeight(double fontSize)
{
return (fontSize + 8) / fontSize;
}
8.2 圆角梯度计算
基于 BorderRadius (Seed) 通过查表(LUT)规则派生:
BorderRadius (Seed) XS SM LG Outer
──────────────────── ── ── ── ─────
< 2 base base base base
2 ≤ r < 5 1 base base base
5 ≤ r < 6 1 4 r+1 4
6 ≤ r < 7 2 4 r+2 4
7 ≤ r < 8 2 5 r+2 4
8 ≤ r < 14 2 6 r+2 6
14 ≤ r < 16 2 7 r+2 6
≥ 16 2 8 16 6
8.3 动效时长梯度
MotionDurationFast = MotionBase + MotionUnit * 1; // 100ms
MotionDurationMid = MotionBase + MotionUnit * 2; // 200ms
MotionDurationSlow = MotionBase + MotionUnit * 3; // 300ms
MotionDurationVerySlow = MotionBase + MotionUnit * 8; // 800ms