UI 色彩系统的数学之美:从色轮理论到算法生成色板的底层原理

UI 色彩系统的数学之美:从色轮理论到算法生成色板的底层原理 UI 色彩系统的数学之美从色轮理论到算法生成色板的底层原理一、引子为什么好看的配色可以写成算法设计师花四年学的色彩理论本质上是一组数学公式在 HSL 色彩空间上的操作。互补色 色相 180°三等分配色 色相 120°/240°类比色 色相 ± 30°。理解这些数学关系后程序化生成色板就从一个美学问题变成了一个参数搜索问题。二、色彩空间的数学表示OKLCH 的优势人眼对黄色的亮度感知强于蓝色。HSL 的 L50%亮度对于黄色感觉很亮对于蓝色感觉很暗。OKLCH 修正了这一点——同一 L 值的不同色相在人眼中感知亮度一致。这对无障碍设计保证对比度至关重要。三、算法生成色板的数学公式/** * 基于 OKLCH 的色板生成算法 */ interface ColorPalette { name: string; primary: string; // 主色 variants: string[]; // 色阶 (50, 100, ..., 900) complements: string[]; // 互补色 } /** * 从单一主色生成完整色板 * * param baseColor OKLCH 格式 { l, c, h } * param mode monochromatic | analogous | complementary | triadic */ function generatePalette( baseColor: OKLCH, mode: ColorMode ): ColorPalette { // 1. 生成主色色阶亮度变化 const variants generateShades(baseColor); // 2. 根据配色模式生成辅助色 const complements generateComplements(baseColor, mode); // 3. 确保所有颜色满足 WCAG AA 对比度 const validated validateContrast([...variants, ...complements]); return { name: Palette-${baseColor.h}, primary: toHex(validated[5]), // 500 作为主色 variants: validated.slice(0, 10).map(toHex), complements: validated.slice(10).map(toHex), }; } /** * 生成色阶从浅到深 10 阶 * * 算法逻辑 * - L 值从 0.98极浅到 0.15极深均匀分布 * - c 值在中间色阶最高两端递减极浅和极深容不下高色度 * - h 值保持恒定 */ function generateShades(base: OKLCH): OKLCH[] { const shades: OKLCH[] []; const numShades 10; for (let i 0; i numShades; i) { const t i / (numShades - 1); // 0 → 1 // L: 从 0.98 到 0.15 线性分布 const l 0.98 - t * 0.83; // c: 中间色阶色度最高抛物线分布 const c base.c * (1 - 4 * Math.pow(t - 0.5, 2)); // h: 保持恒定 const h base.h; shades.push({ l, c, h }); } return shades; } /** * 生成互补色/辅助色 */ function generateComplements(base: OKLCH, mode: ColorMode): OKLCH[] { switch (mode) { case complementary: // 对立色相 180° return [{ l: base.l, c: base.c * 0.8, h: (base.h 180) % 360 }]; case triadic: // 三等分 120°, 240° return [ { l: base.l, c: base.c * 0.7, h: (base.h 120) % 360 }, { l: base.l, c: base.c * 0.7, h: (base.h 240) % 360 }, ]; case analogous: // 相似色± 30° return [ { l: base.l * 0.95, c: base.c * 0.9, h: (base.h 30) % 360 }, { l: base.l * 0.95, c: base.c * 0.9, h: (base.h 330) % 360 }, ]; case split-complementary: // 分裂互补 150°, 210° return [ { l: base.l, c: base.c * 0.7, h: (base.h 150) % 360 }, { l: base.l, c: base.c * 0.7, h: (base.h 210) % 360 }, ]; default: return []; } } /** * 验证对比度 * WCAG AA: 正常文本 4.5:1, 大文本 3:1 */ function validateContrast(colors: OKLCH[]): OKLCH[] { return colors.map((color) { // 在白色背景 (#FFFFFF) 上检查对比度 const contrastOnWhite calculateContrast(color, WHITE); // 如果对比度不足降低 L 值增大对比 if (contrastOnWhite 4.5) { const adjustment Math.pow(4.5 / contrastOnWhite, 0.5); return { ...color, l: color.l * (1 / adjustment) }; } return color; }); } /** * 计算两个 OKLCH 颜色之间的 WCAG 对比度 */ function calculateContrast(color: OKLCH, bg: OKLCH): number { // 将 OKLCH 转为相对亮度Y 分量 const lum1 oklchToRelativeLuminance(color); const lum2 oklchToRelativeLuminance(bg); const lighter Math.max(lum1, lum2); const darker Math.min(lum1, lum2); return (lighter 0.05) / (darker 0.05); } // 类型定义 interface OKLCH { l: number; c: number; h: number; } type ColorMode monochromatic | analogous | complementary | triadic | split-complementary; const WHITE: OKLCH { l: 1.0, c: 0, h: 0 }; function toHex(color: OKLCH): string { /* OKLCH → HEX 转换 */ return #000000; } function oklchToRelativeLuminance(c: OKLCH): number { return 0.5; }四、从数学到美学人的判断不可替代算法生成的色板在数学上是和谐的——色相分布均匀、对比度达标。但数学上的和谐不保证审美上的愉悦。两个色相差 180° 的颜色在理论上互补但特定饱和度和亮度的组合可能刺眼或沉闷。算法的角色是生成候选色板人类设计师不可能手动尝试 360° × 100% × 100% 空间内的所有组合设计师的角色是从候选方案中选择最符合品牌调性的那一组。推荐工作流算法生成 20 组候选色板设计师筛选 3 组在真实 UI 场景中做压力测试最终确定 1 组结论色彩理论本质是 HSL/OKLCH 空间上的数学操作互补色 色相 180°三等分 120°/240°类比色 ± 30°OKLCH 优于 HSL因为感知亮度均匀更适合无障碍设计色阶生成的核心是 L 线性分布 c 抛物线分布算法生成候选色板设计师筛选最优——人机协作模式WCAG 对比度验证必须嵌入算法中自动修正不达标的颜色