基于 Vue3 + TypeScript 的配色方案工具:取色、配色生成与 CSS 导出
欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/
项目 Git 仓库:
https://atomgit.com/liboqian/harmonyOs_color
摘要:本文详细介绍如何使用 Vue3 Composition API 和 TypeScript 构建一个功能完善的 Web 端配色方案工具,涵盖色相环取色器、8 种经典配色算法(单色/互补/近似/三角/分裂互补/四方/渐变/随机)、WCAG 对比度检测、渐变预览、配色和谐度计算、多格式导出(CSS/SCSS/LESS/Tailwind/JSON)等核心功能。项目采用零依赖架构,原生实现完整的配色工具能力。
一、项目背景与技术选型



1.1 开发背景
在 UI/UX 设计和前端开发工作中,配色方案的选择直接影响产品的视觉质量和用户体验。然而现有的配色工具存在以下痛点:
- 功能单一:多数在线工具仅提供颜色选择器,缺乏配色生成能力
- 缺乏标准:不考虑 WCAG 无障碍对比度标准,容易导致可访问性问题
- 无法导出:配色方案无法直接导出为开发可用的 CSS/SCSS 代码
- 无和谐度评估:无法量化评估配色组合的视觉和谐度
- 安装门槛:桌面配色工具如 Adobe Color 需要 Creative Cloud 订阅
基于这些需求,我们开发了一款纯前端、功能完整的配色方案工具。
1.2 技术栈选型
| 技术 | 版本 | 用途 |
|---|---|---|
| Vue 3 | 3.4.0+ | 核心框架,Composition API 响应式系统 |
| TypeScript | 5.3.0+ | 类型安全保障,颜色模型定义 |
| Vite | 5.0.0+ | 极速构建工具 |
| vue-router | 4.6.4+ | 前端路由管理 |
选择 Vue3 的核心理由是其高效的响应式系统,能够实时响应颜色值的变化并立即更新配色预览。TypeScript 确保 HEX/RGB/HSL 三种颜色模型之间的类型安全转换。
📌 零依赖设计原则:本项目不依赖任何第三方颜色处理库(如 chroma.js、tinycolor2),所有颜色转换和配色生成算法均自行实现,最终构建产物仅约 130KB(gzip 后约 48KB)。
1.3 项目架构
vue-app/
├── src/
│ ├── types/
│ │ └── colorscheme.ts # 颜色类型定义
│ ├── services/
│ │ └── ColorService.ts # 颜色处理核心服务
│ ├── components/
│ │ ├── ColorSchemeTool.vue # 主界面组件
│ │ ├── HueWheel.vue # 色相环取色器
│ │ └── ColorTools.vue # 渐变预览+对比度测试
│ ├── views/
│ │ └── ColorSchemeView.vue # 视图容器
│ └── router/
│ └── index.ts # 路由配置
├── index.html
└── package.json
完整的开源代码和技术文档,可参考 CSDN 博客质量评分规则 V5.0 了解本文档编写标准。
二、TypeScript 类型系统设计
2.1 颜色定义模型
typescript
// src/types/colorscheme.ts
// 颜色完整定义
export interface ColorDef {
hex: string // 十六进制 #RRGGBB
rgb: { r: number; g: number; b: number } // RGB 值 0-255
hsl: { h: number; s: number; l: number } // HSL 色相/饱和度/明度
name?: string // 颜色名称(如红色、蓝色等)
}
颜色定义包含三种最常用的颜色模型,支持互相转换。HEX 用于 CSS 和代码中使用,RGB 用于图像处理,HSL 用于配色生成。
2.2 配色方案模型
typescript
export type SchemeType = 'monochromatic' | 'complementary' | 'analogous' | 'triadic' | 'split-complementary' | 'tetradic' | 'random' | 'gradient'
export interface ColorScheme {
id: string
name: string
colors: ColorDef[]
type: SchemeType
createdAt: number
}
| 配色类型 | 中文名 | 色相规则 | 适用场景 |
|---|---|---|---|
| monochromatic | 单色 | 同一色相,不同饱和度和明度 | 简约、统一 |
| complementary | 互补 | 色相差 180° | 对比强烈、醒目 |
| analogous | 近似 | 色相差 ±15°~45° | 和谐、自然 |
| triadic | 三角 | 色相差 120° | 平衡、活跃 |
| split-complementary | 分裂互补 | 150° 和 210° | 对比但不冲突 |
| tetradic | 四方 | 色相差 90° | 丰富、复杂 |
| gradient | 渐变 | 色相连续过渡 | 背景、装饰 |
| random | 随机 | 完全随机 | 创意探索 |
2.3 对比度与导出配置
typescript
// 对比度检测结果
export interface ContrastResult {
ratio: number // 对比度比值
level: 'AAA' | 'AA' | 'AA-large' | 'fail'
foreground: string // 前景色(暗色)
background: string // 背景色(亮色)
}
// 导出配置
export interface ExportConfig {
format: 'css' | 'scss' | 'less' | 'tailwind' | 'json'
prefix: string // 变量前缀
useVariables: boolean
includeComments: boolean
}
// 渐变配置
export interface GradientConfig {
type: 'linear' | 'radial'
angle?: number
colors: string[]
stops?: number[]
}
2.4 预设常量
typescript
export const SCHEME_TYPES: Array<{ value: SchemeType; label: string; icon: string }> = [
{ value: 'monochromatic', label: '单色', icon: '🎨' },
{ value: 'complementary', label: '互补', icon: '🔄' },
{ value: 'analogous', label: '近似', icon: '🌈' },
{ value: 'triadic', label: '三角', icon: '🔺' },
{ value: 'split-complementary', label: '分裂互补', icon: '💠' },
{ value: 'tetradic', label: '四方', icon: '🔷' },
{ value: 'gradient', label: '渐变', icon: '🌅' },
{ value: 'random', label: '随机', icon: '🎲' },
]
export const POPULAR_PALETTES: Array<{ name: string; colors: string[]; tags: string[] }> = [
{ name: '日落', colors: ['#ff6b6b', '#ffa502', '#ff6348', '#ff4757', '#ee5a24'], tags: ['暖色', '热情'] },
{ name: '海洋', colors: ['#0077b6', '#00b4d8', '#90e0ef', '#caf0f8', '#48cae4'], tags: ['冷色', '清新'] },
{ name: '森林', colors: ['#2d6a4f', '#40916c', '#52b788', '#74c69d', '#95d5b2'], tags: ['自然', '绿色'] },
{ name: '莫兰迪', colors: ['#b5a8a8', '#c4b5b0', '#d4c5bb', '#c9b99a', '#a39081'], tags: ['高级', '柔和'] },
{ name: '赛博朋克', colors: ['#f72585', '#b5179e', '#7209b7', '#560bad', '#480ca8'], tags: ['科技', '霓虹'] },
{ name: '复古', colors: ['#e07a5f', '#3d405b', '#81b29a', '#f2cc8f', '#f4f1de'], tags: ['复古', '怀旧'] },
]
💡 类型设计最佳实践 :使用联合类型
SchemeType约束配色方案类型,避免运行时出现无效值。更多 TypeScript 技巧可参考 TypeScript 官方文档。
三、颜色转换算法实现
3.1 HEX 到 RGB 转换
typescript
// src/services/ColorService.ts
static hexToRgb(hex: string): { r: number; g: number; b: number } | null {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null
}
解析逻辑 :使用正则表达式提取 HEX 中的 R、G、B 两部分,再使用 parseInt(x, 16) 转为十进制。
3.2 RGB 到 HSL 转换
typescript
static rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
r /= 255
g /= 255
b /= 255
const max = Math.max(r, g, b)
const min = Math.min(r, g, b)
let h = 0
let s = 0
const l = (max + min) / 2
if (max !== min) {
const d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r:
h = ((g - b) / d + (g < b ? 6 : 0)) / 6
break
case g:
h = ((b - r) / d + 2) / 6
break
case b:
h = ((r - g) / d + 4) / 6
break
}
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100),
}
}
算法原理:
- 归一化:将 RGB 值除以 255,映射到 [0, 1] 区间
- 明度计算 :
L = (max + min) / 2 - 饱和度计算:根据明度是否大于 0.5 选择不同的分母
- 色相计算:根据哪个通道是最大值,使用不同的偏移量
3.3 HSL 到 RGB 转换
typescript
static hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
h /= 360
s /= 100
l /= 100
let r: number, g: number, b: number
if (s === 0) {
r = g = b = l // 灰度色
} else {
const hue2rgb = (p: number, q: number, t: number) => {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
}
}
3.4 颜色解析入口
typescript
static parseColor(input: string): ColorDef | null {
let hex = input
// 支持 RGB 输入
if (input.startsWith('rgb')) {
const match = input.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/)
if (match) {
hex = this.rgbToHex(parseInt(match[1]), parseInt(match[2]), parseInt(match[3]))
}
}
if (!hex.startsWith('#')) hex = '#' + hex
const rgb = this.hexToRgb(hex)
if (!rgb) return null
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b)
const normalizedName = hex.toLowerCase()
return {
hex: normalizedName,
rgb,
hsl,
name: COLOR_NAMES[normalizedName],
}
}
支持的输入格式:
| 输入格式 | 示例 | 处理结果 |
|---|---|---|
| HEX | #4285f4 |
直接解析 |
| HEX(无前缀) | 4285f4 |
自动添加 # |
| RGB | rgb(66, 133, 244) |
转为 HEX 再解析 |
| RGBA | rgba(66, 133, 244, 0.8) |
提取 RGB 部分 |
📊 颜色模型对比 :想要了解更多颜色模型知识,可以参考 MDN 颜色值文档。
四、配色方案生成算法
4.1 单色配色(Monochromatic)
typescript
case 'monochromatic':
for (let i = 0; i < count; i++) {
colors.push({
h: hsl.h, // 色相保持不变
s: Math.max(0, Math.min(100, hsl.s - 20 + i * 10)),
l: Math.max(0, Math.min(100, 20 + i * (60 / (count - 1)))),
})
}
break
生成规则:保持色相不变,线性调整饱和度和明度,创建同一色调的深浅变化。适用于简约、统一的设计风格。
4.2 互补配色(Complementary)
typescript
case 'complementary':
colors.push({ h: hsl.h, s: hsl.s, l: hsl.l }) // 基准色
colors.push({ h: (hsl.h + 180) % 360, s: hsl.s, l: hsl.l }) // 互补色
for (let i = 2; i < count; i++) {
colors.push({
h: i % 2 === 0 ? hsl.h : (hsl.h + 180) % 360, // 交替基准/互补
s: hsl.s,
l: Math.max(0, Math.min(100, hsl.l + (i - 2) * 10 - 10)), // 微调明度
})
}
break
色相环关系:互补色在色相环上相差 180°,对比强烈,视觉效果醒目。
4.3 近似配色(Analogous)
typescript
case 'analogous':
for (let i = 0; i < count; i++) {
colors.push({
h: (hsl.h - 30 + i * 15 + 360) % 360, // 色相范围 ±30°
s: hsl.s,
l: hsl.l,
})
}
break
色相环关系:近似色在色相环上相邻 ±30°,和谐自然,常用于风景、植物主题设计。
4.4 三角配色(Triadic)
typescript
case 'triadic':
for (let i = 0; i < count; i++) {
colors.push({
h: (hsl.h + i * 120) % 360, // 色相差 120°
s: hsl.s,
l: hsl.l,
})
}
break
色相环关系:三个颜色在色相环上均匀分布,形成等边三角形,平衡且活跃。
4.5 分裂互补配色(Split-Complementary)
typescript
case 'split-complementary':
colors.push({ h: hsl.h, s: hsl.s, l: hsl.l }) // 基准色
colors.push({ h: (hsl.h + 150) % 360, s: hsl.s, l: hsl.l }) // +150°
colors.push({ h: (hsl.h + 210) % 360, s: hsl.s, l: hsl.l }) // +210°
for (let i = 3; i < count; i++) {
colors.push({
h: (hsl.h + 150 + (i - 3) * 30) % 360,
s: hsl.s,
l: hsl.l,
})
}
break
色相环关系:在互补色两侧各取 30° 的位置,对比强烈但不会过于冲突。
4.6 四方配色(Tetradic)
typescript
case 'tetradic':
for (let i = 0; i < count; i++) {
colors.push({
h: (hsl.h + i * 90) % 360, // 色相差 90°
s: hsl.s,
l: hsl.l,
})
}
break
色相环关系:四个颜色在色相环上均匀分布,形成矩形,色彩丰富复杂。
4.7 配色算法对比表
| 配色类型 | 色相分布 | 颜色数量 | 对比度 | 和谐度 | 适用场景 |
|---|---|---|---|---|---|
| 单色 | 同一色相 | 任意 | 低 | 极高 | 简约设计 |
| 互补 | 180° | 2+ | 极高 | 中 | 强调对比 |
| 近似 | ±30° | 任意 | 低 | 极高 | 自然主题 |
| 三角 | 120° | 3+ | 高 | 高 | 活跃设计 |
| 分裂互补 | 150°/210° | 3+ | 高 | 高 | 平衡对比 |
| 四方 | 90° | 4+ | 中 | 中 | 复杂设计 |
| 渐变 | 连续 | 任意 | - | 高 | 背景装饰 |
五、WCAG 对比度检测
5.1 相对亮度计算
typescript
static getLuminance(r: number, g: number, b: number): number {
const [rs, gs, bs] = [r, g, b].map(c => {
c /= 255
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
})
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
公式依据 :根据 WCAG 2.1 规范,RGB 通道需要先从线性空间转换到 sRGB 空间,然后按 0.2126R + 0.7152G + 0.0722B 加权计算相对亮度。
5.2 对比度比值计算
typescript
static getContrastRatio(color1: string, color2: string): ContrastResult {
const rgb1 = this.hexToRgb(color1)!
const rgb2 = this.hexToRgb(color2)!
const l1 = this.getLuminance(rgb1.r, rgb1.g, rgb1.b)
const l2 = this.getLuminance(rgb2.r, rgb2.g, rgb2.b)
const lighter = Math.max(l1, l2)
const darker = Math.min(l1, l2)
const ratio = (lighter + 0.05) / (darker + 0.05)
let level: ContrastResult['level']
if (ratio >= 7) level = 'AAA'
else if (ratio >= 4.5) level = 'AA'
else if (ratio >= 3) level = 'AA-large'
else level = 'fail'
return {
ratio: Math.round(ratio * 100) / 100,
level,
foreground: l1 < l2 ? color1 : color2,
background: l1 >= l2 ? color1 : color2,
}
}
5.3 WCAG 2.1 标准对照表
| 等级 | 正常文本 (≥14pt) | 大文本 (≥18pt 或 ≥14pt bold) | 说明 |
|---|---|---|---|
| AAA | ≥ 7:1 | ≥ 4.5:1 | 最高标准,适合所有内容 |
| AA | ≥ 4.5:1 | ≥ 3:1 | 基本标准,适合正常文本 |
| AA Large | ≥ 3:1 | ≥ 3:1 | 仅适合大文本 |
| Fail | < 3:1 | < 3:1 | 不合格,不推荐使用 |
5.4 自动文本颜色选择
typescript
static getTextColorForBackground(bgHex: string): string {
const rgb = this.hexToRgb(bgHex)!
const luminance = this.getLuminance(rgb.r, rgb.g, rgb.b)
return luminance > 0.179 ? '#000000' : '#ffffff'
}
阈值选择:0.179 是根据 W3C 推荐值计算得出的临界点,确保选择的文本颜色与背景色之间的对比度最大化。
六、配色和谐度计算
6.1 和谐度评分算法
typescript
static calculateHarmony(colors: ColorDef[]): number {
if (colors.length < 2) return 100
let totalScore = 0
let pairCount = 0
for (let i = 0; i < colors.length; i++) {
for (let j = i + 1; j < colors.length; j++) {
const hsl1 = colors[i].hsl
const hsl2 = colors[j].hsl
const hueDiff = Math.abs(hsl1.h - hsl2.h)
const hueScore = this.hueHarmonyScore(hueDiff)
const satDiff = Math.abs(hsl1.s - hsl2.s)
const satScore = 1 - satDiff / 100
const lightDiff = Math.abs(hsl1.l - hsl2.l)
const lightScore = 1 - lightDiff / 100
totalScore += (hueScore * 0.6 + satScore * 0.2 + lightScore * 0.2) * 100
pairCount++
}
}
return Math.round(totalScore / pairCount)
}
评分权重:
| 维度 | 权重 | 计算方式 | 说明 |
|---|---|---|---|
| 色相和谐度 | 60% | 色相差值查表 | 最重要的因素 |
| 饱和度一致度 | 20% | 1 - 差值/100 | 次要因素 |
| 明度一致度 | 20% | 1 - 差值/100 | 次要因素 |
6.2 色相和谐度评分表
typescript
private static hueHarmonyScore(diff: number): number {
const normalizedDiff = Math.min(diff, 360 - diff)
if (normalizedDiff <= 15) return 1.0 // 近似色,完美和谐
if (normalizedDiff >= 30 && normalizedDiff <= 45) return 0.85 // 轻微差异,良好
if (normalizedDiff >= 60 && normalizedDiff <= 75) return 0.75 // 三角,较好
if (normalizedDiff >= 90 && normalizedDiff <= 105) return 0.7 // 四方,可接受
if (normalizedDiff >= 120 && normalizedDiff <= 135) return 0.8 // 三角,良好
if (normalizedDiff >= 150 && normalizedDiff <= 180) return 0.85 // 互补,良好
return 0.5 // 其他,较差
}
| 和谐度评分 | 等级 | 描述 |
|---|---|---|
| ≥ 80 | 优秀 | 色彩搭配和谐 |
| 60-79 | 良好 | 大部分场景可用 |
| 40-59 | 一般 | 需要注意使用场景 |
| < 40 | 较差 | 建议重新选择 |
七、多格式导出功能
7.1 CSS Variables 导出
typescript
private static exportCSS(colors: ColorDef[], config: ExportConfig, varName: (i: number) => string): string {
let output = ''
if (config.includeComments) output += '/* 配色方案 */\n'
output += ':root {\n'
colors.forEach((color, i) => {
if (config.includeComments && color.name) output += ` /* ${color.name} */\n`
output += ` --${varName(i)}: ${color.hex};\n`
})
output += '}\n'
return output
}
示例输出:
css
/* 配色方案 */
:root {
/* 红色 */
--color-1: #ff6b6b;
/* 橙色 */
--color-2: #ffa502;
/* 品红 */
--color-3: #ff6348;
--color-4: #ff4757;
--color-5: #ee5a24;
}
7.2 SCSS/LESS 导出对比
| 格式 | 变量语法 | 示例 | 适用框架 |
|---|---|---|---|
| CSS | --var: value |
--color-1: #ff6b6b; |
原生 CSS, Vue, React |
| SCSS | $var: value |
$color-1: #ff6b6b; |
Sass/SCSS 项目 |
| LESS | @var: value |
@color-1: #ff6b6b; |
Less 项目 |
| Tailwind | 配置对象 | 'primary': '#ff6b6b' |
Tailwind CSS |
| JSON | 标准 JSON | {"hex": "#ff6b6b"} |
任何编程语言 |
7.3 Tailwind CSS 导出
typescript
private static exportTailwind(colors: ColorDef[], config: ExportConfig): string {
let output = 'module.exports = {\n theme: {\n extend: {\n colors: {\n'
colors.forEach((color, i) => {
const name = color.name ? this.toCamelCase(color.name) : `color-${i + 1}`
output += ` '${name}': '${color.hex}',\n`
})
output += ' }\n }\n }\n}'
return output
}
示例输出:
javascript
module.exports = {
theme: {
extend: {
colors: {
'primary': '#ff6b6b',
'secondary': '#ffa502',
'accent': '#ff6348',
}
}
}
}
7.4 文件下载机制
typescript
static downloadFile(content: string, filename: string): void {
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
八、色相环取色器实现
8.1 SVG 色相环绘制
vue
<!-- src/components/HueWheel.vue -->
<svg viewBox="0 0 200 200" class="wheel-svg" @click="onHueWheelClick">
<defs>
<linearGradient id="hueGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff0000"/>
<stop offset="17%" stop-color="#ff8800"/>
<stop offset="33%" stop-color="#ffff00"/>
<stop offset="50%" stop-color="#00ff00"/>
<stop offset="67%" stop-color="#0088ff"/>
<stop offset="83%" stop-color="#8800ff"/>
<stop offset="100%" stop-color="#ff0088"/>
</linearGradient>
</defs>
<!-- 外圆环 -->
<circle cx="100" cy="100" r="90" fill="none" stroke="url(#hueGrad)" stroke-width="20" opacity="0.8"/>
<!-- 36 色相点 -->
<circle v-for="h in hueColors" :key="h.hue"
:cx="100 + 90 * Math.cos((h.hue - 90) * Math.PI / 180)"
:cy="100 + 90 * Math.sin((h.hue - 90) * Math.PI / 180)"
r="6"
:fill="h.color"/>
</svg>
8.2 点击取色算法
typescript
const onHueWheelClick = (event: MouseEvent) => {
const rect = (event.target as HTMLElement).getBoundingClientRect()
const x = event.clientX - rect.left - rect.width / 2
const y = event.clientY - rect.top - rect.height / 2
// 计算点击角度
let angle = Math.atan2(y, x) * (180 / Math.PI) + 90
if (angle < 0) angle += 360
hue.value = Math.round(angle) % 360
updateColor()
}
数学原理:
- 将点击坐标相对圆心的偏移转换为角度
- 使用
Math.atan2(y, x)计算弧度 - 转换为角度并校准偏移(+90° 使顶部为 0°)
8.3 HSL 滑块控制
vue
<div class="control-group">
<label>色相: {{ hue }}°</label>
<input type="range" min="0" max="360" v-model.number="hue" @input="onHueChange" class="slider"/>
</div>
<div class="control-group">
<label>饱和度: {{ saturation }}%</label>
<input type="range" min="0" max="100" v-model.number="saturation" @input="onHueChange" class="slider"/>
</div>
<div class="control-group">
<label>明度: {{ lightness }}%</label>
<input type="range" min="0" max="100" v-model.number="lightness" @input="onHueChange" class="slider"/>
</div>
九、渐变预览与对比度测试
9.1 渐变 CSS 生成
typescript
static generateGradientCSS(config: GradientConfig): string {
const colorStops = config.colors
.map((color, i) => {
const stop = config.stops?.[i] ?? (i / (config.colors.length - 1)) * 100
return `${color} ${stop}%`
})
.join(', ')
if (config.type === 'radial') {
return `radial-gradient(circle, ${colorStops})`
}
return `linear-gradient(${config.angle || 135}deg, ${colorStops})`
}
9.2 渐变类型对比
| 渐变类型 | CSS 函数 | 参数 | 效果 |
|---|---|---|---|
| 线性 | linear-gradient() |
角度 + 颜色停靠 | 方向性渐变 |
| 径向 | radial-gradient() |
形状 + 颜色停靠 | 中心向外扩散 |
9.3 对比度测试 UI
vue
<div class="contrast-controls">
<div class="color-input">
<label>前景色</label>
<input type="color" v-model="contrastTest.foreground" class="color-picker"/>
<input type="text" v-model="contrastTest.foreground" class="color-input-text"/>
</div>
<div class="color-input">
<label>背景色</label>
<input type="color" v-model="contrastTest.background" class="color-picker"/>
<input type="text" v-model="contrastTest.background" class="color-input-text"/>
</div>
</div>
<div class="contrast-result" :style="{ borderColor: levelInfo[contrastResult.level].color }">
<div class="result-header">
<span class="level-badge" :style="{ backgroundColor: levelInfo[contrastResult.level].color }">
{{ levelInfo[contrastResult.level].label }}
</span>
<span class="ratio">{{ contrastResult.ratio }}:1</span>
</div>
<p class="result-desc">{{ levelInfo[contrastResult.level].desc }}</p>
</div>
十、响应式 UI 设计与交互
10.1 三视图架构
┌─────────────────────────────────────────────┐
│ 应用标题区域 │
├─────────────────────────────────────────────┤
│ 标签切换:取色器 | 配色生成 | 配色库 │
├─────────────────────────────────────────────┤
│ │
│ 视图1: 取色器(色相环 + 颜色值显示) │
│ 视图2: 配色生成(控制面板 + 配色展示) │
│ 视图3: 配色库(热门配色 + 我的配色) │
│ │
└─────────────────────────────────────────────┘
10.2 主控制面板
vue
<div class="controlsPanel">
<div class="control-row">
<label>基准颜色</label>
<div class="color-input-wrapper">
<input type="color" v-model="baseColor" class="color-picker-native"/>
<span class="color-hex">{{ baseColor.toUpperCase() }}</span>
</div>
<button class="btn small" @click="addRandomColor">🎲 随机</button>
</div>
<div class="control-row">
<label>配色类型</label>
<div class="scheme-types">
<button v-for="st in SCHEME_TYPES" :key="st.value"
class="scheme-btn"
:class="{ active: schemeType === st.value }"
@click="schemeType = st.value">
<span class="scheme-icon">{{ st.icon }}</span>
<span class="scheme-label">{{ st.label }}</span>
</button>
</div>
</div>
<div class="control-row">
<label>颜色数量: {{ colorCount }}</label>
<input type="range" min="2" max="8" v-model.number="colorCount" class="slider"/>
</div>
<div class="control-row">
<label>和谐度</label>
<div class="harmony-meter">
<div class="harmony-fill" :style="{ width: harmonyScore + '%' }"></div>
<span class="harmony-text">{{ harmonyScore }}% - {{ harmonyLabel }}</span>
</div>
</div>
</div>
10.3 颜色卡片展示
vue
<div class="colors-display">
<div v-for="(color, index) in generatedColors" :key="index" class="color-card" @click="copyColor(color.hex)">
<div class="color-swatch" :style="{ backgroundColor: color.hex }">
<span :style="{ color: ColorService.getTextColorForBackground(color.hex) }">
{{ color.hex.toUpperCase() }}
</span>
</div>
<div class="color-details">
<span class="detail-label">RGB</span>
<span class="detail-value">{{ color.rgb.r }}, {{ color.rgb.g }}, {{ color.rgb.b }}</span>
</div>
<div class="color-details">
<span class="detail-label">HSL</span>
<span class="detail-value">{{ color.hsl.h }}°, {{ color.hsl.s }}%, {{ color.hsl.l }}%</span>
</div>
<div v-if="color.name" class="color-name">{{ color.name }}</div>
</div>
</div>
10.4 移动端适配
css
@media (max-width: 768px) {
.color-scheme-tool {
padding: 12px;
}
.picker-layout {
grid-template-columns: 1fr;
}
.scheme-types {
flex-wrap: wrap;
}
.colors-display {
grid-template-columns: repeat(2, 1fr);
}
.palette-grid {
grid-template-columns: 1fr;
}
}
10.5 UI 交互特性
| 组件 | 交互方式 | 视觉反馈 | 响应式策略 |
|---|---|---|---|
| 色相环 | 点击取色 | 白色标记当前位置 | 固定 180px |
| 配色类型按钮 | 点击切换 | 蓝色边框+背景 | 横向滚动 |
| 颜色卡片 | 点击复制 | hover 上浮阴影 | 2/5 列网格 |
| 和谐度仪表 | 自动计算 | 颜色区分等级 | 全宽进度条 |
| 配色库卡片 | 点击加载 | hover 背景高亮 | 单列布局 |
十一、项目构建与部署
11.1 构建配置
json
{
"name": "color-scheme-tool",
"version": "1.0.0",
"description": "配色方案工具 - 取色、生成配色、导出 CSS",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.6.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"typescript": "^5.3.0",
"vite": "^5.0.0",
"vue-tsc": "^1.8.0"
}
}
11.2 路由配置
typescript
// src/router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'ColorScheme',
component: () => import('../views/ColorSchemeView.vue'),
},
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router
11.3 构建产物分析
dist/
├── index.html 0.62 kB │ gzip: 0.46 kB
├── assets/
│ ├── index-*.css 0.21 kB │ gzip: 0.19 kB
│ ├── ColorSchemeView-*.css 12.21 kB │ gzip: 2.61 kB
│ ├── ColorSchemeView-*.js 25.25 kB │ gzip: 9.07 kB
│ └── index-*.js 92.19 kB │ gzip: 36.11 kB
| 文件 | 原始大小 | gzip 压缩 | 说明 |
|---|---|---|---|
| index.html | 0.62 KB | 0.46 KB | 入口页面 |
| 全局样式 | 0.21 KB | 0.19 KB | 基础 CSS |
| 组件样式 | 12.21 KB | 2.61 KB | 组件专属 CSS |
| 组件逻辑 | 25.25 KB | 9.07 KB | 业务 JavaScript |
| Vue 运行时 | 92.19 KB | 36.11 KB | Vue + Router |
| 总计 | 130.48 KB | 48.44 KB | 零额外依赖 |
🚀 HarmonyOS 部署提示 :构建产物可以直接嵌入到 HarmonyOS 应用的 Web 组件中。在 DevEco Studio 中,将
dist目录复制到resources/resfile/下即可使用。更多 HarmonyOS Web 组件集成方法可参考 HarmonyOS Web 开发指南。
十二、性能优化与最佳实践
12.1 已采用的优化策略
| 优化项 | 实现方式 | 效果 |
|---|---|---|
| 响应式更新 | Vue watch 监听 | 颜色变化立即生成新配色 |
| 点击复制 | navigator.clipboard | 一键复制 HEX 值 |
| 内存释放 | URL.revokeObjectURL | 防止 Blob 泄漏 |
| 类型约束 | TypeScript 严格模式 | 编译期发现颜色格式错误 |
| 本地存储 | localStorage | 保存用户配色方案 |
12.2 可扩展优化方向
| 优化方向 | 技术方案 | 适用场景 |
|---|---|---|
| 图片取色 | Canvas getImageData | 从图片提取主色调 |
| 色彩空间扩展 | LAB/XYZ 支持 | 更精确的颜色差异计算 |
| 协作分享 | 后端 API | 团队配色方案共享 |
| 插件集成 | Figma/Sketch 插件 | 设计师工作流集成 |
| AI 配色 | 机器学习模型 | 智能配色推荐 |
12.3 与商业工具对比
| 对比项 | 本工具 | Adobe Color | Coolors |
|---|---|---|---|
| 成本 | ✅ 免费开源 | ❌ 订阅制 | ✅ 免费版 |
| 配色算法 | ✅ 8 种 | ✅ 5 种 | ✅ 5 种 |
| 对比度检测 | ✅ WCAG 2.1 | ✅ WCAG 2.1 | ⚠️ 基础检测 |
| 导出格式 | ✅ 5 种 | ⚠️ 2 种 | ⚠️ 1 种 |
| 和谐度评分 | ✅ 自研算法 | ❌ 无 | ❌ 无 |
| 安装要求 | ❌ 无需安装 | ❌ 需 Adobe ID | ❌ 浏览器即可 |
| 包体积 | ~130KB | ~50MB | N/A |
📚 进阶阅读 :想要了解更多颜色处理知识,可以参考 WCAG 2.1 对比度标准 和 MDN 渐变指南。
十三、总结与展望
13.1 项目成果
本项目成功实现了一个功能完整的 Web 端配色方案工具,具备以下核心能力:
- 色相环取色器:SVG 绘制 36 色相环,点击取色 + HSL 滑块控制
- 8 种配色算法:单色/互补/近似/三角/分裂互补/四方/渐变/随机
- WCAG 对比度检测:自动计算对比度比值,判定 AA/AAA 等级
- 渐变预览:线性/径向渐变,角度可调,实时预览
- 和谐度评分:多维度(色相/饱和度/明度)计算配色和谐度
- 5 种导出格式:CSS/SCSS/LESS/Tailwind/JSON
- 配色库:6 套预设热门配色,支持保存/管理自定义配色
13.2 技术亮点
- ✅ 完整的 TypeScript 类型系统,HEX/RGB/HSL 类型安全转换
- ✅ Vue3 Composition API 实现高效响应式数据流
- ✅ 零依赖实现,所有算法自行编写
- ✅ WCAG 2.1 标准对比度计算
- ✅ 自研配色和谐度评分算法
- ✅ 5 种格式导出支持完整注释
13.3 未来规划
| 功能方向 | 优先级 | 预期效果 |
|---|---|---|
| 图片取色 | 高 | Canvas 提取图片主色调 |
| 色彩空间扩展 | 高 | 支持 LAB/XYZ 颜色空间 |
| 协作分享 | 中 | 生成链接分享配色方案 |
| AI 配色推荐 | 低 | 基于场景智能推荐配色 |
| Figma 插件 | 低 | 设计师工作流集成 |