KMP OpenHarmony 颜色转换工具 - RGB/HEX/HSL颜色格式转换
本文介绍了一个基于Kotlin Multiplatform (KMP)的颜色转换工具系统,支持RGB、HEX、HSL、HSV、CMYK等多种颜色格式的精确转换。该工具提供双向转换、颜色分析(亮度、对比度等)和颜色搭配建议功能,适用于UI设计、图形处理等场景。核心实现包括RGB与HEX/HSL的转换算法,通过KMP框架实现跨平台兼容,确保颜色处理的一致性和准确性。工具特点包括多格式支持、标准化算法和
目录
概述
本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个功能完整的颜色转换工具系统。颜色转换是现代应用开发中的常见需求,广泛应用于UI设计、图形处理、网页开发、数据可视化等领域。这个工具提供了对多种颜色格式的转换支持,包括RGB(红绿蓝)、HEX(十六进制)、HSL(色调饱和度亮度)、HSV(色调饱和度值)、CMYK(青洋红黄黑)等格式。
在实际应用中,颜色转换工具广泛应用于以下场景:UI设计工具、图形编辑器、网页设计应用、数据可视化平台、色彩搭配工具、无障碍设计工具等。通过 KMP 框架的跨端能力,我们可以在不同平台上使用相同的颜色转换逻辑,确保颜色表示的准确性和一致性。
工具的特点
- 多格式支持:支持RGB、HEX、HSL、HSV、CMYK等多种颜色格式
- 精确转换:使用标准的色彩空间转换算法
- 双向转换:支持任意两种格式之间的相互转换
- 颜色分析:提供颜色的亮度、饱和度、对比度等分析
- 颜色搭配:生成互补色、类似色等颜色搭配建议
- 跨端兼容:一份 Kotlin 代码可同时服务多个平台
工具功能
1. RGB 颜色格式
RGB 是最常见的颜色格式,由红、绿、蓝三个通道组成,每个通道的值范围是 0-255。RGB 格式在计算机显示、网页设计和图形处理中广泛使用。RGB 转换需要考虑通道值的有效范围和精度。
- RGB 值范围:每个通道 0-255
- RGB 字符串表示:rgb(r, g, b) 或 rgba(r, g, b, a)
- RGB 对象表示:包含 r、g、b、a 四个属性
- RGB 验证:检查通道值是否在有效范围内
2. HEX 颜色格式
HEX 是十六进制颜色表示法,使用 # 符号后跟六位十六进制数字。HEX 格式在网页设计和 CSS 中广泛使用。HEX 转换需要处理十六进制和十进制之间的转换。
- HEX 格式:#RRGGBB 或 #RRGGBBAA
- 短格式:#RGB(每个通道一位)
- 大小写处理:支持大小写转换
- 透明度支持:支持 Alpha 通道
3. HSL 颜色格式
HSL 是基于色调、饱和度、亮度的颜色表示法。HSL 格式对于颜色调整和搭配更加直观。HSL 转换涉及到复杂的数学计算。
- 色调(Hue):0-360 度,表示颜色
- 饱和度(Saturation):0-100%,表示颜色的纯度
- 亮度(Lightness):0-100%,表示颜色的明暗
- HSL 字符串表示:hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
4. HSV 颜色格式
HSV 是基于色调、饱和度、值的颜色表示法。HSV 与 HSL 相似,但亮度的计算方式不同。HSV 在图像处理和颜色选择器中常见。
- 色调(Hue):0-360 度
- 饱和度(Saturation):0-100%
- 值(Value):0-100%,表示颜色的亮度
- HSV 应用:图像处理、颜色选择器
5. CMYK 颜色格式
CMYK 是用于印刷的颜色格式,由青、洋红、黄、黑四个通道组成。CMYK 转换用于将数字颜色转换为印刷颜色。
- CMYK 值范围:每个通道 0-100%
- 印刷应用:用于印刷和出版
- RGB 到 CMYK:需要反演和黑色提取
- CMYK 到 RGB:需要黑色还原和反演
6. 颜色分析
颜色分析提供颜色的各种属性和特征,包括亮度、饱和度、对比度等。颜色分析对于无障碍设计和颜色搭配很重要。
- 亮度计算:计算颜色的相对亮度
- 对比度计算:计算两种颜色的对比度
- 颜色距离:计算两种颜色的距离
- 无障碍检查:检查颜色对比度是否符合 WCAG 标准
7. 颜色搭配建议
根据给定的颜色,生成互补色、类似色、三角配色等搭配建议。颜色搭配建议对于 UI 设计和品牌设计很有用。
- 互补色:色轮上相对的颜色
- 类似色:色轮上相邻的颜色
- 三角配色:色轮上均匀分布的三种颜色
- 分裂互补色:互补色两侧的颜色
核心实现
1. RGB 到 HEX 转换
fun rgbToHex(r: Int, g: Int, b: Int, a: Int = 255): String {
val r = r.coerceIn(0, 255)
val g = g.coerceIn(0, 255)
val b = b.coerceIn(0, 255)
val a = a.coerceIn(0, 255)
return if (a == 255) {
String.format("#%02X%02X%02X", r, g, b)
} else {
String.format("#%02X%02X%02X%02X", r, g, b, a)
}
}
fun hexToRgb(hex: String): Map<String, Int> {
val cleanHex = hex.removePrefix("#")
return when (cleanHex.length) {
6 -> {
mapOf(
"r" to cleanHex.substring(0, 2).toInt(16),
"g" to cleanHex.substring(2, 4).toInt(16),
"b" to cleanHex.substring(4, 6).toInt(16),
"a" to 255
)
}
8 -> {
mapOf(
"r" to cleanHex.substring(0, 2).toInt(16),
"g" to cleanHex.substring(2, 4).toInt(16),
"b" to cleanHex.substring(4, 6).toInt(16),
"a" to cleanHex.substring(6, 8).toInt(16)
)
}
else -> mapOf("r" to 0, "g" to 0, "b" to 0, "a" to 255)
}
}
代码说明: RGB 到 HEX 的转换使用十六进制格式化。HEX 到 RGB 的转换需要解析十六进制字符串。通过处理不同长度的 HEX 字符串,可以支持带或不带透明度的颜色。
2. RGB 到 HSL 转换
fun rgbToHsl(r: Int, g: Int, b: Int): Map<String, Double> {
val r = r / 255.0
val g = g / 255.0
val b = b / 255.0
val max = maxOf(r, g, b)
val min = minOf(r, g, b)
val l = (max + min) / 2.0
if (max == min) {
return mapOf("h" to 0.0, "s" to 0.0, "l" to l * 100)
}
val d = max - min
val s = if (l > 0.5) d / (2.0 - max - min) else d / (max + min)
val h = when (max) {
r -> (60 * (((g - b) / d) % 6) + 360) % 360
g -> (60 * (((b - r) / d) + 2) + 360) % 360
b -> (60 * (((r - g) / d) + 4) + 360) % 360
else -> 0.0
}
return mapOf("h" to h, "s" to s * 100, "l" to l * 100)
}
fun hslToRgb(h: Double, s: Double, l: Double): Map<String, Int> {
val h = h % 360
val s = s / 100.0
val l = l / 100.0
val c = (1 - kotlin.math.abs(2 * l - 1)) * s
val x = c * (1 - kotlin.math.abs((h / 60) % 2 - 1))
val m = l - c / 2
val (r1, g1, b1) = when {
h < 60 -> Triple(c, x, 0.0)
h < 120 -> Triple(x, c, 0.0)
h < 180 -> Triple(0.0, c, x)
h < 240 -> Triple(0.0, x, c)
h < 300 -> Triple(x, 0.0, c)
else -> Triple(c, 0.0, x)
}
return mapOf(
"r" to ((r1 + m) * 255).toInt(),
"g" to ((g1 + m) * 255).toInt(),
"b" to ((b1 + m) * 255).toInt()
)
}
代码说明: RGB 到 HSL 的转换使用标准的色彩空间转换算法。HSL 到 RGB 的转换需要反向计算。这些转换涉及到复杂的数学计算,需要特别注意精度。
3. 颜色亮度计算
fun calculateLuminance(r: Int, g: Int, b: Int): Double {
val r = r / 255.0
val g = g / 255.0
val b = b / 255.0
val rs = if (r <= 0.03928) r / 12.92 else kotlin.math.pow((r + 0.055) / 1.055, 2.4)
val gs = if (g <= 0.03928) g / 12.92 else kotlin.math.pow((g + 0.055) / 1.055, 2.4)
val bs = if (b <= 0.03928) b / 12.92 else kotlin.math.pow((b + 0.055) / 1.055, 2.4)
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
fun calculateContrast(color1: Map<String, Int>, color2: Map<String, Int>): Double {
val l1 = calculateLuminance(color1["r"] ?: 0, color1["g"] ?: 0, color1["b"] ?: 0)
val l2 = calculateLuminance(color2["r"] ?: 0, color2["g"] ?: 0, color2["b"] ?: 0)
val lighter = maxOf(l1, l2)
val darker = minOf(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
代码说明: 颜色亮度计算使用 WCAG 标准的相对亮度公式。对比度计算用于检查颜色组合是否符合无障碍标准。
4. 颜色搭配建议
fun getComplementaryColor(h: Double): Double {
return (h + 180) % 360
}
fun getAnalogousColors(h: Double): List<Double> {
return listOf(
(h - 30 + 360) % 360,
h,
(h + 30) % 360
)
}
fun getTriadicColors(h: Double): List<Double> {
return listOf(
h,
(h + 120) % 360,
(h + 240) % 360
)
}
fun getSplitComplementaryColors(h: Double): List<Double> {
val comp = (h + 180) % 360
return listOf(
h,
(comp - 30 + 360) % 360,
(comp + 30) % 360
)
}
代码说明: 颜色搭配建议基于色轮的几何关系。互补色相差 180 度,类似色相差 30 度,三角配色相差 120 度。
Kotlin 源代码
// ColorConverter.kt
class ColorConverter {
fun rgbToHex(r: Int, g: Int, b: Int, a: Int = 255): String {
val r = r.coerceIn(0, 255)
val g = g.coerceIn(0, 255)
val b = b.coerceIn(0, 255)
val a = a.coerceIn(0, 255)
return if (a == 255) {
String.format("#%02X%02X%02X", r, g, b)
} else {
String.format("#%02X%02X%02X%02X", r, g, b, a)
}
}
fun hexToRgb(hex: String): Map<String, Int> {
val cleanHex = hex.removePrefix("#")
return when (cleanHex.length) {
6 -> {
mapOf(
"r" to cleanHex.substring(0, 2).toInt(16),
"g" to cleanHex.substring(2, 4).toInt(16),
"b" to cleanHex.substring(4, 6).toInt(16),
"a" to 255
)
}
8 -> {
mapOf(
"r" to cleanHex.substring(0, 2).toInt(16),
"g" to cleanHex.substring(2, 4).toInt(16),
"b" to cleanHex.substring(4, 6).toInt(16),
"a" to cleanHex.substring(6, 8).toInt(16)
)
}
else -> mapOf("r" to 0, "g" to 0, "b" to 0, "a" to 255)
}
}
fun rgbToHsl(r: Int, g: Int, b: Int): Map<String, Double> {
val r = r / 255.0
val g = g / 255.0
val b = b / 255.0
val max = maxOf(r, g, b)
val min = minOf(r, g, b)
val l = (max + min) / 2.0
if (max == min) {
return mapOf("h" to 0.0, "s" to 0.0, "l" to l * 100)
}
val d = max - min
val s = if (l > 0.5) d / (2.0 - max - min) else d / (max + min)
val h = when (max) {
r -> (60 * (((g - b) / d) % 6) + 360) % 360
g -> (60 * (((b - r) / d) + 2) + 360) % 360
b -> (60 * (((r - g) / d) + 4) + 360) % 360
else -> 0.0
}
return mapOf("h" to h, "s" to s * 100, "l" to l * 100)
}
fun hslToRgb(h: Double, s: Double, l: Double): Map<String, Int> {
val h = h % 360
val s = s / 100.0
val l = l / 100.0
val c = (1 - kotlin.math.abs(2 * l - 1)) * s
val x = c * (1 - kotlin.math.abs((h / 60) % 2 - 1))
val m = l - c / 2
val (r1, g1, b1) = when {
h < 60 -> Triple(c, x, 0.0)
h < 120 -> Triple(x, c, 0.0)
h < 180 -> Triple(0.0, c, x)
h < 240 -> Triple(0.0, x, c)
h < 300 -> Triple(x, 0.0, c)
else -> Triple(c, 0.0, x)
}
return mapOf(
"r" to ((r1 + m) * 255).toInt(),
"g" to ((g1 + m) * 255).toInt(),
"b" to ((b1 + m) * 255).toInt()
)
}
fun rgbToHsv(r: Int, g: Int, b: Int): Map<String, Double> {
val r = r / 255.0
val g = g / 255.0
val b = b / 255.0
val max = maxOf(r, g, b)
val min = minOf(r, g, b)
val d = max - min
val h = when {
d == 0.0 -> 0.0
max == r -> (60 * (((g - b) / d) % 6) + 360) % 360
max == g -> (60 * (((b - r) / d) + 2)) % 360
else -> (60 * (((r - g) / d) + 4)) % 360
}
val s = if (max == 0.0) 0.0 else d / max
val v = max
return mapOf("h" to h, "s" to s * 100, "v" to v * 100)
}
fun calculateLuminance(r: Int, g: Int, b: Int): Double {
val r = r / 255.0
val g = g / 255.0
val b = b / 255.0
val rs = if (r <= 0.03928) r / 12.92 else kotlin.math.pow((r + 0.055) / 1.055, 2.4)
val gs = if (g <= 0.03928) g / 12.92 else kotlin.math.pow((g + 0.055) / 1.055, 2.4)
val bs = if (b <= 0.03928) b / 12.92 else kotlin.math.pow((b + 0.055) / 1.055, 2.4)
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
fun calculateContrast(color1: Map<String, Int>, color2: Map<String, Int>): Double {
val l1 = calculateLuminance(color1["r"] ?: 0, color1["g"] ?: 0, color1["b"] ?: 0)
val l2 = calculateLuminance(color2["r"] ?: 0, color2["g"] ?: 0, color2["b"] ?: 0)
val lighter = maxOf(l1, l2)
val darker = minOf(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
fun getComplementaryColor(h: Double): Double {
return (h + 180) % 360
}
fun getAnalogousColors(h: Double): List<Double> {
return listOf(
(h - 30 + 360) % 360,
h,
(h + 30) % 360
)
}
fun getTriadicColors(h: Double): List<Double> {
return listOf(
h,
(h + 120) % 360,
(h + 240) % 360
)
}
fun convert(value: String, fromFormat: String, toFormat: String): Map<String, Any> {
return try {
when {
fromFormat == "hex" && toFormat == "rgb" -> {
val rgb = hexToRgb(value)
mapOf("success" to true, "result" to rgb)
}
fromFormat == "rgb" && toFormat == "hex" -> {
val parts = value.split(",").map { it.trim().toInt() }
val hex = rgbToHex(parts[0], parts[1], parts[2])
mapOf("success" to true, "result" to hex)
}
fromFormat == "rgb" && toFormat == "hsl" -> {
val parts = value.split(",").map { it.trim().toInt() }
val hsl = rgbToHsl(parts[0], parts[1], parts[2])
mapOf("success" to true, "result" to hsl)
}
fromFormat == "hsl" && toFormat == "rgb" -> {
val parts = value.split(",").map { it.trim().toDouble() }
val rgb = hslToRgb(parts[0], parts[1], parts[2])
mapOf("success" to true, "result" to rgb)
}
else -> mapOf("success" to false, "error" to "不支持的转换")
}
} catch (e: Exception) {
mapOf("success" to false, "error" to e.message)
}
}
}
fun main() {
val converter = ColorConverter()
println("=== 颜色转换工具演示 ===\n")
// RGB 到 HEX
val hex = converter.rgbToHex(255, 128, 0)
println("RGB(255, 128, 0) = $hex")
// HEX 到 RGB
val rgb = converter.hexToRgb("#FF8000")
println("HEX #FF8000 = RGB${rgb.values.take(3)}")
// RGB 到 HSL
val hsl = converter.rgbToHsl(255, 128, 0)
println("RGB(255, 128, 0) = HSL(${hsl["h"]?.toInt()}, ${hsl["s"]?.toInt()}%, ${hsl["l"]?.toInt()}%)")
// 颜色对比度
val color1 = mapOf("r" to 255, "g" to 255, "b" to 255)
val color2 = mapOf("r" to 0, "g" to 0, "b" to 0)
val contrast = converter.calculateContrast(color1, color2)
println("白色和黑色的对比度: ${String.format("%.2f", contrast)}")
// 互补色
val complementary = converter.getComplementaryColor(30.0)
println("30度的互补色: ${complementary.toInt()}度")
}
Kotlin 代码说明: 这个实现提供了完整的颜色转换功能。ColorConverter 类包含了 RGB、HEX、HSL、HSV 等多种格式的转换方法。通过使用标准的色彩空间转换算法,确保了转换的准确性。颜色分析和搭配建议方法基于色轮的几何关系。
JavaScript 编译代码
// ColorConverter.js
class ColorConverter {
rgbToHex(r, g, b, a = 255) {
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
a = Math.max(0, Math.min(255, a));
if (a === 255) {
return "#" + [r, g, b].map(x => x.toString(16).padStart(2, '0').toUpperCase()).join('');
} else {
return "#" + [r, g, b, a].map(x => x.toString(16).padStart(2, '0').toUpperCase()).join('');
}
}
hexToRgb(hex) {
const cleanHex = hex.replace('#', '');
if (cleanHex.length === 6) {
return {
r: parseInt(cleanHex.substring(0, 2), 16),
g: parseInt(cleanHex.substring(2, 4), 16),
b: parseInt(cleanHex.substring(4, 6), 16),
a: 255
};
} else if (cleanHex.length === 8) {
return {
r: parseInt(cleanHex.substring(0, 2), 16),
g: parseInt(cleanHex.substring(2, 4), 16),
b: parseInt(cleanHex.substring(4, 6), 16),
a: parseInt(cleanHex.substring(6, 8), 16)
};
}
return { r: 0, g: 0, b: 0, a: 255 };
}
rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let l = (max + min) / 2;
if (max === min) {
return { h: 0, s: 0, l: l * 100 };
}
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h;
if (max === r) h = (60 * (((g - b) / d) % 6) + 360) % 360;
else if (max === g) h = (60 * (((b - r) / d) + 2) + 360) % 360;
else h = (60 * (((r - g) / d) + 4) + 360) % 360;
return { h: h, s: s * 100, l: l * 100 };
}
hslToRgb(h, s, l) {
h = h % 360;
s /= 100;
l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
const m = l - c / 2;
let r1, g1, b1;
if (h < 60) { r1 = c; g1 = x; b1 = 0; }
else if (h < 120) { r1 = x; g1 = c; b1 = 0; }
else if (h < 180) { r1 = 0; g1 = c; b1 = x; }
else if (h < 240) { r1 = 0; g1 = x; b1 = c; }
else if (h < 300) { r1 = x; g1 = 0; b1 = c; }
else { r1 = c; g1 = 0; b1 = x; }
return {
r: Math.round((r1 + m) * 255),
g: Math.round((g1 + m) * 255),
b: Math.round((b1 + m) * 255)
};
}
calculateLuminance(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const rs = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
const gs = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
const bs = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}
calculateContrast(color1, color2) {
const l1 = this.calculateLuminance(color1.r || 0, color1.g || 0, color1.b || 0);
const l2 = this.calculateLuminance(color2.r || 0, color2.g || 0, color2.b || 0);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
getComplementaryColor(h) {
return (h + 180) % 360;
}
getAnalogousColors(h) {
return [
(h - 30 + 360) % 360,
h,
(h + 30) % 360
];
}
getTriadicColors(h) {
return [
h,
(h + 120) % 360,
(h + 240) % 360
];
}
convert(value, fromFormat, toFormat) {
try {
if (fromFormat === "hex" && toFormat === "rgb") {
const rgb = this.hexToRgb(value);
return { success: true, result: rgb };
} else if (fromFormat === "rgb" && toFormat === "hex") {
const parts = value.split(",").map(x => parseInt(x.trim()));
const hex = this.rgbToHex(parts[0], parts[1], parts[2]);
return { success: true, result: hex };
} else if (fromFormat === "rgb" && toFormat === "hsl") {
const parts = value.split(",").map(x => parseInt(x.trim()));
const hsl = this.rgbToHsl(parts[0], parts[1], parts[2]);
return { success: true, result: hsl };
} else if (fromFormat === "hsl" && toFormat === "rgb") {
const parts = value.split(",").map(x => parseFloat(x.trim()));
const rgb = this.hslToRgb(parts[0], parts[1], parts[2]);
return { success: true, result: rgb };
}
return { success: false, error: "不支持的转换" };
} catch (e) {
return { success: false, error: e.message };
}
}
}
// 使用示例
const converter = new ColorConverter();
console.log("=== 颜色转换工具演示 ===\n");
const hex = converter.rgbToHex(255, 128, 0);
console.log("RGB(255, 128, 0) =", hex);
const rgb = converter.hexToRgb("#FF8000");
console.log("HEX #FF8000 = RGB", [rgb.r, rgb.g, rgb.b]);
const hsl = converter.rgbToHsl(255, 128, 0);
console.log(`RGB(255, 128, 0) = HSL(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`);
const color1 = { r: 255, g: 255, b: 255 };
const color2 = { r: 0, g: 0, b: 0 };
const contrast = converter.calculateContrast(color1, color2);
console.log("白色和黑色的对比度:", contrast.toFixed(2));
const complementary = converter.getComplementaryColor(30);
console.log("30度的互补色:", Math.round(complementary) + "度");
JavaScript 代码说明: JavaScript 版本是 Kotlin 代码的直接转译。由于 JavaScript 和 Kotlin 在语法上有差异,我们使用对象替代 Map,使用 Math 函数替代 kotlin.math 函数。整体逻辑和算法与 Kotlin 版本保持一致,确保跨平台的一致性。
ArkTS 调用代码
// ColorConverterPage.ets
import { ColorConverter } from './ColorConverter';
@Entry
@Component
struct ColorConverterPage {
@State fromFormat: string = 'rgb';
@State toFormat: string = 'hex';
@State inputValue: string = '255,128,0';
@State outputValue: string = '';
@State showResult: boolean = false;
@State isLoading: boolean = false;
@State previewColor: string = '#FF8000';
private converter: ColorConverter = new ColorConverter();
private formatOptions = [
{ label: 'RGB', value: 'rgb' },
{ label: 'HEX', value: 'hex' },
{ label: 'HSL', value: 'hsl' },
{ label: 'HSV', value: 'hsv' }
];
performConversion() {
this.isLoading = true;
try {
const result = this.converter.convert(this.inputValue, this.fromFormat, this.toFormat);
if (result.success) {
if (typeof result.result === 'object') {
this.outputValue = JSON.stringify(result.result);
if (this.toFormat === 'hex') {
this.previewColor = result.result;
}
} else {
this.outputValue = result.result;
}
} else {
this.outputValue = '转换失败: ' + result.error;
}
this.showResult = true;
} catch (error) {
this.outputValue = '转换出错: ' + (error instanceof Error ? error.message : String(error));
this.showResult = true;
} finally {
this.isLoading = false;
}
}
build() {
Column() {
// 标题
Text('颜色转换工具')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
.textAlign(TextAlign.Center)
.width('100%')
// 颜色预览
if (this.previewColor) {
Row() {
Column()
.width(60)
.height(60)
.backgroundColor(this.previewColor)
.borderRadius(8)
.border({ width: 2, color: '#cccccc' })
Text(this.previewColor)
.fontSize(16)
.fontColor('#333333')
.margin({ left: 15 })
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
.margin({ bottom: 20 })
}
// 格式选择
Row() {
Column() {
Text('从')
.fontSize(12)
.fontColor('#666666')
.margin({ bottom: 8 })
Select(this.formatOptions)
.value(this.fromFormat)
.onSelect((index: number, value?: string) => {
this.fromFormat = value || 'rgb';
})
.width('100%')
.height(40)
}
.width('45%')
Text('→')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.width('10%')
.textAlign(TextAlign.Center)
Column() {
Text('到')
.fontSize(12)
.fontColor('#666666')
.margin({ bottom: 8 })
Select(this.formatOptions)
.value(this.toFormat)
.onSelect((index: number, value?: string) => {
this.toFormat = value || 'hex';
})
.width('100%')
.height(40)
}
.width('45%')
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
.margin({ bottom: 20 })
// 输入框
Column() {
Text('输入颜色值')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 10 })
TextInput({ placeholder: '输入颜色值' })
.value(this.inputValue)
.onChange((value: string) => {
this.inputValue = value;
})
.width('100%')
.height(45)
.padding({ left: 10, right: 10 })
.border({ width: 1, color: '#cccccc', radius: 8 })
.fontSize(14)
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
.margin({ bottom: 20 })
// 转换按钮
Button('转换')
.width('100%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.backgroundColor('#1B7837')
.fontColor('#ffffff')
.onClick(() => {
this.performConversion();
})
.margin({ bottom: 20 })
// 结果显示
if (this.showResult) {
Column() {
Text('转换结果')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1B7837')
.borderRadius(8)
.margin({ bottom: 12 })
Scroll() {
Text(this.outputValue)
.fontSize(14)
.fontColor('#333333')
.width('100%')
.padding(12)
.backgroundColor('#f9f9f9')
.borderRadius(6)
.textAlign(TextAlign.Start)
.selectable(true)
}
.width('100%')
.height(120)
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
}
Blank()
}
.width('100%')
.height('100%')
.padding(15)
.backgroundColor('#f5f5f5')
.scrollable(ScrollDirection.Vertical)
}
}
ArkTS 代码说明: ArkTS 调用代码展示了如何在 OpenHarmony 应用中使用颜色转换工具。页面包含颜色预览、格式选择、输入框、转换按钮和结果显示等功能。通过 @State 装饰器管理状态,实现了完整的用户交互流程。用户可以选择源格式和目标格式,输入颜色值,然后点击转换按钮获得转换结果。
实战案例
案例1:UI设计工具
在 UI 设计工具中,需要支持多种颜色格式的输入和转换。
fun updateColorInDesignTool(colorValue: String, format: String) {
val converter = ColorConverter()
// 转换为 RGB 以便在画布上显示
val rgb = when (format) {
"hex" -> converter.hexToRgb(colorValue)
"hsl" -> {
val parts = colorValue.split(",").map { it.trim().toDouble() }
converter.hslToRgb(parts[0], parts[1], parts[2])
}
else -> mapOf("r" to 0, "g" to 0, "b" to 0)
}
updateCanvasColor(rgb)
}
案例2:无障碍检查工具
在无障碍检查工具中,需要验证颜色对比度是否符合标准。
fun checkColorAccessibility(foreground: Map<String, Int>, background: Map<String, Int>): Boolean {
val converter = ColorConverter()
val contrast = converter.calculateContrast(foreground, background)
// WCAG AA 标准要求对比度至少为 4.5:1
return contrast >= 4.5
}
案例3:颜色搭配建议应用
在颜色搭配建议应用中,根据用户选择的颜色生成搭配方案。
fun generateColorScheme(baseColor: String): Map<String, List<Double>> {
val converter = ColorConverter()
val rgb = converter.hexToRgb(baseColor)
val hsl = converter.rgbToHsl(rgb["r"] ?: 0, rgb["g"] ?: 0, rgb["b"] ?: 0)
val h = hsl["h"] ?: 0.0
return mapOf(
"complementary" to listOf(converter.getComplementaryColor(h)),
"analogous" to converter.getAnalogousColors(h),
"triadic" to converter.getTriadicColors(h)
)
}
最佳实践
1. 输入验证
在转换前验证输入值的格式和范围。确保输入数据的有效性。
2. 精度管理
对于浮点数计算,需要考虑精度问题。使用适当的舍入和格式化。
3. 错误处理
提供清晰的错误消息,帮助用户理解转换失败的原因。
4. 性能优化
对于频繁的颜色转换,考虑缓存结果以提高性能。
5. 无障碍支持
提供颜色对比度检查功能,确保应用符合无障碍标准。
6. 用户体验
提供颜色预览功能,让用户直观地看到转换结果。
总结
颜色转换工具是现代应用开发中的重要组件。通过 KMP 框架,我们可以创建一个跨端的颜色转换系统,在 Kotlin、JavaScript 和 ArkTS 中使用相同的转换逻辑。这不仅提高了代码的可维护性,还确保了不同平台上转换结果的一致性。
在实际应用中,合理使用颜色转换工具可以提高应用的功能性和用户体验。无论是 UI 设计、图形处理还是无障碍设计,颜色转换工具都能发挥重要作用。通过支持多种颜色格式和提供颜色分析功能,我们可以帮助用户更好地处理和理解颜色信息。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)