OpenHarmony KMP颜色转换和分析 | Kotlin实现

目录
概述
本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个完整的颜色转换和分析工具系统。颜色处理是现代应用开发中的一个重要需求,涉及颜色空间转换、颜色匹配、颜色分析等多个方面。无论是进行界面设计、图像处理还是数据可视化,一个功能强大的颜色工具都能提供便利的支持。
这个案例展示了如何使用 Kotlin 的数学运算、字符串处理和颜色空间理论来创建一个功能丰富的颜色分析工具。颜色转换和分析工具需要能够在不同的颜色空间之间进行转换、计算颜色的相似度、生成颜色调色板、分析颜色的对比度等。通过 KMP,这个工具可以无缝编译到 JavaScript,在 OpenHarmony 应用中运行,并支持用户输入进行实时颜色处理。
在实际应用中,颜色工具广泛应用于以下场景:网页设计中的颜色选择、移动应用的主题配置、图像编辑软件的颜色调整、数据可视化中的颜色映射、无障碍设计中的对比度检查等。通过支持多种颜色格式如 RGB、HSL、HEX 和 CMYK,我们可以满足不同场景的需求。同时,通过 KMP 框架的跨端能力,我们可以在不同平台上使用相同的颜色处理逻辑,确保颜色在各个平台上的一致性。
工具的特点
- 多格式支持:支持 RGB、HEX、HSL、HSV、CMYK 等多种颜色格式
- 格式转换:支持不同颜色格式之间的相互转换
- 颜色分析:提供亮度、饱和度、对比度等多维度分析
- 颜色匹配:计算颜色之间的相似度和距离
- 调色板生成:自动生成互补色、类似色等颜色组合
- 跨端兼容:一份 Kotlin 代码可同时服务多个平台
工具功能
1. RGB 和 HEX 转换
RGB(红绿蓝)是最常见的颜色表示方法,每个颜色分量的值范围是 0-255。HEX(十六进制)是网页设计中最常用的颜色表示方法,用六位十六进制数字表示颜色,每两位分别代表红、绿、蓝三个分量。这两种格式的转换是颜色处理中的基础操作,广泛应用于网页设计、移动应用开发等领域。在实际应用中,RGB 格式常用于编程和图像处理,而 HEX 格式则更常见于网页和设计工具中。
- RGB 转 HEX:将 RGB 值转换为十六进制格式
- HEX 转 RGB:将十六进制格式转换为 RGB 值
- 格式验证:验证输入颜色格式的有效性
2. HSL 和 HSV 转换
HSL(色调、饱和度、亮度)和 HSV(色调、饱和度、明度)是基于人类视觉感知的颜色模型。与 RGB 相比,HSL 和 HSV 更符合人类对颜色的直观理解。HSL 中的亮度参数使得颜色的明暗变化更加直观,而 HSV 中的明度参数更适合描述颜色的纯度。这些颜色空间在图像编辑、颜色选择器等应用中被广泛使用。HSL 模型特别适合于创建颜色主题和调色板,因为改变亮度值可以轻松创建颜色的浅色和深色版本。
- RGB 转 HSL:将 RGB 颜色转换为 HSL 模型
- HSL 转 RGB:将 HSL 颜色转换为 RGB 模型
- RGB 转 HSV:将 RGB 颜色转换为 HSV 模型
- HSV 转 RGB:将 HSV 颜色转换为 RGB 模型
3. CMYK 转换
CMYK(青、品红、黄、黑)是印刷行业标准的颜色模型。与 RGB 不同,CMYK 是一种减色模型,用于描述印刷墨水的混合。在设计印刷品时,需要将 RGB 颜色转换为 CMYK 颜色,以确保印刷效果与屏幕显示的一致性。CMYK 模型中的黑色(K)分量对于印刷质量至关重要,它可以提高印刷效率并改善颜色的深度。
- RGB 转 CMYK:将 RGB 颜色转换为 CMYK 模型
- CMYK 转 RGB:将 CMYK 颜色转换为 RGB 模型
- 印刷优化:提供印刷友好的颜色建议
4. 颜色分析
颜色分析是理解颜色特性的关键。通过计算颜色的亮度、饱和度和对比度,我们可以评估颜色的可读性和视觉效果。亮度计算基于人眼对不同颜色的感知敏感度,绿色通常被认为最亮,蓝色最暗。对比度计算遵循 WCAG 标准,用于确保网页和应用的无障碍性。
- 亮度计算:计算颜色的相对亮度
- 饱和度分析:分析颜色的饱和度水平
- 对比度检查:计算两种颜色的对比度,用于无障碍设计
- 色温分析:判断颜色的冷暖属性
5. 颜色匹配和生成
颜色匹配和生成是设计工作中的重要工具。互补色是在色轮上相对的颜色,能产生高对比度的视觉效果。类似色是相邻的颜色,能产生和谐的视觉效果。三角配色方案使用三种均匀分布的颜色,能产生平衡的视觉效果。这些配色方案在网页设计、应用界面设计和品牌设计中被广泛应用。
- 互补色:生成给定颜色的互补色
- 类似色:生成与给定颜色相似的颜色
- 三角配色:生成三种颜色的和谐配色方案
- 颜色距离:计算两种颜色之间的欧几里得距离
核心实现
1. RGB 和 HEX 转换
fun rgbToHex(r: Int, g: Int, b: Int): String {
require(r in 0..255 && g in 0..255 && b in 0..255) { "RGB 值必须在 0-255 之间" }
return "#" + String.format("%02X%02X%02X", r, g, b)
}
fun hexToRgb(hex: String): Triple<Int, Int, Int> {
val cleanHex = hex.removePrefix("#")
require(cleanHex.length == 6) { "HEX 颜色必须是 6 位十六进制数字" }
val r = cleanHex.substring(0, 2).toInt(16)
val g = cleanHex.substring(2, 4).toInt(16)
val b = cleanHex.substring(4, 6).toInt(16)
return Triple(r, g, b)
}
代码说明: RGB 到 HEX 的转换通过将每个颜色分量(0-255)转换为两位十六进制数字来实现。使用 String.format 函数可以确保每个分量都用两位十六进制数字表示,不足两位时用 0 补充。HEX 到 RGB 的转换是反向过程,首先移除 ‘#’ 前缀,然后将六位十六进制数字分成三组,每组两位,分别转换为十进制的 RGB 值。这种转换在网页设计和移动应用开发中非常常见。
2. RGB 和 HSL 转换
fun rgbToHsl(r: Int, g: Int, b: Int): Triple<Double, Double, Double> {
val rNorm = r / 255.0
val gNorm = g / 255.0
val bNorm = b / 255.0
val max = maxOf(rNorm, gNorm, bNorm)
val min = minOf(rNorm, gNorm, bNorm)
val delta = max - min
val lightness = (max + min) / 2.0
val saturation = if (delta == 0.0) 0.0 else delta / (1 - Math.abs(2 * lightness - 1))
val hue = when {
delta == 0.0 -> 0.0
max == rNorm -> (60 * (((gNorm - bNorm) / delta) % 6) + 360) % 360
max == gNorm -> (60 * (((bNorm - rNorm) / delta) + 2) + 360) % 360
else -> (60 * (((rNorm - gNorm) / delta) + 4) + 360) % 360
}
return Triple(hue, saturation * 100, lightness * 100)
}
代码说明: RGB 到 HSL 的转换涉及复杂的数学计算。首先,我们将 RGB 值归一化到 0-1 范围,然后计算最大值、最小值和它们的差。亮度是最大值和最小值的平均值。饱和度的计算取决于亮度值,使用特定的公式确保在不同亮度下的准确性。色调的计算基于哪个颜色分量最大,使用不同的公式来确定色调的角度。
3. 颜色对比度计算
fun calculateLuminance(r: Int, g: Int, b: Int): Double {
val rNorm = r / 255.0
val gNorm = g / 255.0
val bNorm = b / 255.0
val rLinear = if (rNorm <= 0.03928) rNorm / 12.92 else Math.pow((rNorm + 0.055) / 1.055, 2.4)
val gLinear = if (gNorm <= 0.03928) gNorm / 12.92 else Math.pow((gNorm + 0.055) / 1.055, 2.4)
val bLinear = if (bNorm <= 0.03928) bNorm / 12.92 else Math.pow((bNorm + 0.055) / 1.055, 2.4)
return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear
}
fun calculateContrast(r1: Int, g1: Int, b1: Int, r2: Int, g2: Int, b2: Int): Double {
val lum1 = calculateLuminance(r1, g1, b1)
val lum2 = calculateLuminance(r2, g2, b2)
val lighter = maxOf(lum1, lum2)
val darker = minOf(lum1, lum2)
return (lighter + 0.05) / (darker + 0.05)
}
代码说明: 颜色对比度的计算基于 WCAG(网页内容无障碍指南)的标准。首先计算颜色的相对亮度,这涉及到 gamma 校正和加权平均。然后,对比度是较亮颜色的亮度加 0.05 与较暗颜色的亮度加 0.05 的比值。对比度值越高,两种颜色的区分度越好,越符合无障碍设计的要求。
4. 互补色和类似色生成
fun getComplementaryColor(h: Double, s: Double, l: Double): Triple<Double, Double, Double> {
val complementaryHue = (h + 180) % 360
return Triple(complementaryHue, s, l)
}
fun getAnalogousColors(h: Double, s: Double, l: Double): List<Triple<Double, Double, Double>> {
val angle = 30.0
return listOf(
Triple((h - angle + 360) % 360, s, l),
Triple(h, s, l),
Triple((h + angle) % 360, s, l)
)
}
代码说明: 互补色是在色轮上相对的颜色,色调相差 180 度。类似色是在色轮上相邻的颜色,通常相差 30 度。这些颜色组合在设计中能产生和谐的视觉效果。
Kotlin 源代码
// ColorConversionAnalysis.kt
class ColorConversionAnalysis {
fun rgbToHex(r: Int, g: Int, b: Int): String {
require(r in 0..255 && g in 0..255 && b in 0..255)
return "#" + String.format("%02X%02X%02X", r, g, b)
}
fun hexToRgb(hex: String): Triple<Int, Int, Int> {
val cleanHex = hex.removePrefix("#")
require(cleanHex.length == 6)
val r = cleanHex.substring(0, 2).toInt(16)
val g = cleanHex.substring(2, 4).toInt(16)
val b = cleanHex.substring(4, 6).toInt(16)
return Triple(r, g, b)
}
fun rgbToHsl(r: Int, g: Int, b: Int): Triple<Double, Double, Double> {
val rNorm = r / 255.0
val gNorm = g / 255.0
val bNorm = b / 255.0
val max = maxOf(rNorm, gNorm, bNorm)
val min = minOf(rNorm, gNorm, bNorm)
val delta = max - min
val lightness = (max + min) / 2.0
val saturation = if (delta == 0.0) 0.0 else delta / (1 - Math.abs(2 * lightness - 1))
val hue = when {
delta == 0.0 -> 0.0
max == rNorm -> (60 * (((gNorm - bNorm) / delta) % 6) + 360) % 360
max == gNorm -> (60 * (((bNorm - rNorm) / delta) + 2) + 360) % 360
else -> (60 * (((rNorm - gNorm) / delta) + 4) + 360) % 360
}
return Triple(hue, saturation * 100, lightness * 100)
}
fun hslToRgb(h: Double, s: Double, l: Double): Triple<Int, Int, Int> {
val sNorm = s / 100.0
val lNorm = l / 100.0
val c = (1 - Math.abs(2 * lNorm - 1)) * sNorm
val hPrime = h / 60.0
val x = c * (1 - Math.abs((hPrime % 2) - 1))
val (rPrime, gPrime, bPrime) = when {
hPrime < 1 -> Triple(c, x, 0.0)
hPrime < 2 -> Triple(x, c, 0.0)
hPrime < 3 -> Triple(0.0, c, x)
hPrime < 4 -> Triple(0.0, x, c)
hPrime < 5 -> Triple(x, 0.0, c)
else -> Triple(c, 0.0, x)
}
val m = lNorm - c / 2.0
val r = ((rPrime + m) * 255).toInt()
val g = ((gPrime + m) * 255).toInt()
val b = ((bPrime + m) * 255).toInt()
return Triple(r, g, b)
}
fun calculateLuminance(r: Int, g: Int, b: Int): Double {
val rNorm = r / 255.0
val gNorm = g / 255.0
val bNorm = b / 255.0
val rLinear = if (rNorm <= 0.03928) rNorm / 12.92 else Math.pow((rNorm + 0.055) / 1.055, 2.4)
val gLinear = if (gNorm <= 0.03928) gNorm / 12.92 else Math.pow((gNorm + 0.055) / 1.055, 2.4)
val bLinear = if (bNorm <= 0.03928) bNorm / 12.92 else Math.pow((bNorm + 0.055) / 1.055, 2.4)
return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear
}
fun calculateContrast(r1: Int, g1: Int, b1: Int, r2: Int, g2: Int, b2: Int): Double {
val lum1 = calculateLuminance(r1, g1, b1)
val lum2 = calculateLuminance(r2, g2, b2)
val lighter = maxOf(lum1, lum2)
val darker = minOf(lum1, lum2)
return (lighter + 0.05) / (darker + 0.05)
}
fun getComplementaryColor(h: Double, s: Double, l: Double): Triple<Double, Double, Double> {
val complementaryHue = (h + 180) % 360
return Triple(complementaryHue, s, l)
}
fun getAnalogousColors(h: Double, s: Double, l: Double): List<Triple<Double, Double, Double>> {
val angle = 30.0
return listOf(
Triple((h - angle + 360) % 360, s, l),
Triple(h, s, l),
Triple((h + angle) % 360, s, l)
)
}
fun getTriadicColors(h: Double, s: Double, l: Double): List<Triple<Double, Double, Double>> {
val angle = 120.0
return listOf(
Triple(h, s, l),
Triple((h + angle) % 360, s, l),
Triple((h + 2 * angle) % 360, s, l)
)
}
fun calculateColorDistance(r1: Int, g1: Int, b1: Int, r2: Int, g2: Int, b2: Int): Double {
val dr = (r1 - r2).toDouble()
val dg = (g1 - g2).toDouble()
val db = (b1 - b2).toDouble()
return Math.sqrt(dr * dr + dg * dg + db * db)
}
fun analyzeColor(r: Int, g: Int, b: Int): Map<String, Any> {
val hex = rgbToHex(r, g, b)
val (h, s, l) = rgbToHsl(r, g, b)
val luminance = calculateLuminance(r, g, b)
val colorType = when {
s < 10 -> "灰色"
l < 20 -> "深色"
l > 80 -> "浅色"
else -> "彩色"
}
return mapOf(
"hex" to hex,
"rgb" to "RGB($r, $g, $b)",
"hsl" to "HSL(${h.toInt()}, ${s.toInt()}%, ${l.toInt()}%)",
"luminance" to String.format("%.4f", luminance),
"colorType" to colorType
)
}
}
Kotlin 代码说明: 这个实现提供了完整的颜色转换和分析功能。每个方法都有明确的输入验证和错误处理。RGB 和 HEX 的转换是基础操作,而 HSL 的转换涉及更复杂的数学计算。此外,还提供了颜色对比度计算、颜色距离计算和颜色属性分析等功能。
JavaScript 编译代码
// ColorConversionAnalysis.js
class ColorConversionAnalysis {
rgbToHex(r, g, b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
throw new Error("RGB 值必须在 0-255 之间");
}
const toHex = (n) => {
const hex = n.toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return '#' + toHex(r).toUpperCase() + toHex(g).toUpperCase() + toHex(b).toUpperCase();
}
hexToRgb(hex) {
const cleanHex = hex.startsWith('#') ? hex.substring(1) : hex;
if (cleanHex.length !== 6) {
throw new Error("HEX 颜色必须是 6 位十六进制数字");
}
const r = parseInt(cleanHex.substring(0, 2), 16);
const g = parseInt(cleanHex.substring(2, 4), 16);
const b = parseInt(cleanHex.substring(4, 6), 16);
return { r, g, b };
}
rgbToHsl(r, g, b) {
const rNorm = r / 255.0;
const gNorm = g / 255.0;
const bNorm = b / 255.0;
const max = Math.max(rNorm, gNorm, bNorm);
const min = Math.min(rNorm, gNorm, bNorm);
const delta = max - min;
const lightness = (max + min) / 2.0;
let saturation = 0;
if (delta !== 0) {
saturation = delta / (1 - Math.abs(2 * lightness - 1));
}
let hue = 0;
if (delta !== 0) {
if (max === rNorm) {
hue = (60 * (((gNorm - bNorm) / delta) % 6) + 360) % 360;
} else if (max === gNorm) {
hue = (60 * (((bNorm - rNorm) / delta) + 2) + 360) % 360;
} else {
hue = (60 * (((rNorm - gNorm) / delta) + 4) + 360) % 360;
}
}
return { h: hue, s: saturation * 100, l: lightness * 100 };
}
hslToRgb(h, s, l) {
const sNorm = s / 100.0;
const lNorm = l / 100.0;
const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
const hPrime = h / 60.0;
const x = c * (1 - Math.abs((hPrime % 2) - 1));
let rPrime = 0, gPrime = 0, bPrime = 0;
if (hPrime < 1) {
rPrime = c; gPrime = x; bPrime = 0;
} else if (hPrime < 2) {
rPrime = x; gPrime = c; bPrime = 0;
} else if (hPrime < 3) {
rPrime = 0; gPrime = c; bPrime = x;
} else if (hPrime < 4) {
rPrime = 0; gPrime = x; bPrime = c;
} else if (hPrime < 5) {
rPrime = x; gPrime = 0; bPrime = c;
} else {
rPrime = c; gPrime = 0; bPrime = x;
}
const m = lNorm - c / 2.0;
const r = Math.round((rPrime + m) * 255);
const g = Math.round((gPrime + m) * 255);
const b = Math.round((bPrime + m) * 255);
return { r, g, b };
}
calculateLuminance(r, g, b) {
const rNorm = r / 255.0;
const gNorm = g / 255.0;
const bNorm = b / 255.0;
const rLinear = rNorm <= 0.03928 ? rNorm / 12.92 : Math.pow((rNorm + 0.055) / 1.055, 2.4);
const gLinear = gNorm <= 0.03928 ? gNorm / 12.92 : Math.pow((gNorm + 0.055) / 1.055, 2.4);
const bLinear = bNorm <= 0.03928 ? bNorm / 12.92 : Math.pow((bNorm + 0.055) / 1.055, 2.4);
return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear;
}
calculateContrast(r1, g1, b1, r2, g2, b2) {
const lum1 = this.calculateLuminance(r1, g1, b1);
const lum2 = this.calculateLuminance(r2, g2, b2);
const lighter = Math.max(lum1, lum2);
const darker = Math.min(lum1, lum2);
return (lighter + 0.05) / (darker + 0.05);
}
getComplementaryColor(h, s, l) {
const complementaryHue = (h + 180) % 360;
return { h: complementaryHue, s, l };
}
getAnalogousColors(h, s, l) {
const angle = 30.0;
return [
{ h: (h - angle + 360) % 360, s, l },
{ h, s, l },
{ h: (h + angle) % 360, s, l }
];
}
getTriadicColors(h, s, l) {
const angle = 120.0;
return [
{ h, s, l },
{ h: (h + angle) % 360, s, l },
{ h: (h + 2 * angle) % 360, s, l }
];
}
calculateColorDistance(r1, g1, b1, r2, g2, b2) {
const dr = r1 - r2;
const dg = g1 - g2;
const db = b1 - b2;
return Math.sqrt(dr * dr + dg * dg + db * db);
}
analyzeColor(r, g, b) {
const hex = this.rgbToHex(r, g, b);
const hsl = this.rgbToHsl(r, g, b);
const luminance = this.calculateLuminance(r, g, b);
let colorType = '彩色';
if (hsl.s < 10) {
colorType = '灰色';
} else if (hsl.l < 20) {
colorType = '深色';
} else if (hsl.l > 80) {
colorType = '浅色';
}
return {
hex: hex,
rgb: `RGB(${r}, ${g}, ${b})`,
hsl: `HSL(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`,
luminance: luminance.toFixed(4),
colorType: colorType
};
}
}
JavaScript 代码说明: JavaScript 版本是 Kotlin 代码的直接转译。由于 JavaScript 和 Kotlin 在语法上有差异,我们需要进行相应的调整。例如,Kotlin 的 Triple 数据结构在 JavaScript 中使用对象来表示。整体逻辑和算法与 Kotlin 版本保持一致,确保跨平台的一致性。
ArkTS 调用代码
// ColorConversionPage.ets
import { ColorConversionAnalysis } from './ColorConversionAnalysis';
@Entry
@Component
struct ColorConversionPage {
@State selectedColor: string = '#FF6432';
@State colorR: number = 255;
@State colorG: number = 100;
@State colorB: number = 50;
@State colorHex: string = '#FF6432';
@State colorHsl: string = '';
@State colorType: string = '';
@State luminance: string = '';
@State complementaryColor: string = '';
@State analogousColors: string[] = [];
@State triadicColors: string[] = [];
@State contrastRatio: string = '';
private tool: ColorConversionAnalysis = new ColorConversionAnalysis();
updateColorAnalysis() {
try {
const analysis = this.tool.analyzeColor(this.colorR, this.colorG, this.colorB);
this.colorHex = analysis.hex;
this.colorHsl = analysis.hsl;
this.colorType = analysis.colorType;
this.luminance = analysis.luminance;
const hsl = this.tool.rgbToHsl(this.colorR, this.colorG, this.colorB);
const comp = this.tool.getComplementaryColor(hsl.h, hsl.s, hsl.l);
const compRgb = this.tool.hslToRgb(comp.h, comp.s, comp.l);
this.complementaryColor = this.tool.rgbToHex(compRgb.r, compRgb.g, compRgb.b);
const analogous = this.tool.getAnalogousColors(hsl.h, hsl.s, hsl.l);
this.analogousColors = analogous.map(color => {
const rgb = this.tool.hslToRgb(color.h, color.s, color.l);
return this.tool.rgbToHex(rgb.r, rgb.g, rgb.b);
});
const triadic = this.tool.getTriadicColors(hsl.h, hsl.s, hsl.l);
this.triadicColors = triadic.map(color => {
const rgb = this.tool.hslToRgb(color.h, color.s, color.l);
return this.tool.rgbToHex(rgb.r, rgb.g, rgb.b);
});
const contrast = this.tool.calculateContrast(this.colorR, this.colorG, this.colorB, 255, 255, 255);
this.contrastRatio = contrast.toFixed(2);
} catch (error) {
AlertDialog.show({ message: '颜色分析失败' });
}
}
build() {
Column() {
Text('颜色转换和分析工具')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
Row() {
Column() {
Text('当前颜色').fontSize(12).fontColor('#666666').margin({ bottom: 5 })
Rect().width(80).height(80).fill(this.selectedColor)
.border({ width: 2, color: '#cccccc' }).borderRadius(4)
}
.margin({ right: 20 })
Column() {
Text('互补色').fontSize(12).fontColor('#666666').margin({ bottom: 5 })
Rect().width(80).height(80).fill(this.complementaryColor)
.border({ width: 2, color: '#cccccc' }).borderRadius(4)
}
}
.margin({ bottom: 20 })
TextInput({ placeholder: '输入 HEX 颜色代码' })
.value(this.colorHex)
.onChange((value: string) => {
try {
const rgb = this.tool.hexToRgb(value);
this.colorR = rgb.r;
this.colorG = rgb.g;
this.colorB = rgb.b;
this.selectedColor = value;
this.updateColorAnalysis();
} catch (error) {}
})
.margin({ bottom: 15 })
.padding(10)
.border({ width: 1, color: '#cccccc' })
Column() {
Row() {
Text('R:').width(30)
Slider({ value: this.colorR, min: 0, max: 255 })
.flex(1)
.onChange((value: number) => {
this.colorR = Math.round(value);
this.selectedColor = this.tool.rgbToHex(this.colorR, this.colorG, this.colorB);
this.updateColorAnalysis();
})
Text(this.colorR.toString()).width(40)
}
.margin({ bottom: 10 })
Row() {
Text('G:').width(30)
Slider({ value: this.colorG, min: 0, max: 255 })
.flex(1)
.onChange((value: number) => {
this.colorG = Math.round(value);
this.selectedColor = this.tool.rgbToHex(this.colorR, this.colorG, this.colorB);
this.updateColorAnalysis();
})
Text(this.colorG.toString()).width(40)
}
.margin({ bottom: 10 })
Row() {
Text('B:').width(30)
Slider({ value: this.colorB, min: 0, max: 255 })
.flex(1)
.onChange((value: number) => {
this.colorB = Math.round(value);
this.selectedColor = this.tool.rgbToHex(this.colorR, this.colorG, this.colorB);
this.updateColorAnalysis();
})
Text(this.colorB.toString()).width(40)
}
}
.margin({ bottom: 20 })
Column() {
Text('颜色信息').fontSize(14).fontWeight(FontWeight.Bold).margin({ bottom: 10 })
Row() { Text('HEX:').width(60); Text(this.colorHex).flex(1).selectable(true) }.margin({ bottom: 8 })
Row() { Text('HSL:').width(60); Text(this.colorHsl).flex(1).selectable(true) }.margin({ bottom: 8 })
Row() { Text('类型:').width(60); Text(this.colorType).flex(1) }.margin({ bottom: 8 })
Row() { Text('亮度:').width(60); Text(this.luminance).flex(1) }.margin({ bottom: 8 })
Row() { Text('对比度:').width(60); Text(this.contrastRatio).flex(1) }
}
.width('100%')
.padding(10)
.backgroundColor('#f5f5f5')
.borderRadius(4)
.margin({ bottom: 20 })
Column() {
Text('类似色').fontSize(14).fontWeight(FontWeight.Bold).margin({ bottom: 10 })
Row() {
ForEach(this.analogousColors, (color: string) => {
Rect().width(50).height(50).fill(color)
.border({ width: 1, color: '#cccccc' }).borderRadius(4).margin({ right: 10 })
})
}
}
.width('100%')
.margin({ bottom: 20 })
Column() {
Text('三角配色').fontSize(14).fontWeight(FontWeight.Bold).margin({ bottom: 10 })
Row() {
ForEach(this.triadicColors, (color: string) => {
Rect().width(50).height(50).fill(color)
.border({ width: 1, color: '#cccccc' }).borderRadius(4).margin({ right: 10 })
})
}
}
.width('100%')
}
.padding(20)
.width('100%')
.height('100%')
.backgroundColor('#ffffff')
.onAppear(() => { this.updateColorAnalysis() })
}
}
ArkTS 代码说明: 这个示例展示了如何在 ArkTS 中构建一个完整的颜色转换和分析用户界面。页面包含了颜色预览、HEX 输入框、RGB 滑块、颜色信息显示、类似色和三角配色等组件。用户可以通过输入 HEX 代码或调整 RGB 滑块来改变颜色,系统会实时计算并显示颜色的各种属性和相关配色方案。
实战案例
案例 1: 网页设计中的颜色选择
在网页设计中,选择合适的颜色方案至关重要。使用颜色工具可以快速生成和谐的配色方案。设计师可以先选择一个主色调,然后使用工具生成互补色、类似色或三角配色方案。这样可以确保整个网站的颜色搭配协调一致,提高用户体验。
val tool = ColorConversionAnalysis()
val primaryColor = Triple(255, 100, 50)
val (h, s, l) = tool.rgbToHsl(primaryColor.first, primaryColor.second, primaryColor.third)
val complementary = tool.getComplementaryColor(h, s, l)
val complementaryRgb = tool.hslToRgb(complementary.first, complementary.second, complementary.third)
val complementaryHex = tool.rgbToHex(complementaryRgb.first, complementaryRgb.second, complementaryRgb.third)
案例 2: 无障碍设计中的对比度检查
在开发应用时,需要确保文本和背景颜色有足够的对比度,以便用户能够清晰地阅读。WCAG 标准要求最小对比度为 4.5:1。使用颜色工具可以快速检查颜色对是否满足无障碍要求。
val tool = ColorConversionAnalysis()
val textColor = Triple(0, 0, 0)
val backgroundColor = Triple(255, 255, 255)
val contrast = tool.calculateContrast(
textColor.first, textColor.second, textColor.third,
backgroundColor.first, backgroundColor.second, backgroundColor.third
)
val isAccessible = contrast >= 4.5
案例 3: 数据可视化中的颜色映射
在数据可视化中,需要将数据值映射到颜色。使用 HSL 颜色空间可以轻松创建渐变色。通过改变亮度值,可以创建从浅色到深色的渐变,这样可以直观地表示数据的大小。
val tool = ColorConversionAnalysis()
val dataValues = listOf(10, 20, 30, 40, 50)
val colors = dataValues.map { value ->
val normalizedValue = value / 50.0
val lightness = 50 + (normalizedValue * 30)
val hsl = Triple(220.0, 80.0, lightness)
val rgb = tool.hslToRgb(hsl.first, hsl.second, hsl.third)
tool.rgbToHex(rgb.first, rgb.second, rgb.third)
}
最佳实践
1. 颜色空间选择
- RGB:用于屏幕显示和编程
- HSL/HSV:用于颜色选择和调整
- CMYK:用于印刷设计
- 根据场景选择合适的颜色空间
2. 对比度检查
- 遵循 WCAG 标准:确保最小对比度为 4.5:1
- 定期测试:在不同设备上测试颜色对比度
- 考虑色盲用户:使用工具检查色盲用户能否区分颜色
3. 颜色一致性
- 使用调色板:为整个应用定义一套标准颜色
- 文档化颜色:记录每个颜色的用途和含义
- 跨平台验证:确保颜色在不同平台上的一致性
4. 性能优化
- 缓存转换结果:对于频繁使用的颜色转换,使用缓存
- 预计算调色板:在应用启动时预计算常用的颜色方案
- 异步处理:对于大量颜色处理,使用异步操作
5. 用户体验
- 实时预览:提供实时的颜色预览功能
- 快速反馈:确保颜色调整的响应速度
- 直观界面:使用滑块和颜色选择器等直观的控件
总结
颜色转换和分析工具是现代应用开发中的一个重要组件。通过使用 Kotlin Multiplatform,我们可以编写一次代码,然后在多个平台上运行,大大提高了开发效率和代码的可维护性。这个案例展示了如何实现多种颜色空间之间的转换、进行颜色分析、生成和谐的配色方案等功能。
在实际应用中,应该根据具体的需求选择合适的颜色空间和算法,并遵循最佳实践来确保颜色的准确性和一致性。同时,应该定期进行测试和优化,以提高应用的性能和用户体验。通过合理使用颜色工具,我们可以创建更加美观、易用和无障碍的应用。欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)