在这里插入图片描述
在这里插入图片描述

项目概述

颜色处理是现代应用开发中的重要需求。无论是在 UI 设计、数据可视化、图像处理还是游戏开发中,都需要进行各种颜色操作。然而,不同的编程语言和平台对颜色的表示和处理方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。

本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的颜色工具库。这个工具库提供了一套完整的颜色处理能力,包括颜色格式转换、颜色亮度调整、颜色混合、颜色分析等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。

技术架构

多平台支持

  • Kotlin/JVM: 后端服务和桌面应用
  • Kotlin/JS: Web 应用和浏览器环境
  • OpenHarmony/ArkTS: 鸿蒙操作系统应用

核心功能模块

  1. 颜色格式转换: HEX、RGB、HSL、HSV 等格式之间的转换
  2. 颜色亮度调整: 增加或减少颜色的亮度
  3. 颜色饱和度调整: 调整颜色的饱和度
  4. 颜色混合: 混合两种或多种颜色
  5. 颜色分析: 分析颜色的各种属性
  6. 颜色生成: 生成互补色、类似色等
  7. 颜色验证: 验证颜色值的有效性

Kotlin 实现

核心颜色处理类

// 文件: src/commonMain/kotlin/ColorProcessor.kt

import kotlin.math.*

/**
 * 颜色处理工具类
 * 提供颜色格式转换、亮度调整、混合等功能
 */
class ColorProcessor {
    
    /**
     * HEX 颜色转 RGB
     * @param hexColor HEX 颜色字符串(如 #FF5733)
     * @return RGB 颜色数组 [R, G, B]
     */
    fun hexToRgb(hexColor: String): IntArray {
        val hex = hexColor.removePrefix("#")
        return if (hex.length == 6) {
            intArrayOf(
                hex.substring(0, 2).toInt(16),
                hex.substring(2, 4).toInt(16),
                hex.substring(4, 6).toInt(16)
            )
        } else {
            intArrayOf(0, 0, 0)
        }
    }
    
    /**
     * RGB 颜色转 HEX
     * @param r 红色分量(0-255)
     * @param g 绿色分量(0-255)
     * @param b 蓝色分量(0-255)
     * @return HEX 颜色字符串
     */
    fun rgbToHex(r: Int, g: Int, b: Int): String {
        return "#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}".uppercase()
    }
    
    /**
     * RGB 颜色转 HSL
     * @param r 红色分量(0-255)
     * @param g 绿色分量(0-255)
     * @param b 蓝色分量(0-255)
     * @return HSL 颜色数组 [H(0-360), S(0-100), L(0-100)]
     */
    fun rgbToHsl(r: Int, g: Int, b: Int): DoubleArray {
        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 l = (max + min) / 2
        
        val h = when {
            max == min -> 0.0
            max == rNorm -> (60 * ((gNorm - bNorm) / (max - min)) + 360) % 360
            max == gNorm -> (60 * ((bNorm - rNorm) / (max - min)) + 120) % 360
            else -> (60 * ((rNorm - gNorm) / (max - min)) + 240) % 360
        }
        
        val s = when {
            max == min -> 0.0
            l < 0.5 -> (max - min) / (max + min)
            else -> (max - min) / (2 - max - min)
        }
        
        return doubleArrayOf(h, s * 100, l * 100)
    }
    
    /**
     * HSL 颜色转 RGB
     * @param h 色调(0-360)
     * @param s 饱和度(0-100)
     * @param l 亮度(0-100)
     * @return RGB 颜色数组 [R, G, B]
     */
    fun hslToRgb(h: Double, s: Double, l: Double): IntArray {
        val sNorm = s / 100
        val lNorm = l / 100
        
        val c = (1 - abs(2 * lNorm - 1)) * sNorm
        val x = c * (1 - abs((h / 60) % 2 - 1))
        val m = lNorm - c / 2
        
        val (rPrime, gPrime, bPrime) = 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 intArrayOf(
            ((rPrime + m) * 255).toInt(),
            ((gPrime + m) * 255).toInt(),
            ((bPrime + m) * 255).toInt()
        )
    }
    
    /**
     * 调整颜色亮度
     * @param hexColor HEX 颜色字符串
     * @param percent 亮度调整百分比(-100 到 100)
     * @return 调整后的 HEX 颜色
     */
    fun adjustBrightness(hexColor: String, percent: Int): String {
        val rgb = hexToRgb(hexColor)
        val hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
        
        val newL = (hsl[2] + percent).coerceIn(0.0, 100.0)
        val newRgb = hslToRgb(hsl[0], hsl[1], newL)
        
        return rgbToHex(newRgb[0], newRgb[1], newRgb[2])
    }
    
    /**
     * 调整颜色饱和度
     * @param hexColor HEX 颜色字符串
     * @param percent 饱和度调整百分比(-100 到 100)
     * @return 调整后的 HEX 颜色
     */
    fun adjustSaturation(hexColor: String, percent: Int): String {
        val rgb = hexToRgb(hexColor)
        val hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
        
        val newS = (hsl[1] + percent).coerceIn(0.0, 100.0)
        val newRgb = hslToRgb(hsl[0], newS, hsl[2])
        
        return rgbToHex(newRgb[0], newRgb[1], newRgb[2])
    }
    
    /**
     * 混合两种颜色
     * @param color1 第一种颜色(HEX)
     * @param color2 第二种颜色(HEX)
     * @param ratio 混合比例(0-1,0 表示全是 color1,1 表示全是 color2)
     * @return 混合后的颜色
     */
    fun blendColors(color1: String, color2: String, ratio: Double): String {
        val rgb1 = hexToRgb(color1)
        val rgb2 = hexToRgb(color2)
        
        val r = (rgb1[0] * (1 - ratio) + rgb2[0] * ratio).toInt()
        val g = (rgb1[1] * (1 - ratio) + rgb2[1] * ratio).toInt()
        val b = (rgb1[2] * (1 - ratio) + rgb2[2] * ratio).toInt()
        
        return rgbToHex(r, g, b)
    }
    
    /**
     * 获取互补色
     * @param hexColor HEX 颜色字符串
     * @return 互补色
     */
    fun getComplementaryColor(hexColor: String): String {
        val rgb = hexToRgb(hexColor)
        val hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
        
        val complementaryH = (hsl[0] + 180) % 360
        val newRgb = hslToRgb(complementaryH, hsl[1], hsl[2])
        
        return rgbToHex(newRgb[0], newRgb[1], newRgb[2])
    }
    
    /**
     * 获取类似色
     * @param hexColor HEX 颜色字符串
     * @param angle 角度偏移(通常为 30)
     * @return 类似色列表
     */
    fun getAnalogousColors(hexColor: String, angle: Int = 30): List<String> {
        val rgb = hexToRgb(hexColor)
        val hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
        
        val color1H = (hsl[0] - angle + 360) % 360
        val color2H = (hsl[0] + angle) % 360
        
        val color1Rgb = hslToRgb(color1H, hsl[1], hsl[2])
        val color2Rgb = hslToRgb(color2H, hsl[1], hsl[2])
        
        return listOf(
            rgbToHex(color1Rgb[0], color1Rgb[1], color1Rgb[2]),
            hexColor,
            rgbToHex(color2Rgb[0], color2Rgb[1], color2Rgb[2])
        )
    }
    
    /**
     * 验证 HEX 颜色
     * @param hexColor HEX 颜色字符串
     * @return 是否有效
     */
    fun isValidHexColor(hexColor: String): Boolean {
        val hex = hexColor.removePrefix("#")
        return hex.length == 6 && hex.all { it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
    }
    
    /**
     * 获取颜色的亮度值
     * @param hexColor HEX 颜色字符串
     * @return 亮度值(0-255)
     */
    fun getLuminance(hexColor: String): Int {
        val rgb = hexToRgb(hexColor)
        return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]).toInt()
    }
    
    /**
     * 判断颜色是否为浅色
     * @param hexColor HEX 颜色字符串
     * @return 是否为浅色
     */
    fun isLightColor(hexColor: String): Boolean {
        return getLuminance(hexColor) > 128
    }
    
    /**
     * 获取对比色(黑或白)
     * @param hexColor HEX 颜色字符串
     * @return 对比色(#000000 或 #FFFFFF)
     */
    fun getContrastColor(hexColor: String): String {
        return if (isLightColor(hexColor)) "#000000" else "#FFFFFF"
    }
}

Kotlin 实现的核心特点

Kotlin 实现中的颜色处理功能充分利用了 Kotlin 标准库的数学函数。HEX 到 RGB 的转换使用了字符串操作和进制转换。RGB 到 HSL 的转换使用了标准的色彩空间转换公式,这是色彩处理中的基础操作。

颜色亮度和饱和度的调整通过先转换到 HSL 色彩空间,修改相应的分量,然后转换回 RGB 来实现。这种方法比直接修改 RGB 值更加直观和有效。

颜色混合功能使用了线性插值的方法,这是图形学中的常见技术。通过调整混合比例,可以实现平滑的颜色过渡。

互补色和类似色的生成基于色调的旋转。互补色是色调旋转 180 度,类似色是色调旋转 30 度左右。这些都是基于色彩理论的标准方法。

亮度计算使用了加权平均的方法,考虑了人眼对不同颜色的感知差异。这个公式在图像处理和 UI 设计中广泛使用。

JavaScript 实现

编译后的 JavaScript 代码

// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)

/**
 * ColorProcessor 类的 JavaScript 版本
 * 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
 */
class ColorProcessor {
  /**
   * HEX 颜色转 RGB
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {number[]} RGB 颜色数组
   */
  hexToRgb(hexColor) {
    const hex = hexColor.replace('#', '');
    if (hex.length === 6) {
      return [
        parseInt(hex.substring(0, 2), 16),
        parseInt(hex.substring(2, 4), 16),
        parseInt(hex.substring(4, 6), 16)
      ];
    }
    return [0, 0, 0];
  }

  /**
   * RGB 颜色转 HEX
   * @param {number} r - 红色分量
   * @param {number} g - 绿色分量
   * @param {number} b - 蓝色分量
   * @returns {string} HEX 颜色字符串
   */
  rgbToHex(r, g, b) {
    const toHex = (n) => {
      const hex = n.toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    };
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
  }

  /**
   * RGB 颜色转 HSL
   * @param {number} r - 红色分量
   * @param {number} g - 绿色分量
   * @param {number} b - 蓝色分量
   * @returns {number[]} HSL 颜色数组
   */
  rgbToHsl(r, g, b) {
    const rNorm = r / 255;
    const gNorm = g / 255;
    const bNorm = b / 255;

    const max = Math.max(rNorm, gNorm, bNorm);
    const min = Math.min(rNorm, gNorm, bNorm);
    const l = (max + min) / 2;

    let h = 0;
    if (max === min) {
      h = 0;
    } else if (max === rNorm) {
      h = (60 * ((gNorm - bNorm) / (max - min)) + 360) % 360;
    } else if (max === gNorm) {
      h = (60 * ((bNorm - rNorm) / (max - min)) + 120) % 360;
    } else {
      h = (60 * ((rNorm - gNorm) / (max - min)) + 240) % 360;
    }

    let s = 0;
    if (max === min) {
      s = 0;
    } else if (l < 0.5) {
      s = (max - min) / (max + min);
    } else {
      s = (max - min) / (2 - max - min);
    }

    return [h, s * 100, l * 100];
  }

  /**
   * HSL 颜色转 RGB
   * @param {number} h - 色调
   * @param {number} s - 饱和度
   * @param {number} l - 亮度
   * @returns {number[]} RGB 颜色数组
   */
  hslToRgb(h, s, l) {
    const sNorm = s / 100;
    const lNorm = l / 100;

    const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
    const x = c * (1 - Math.abs((h / 60) % 2 - 1));
    const m = lNorm - c / 2;

    let rPrime = 0, gPrime = 0, bPrime = 0;

    if (h < 60) {
      rPrime = c; gPrime = x; bPrime = 0;
    } else if (h < 120) {
      rPrime = x; gPrime = c; bPrime = 0;
    } else if (h < 180) {
      rPrime = 0; gPrime = c; bPrime = x;
    } else if (h < 240) {
      rPrime = 0; gPrime = x; bPrime = c;
    } else if (h < 300) {
      rPrime = x; gPrime = 0; bPrime = c;
    } else {
      rPrime = c; gPrime = 0; bPrime = x;
    }

    return [
      Math.round((rPrime + m) * 255),
      Math.round((gPrime + m) * 255),
      Math.round((bPrime + m) * 255)
    ];
  }

  /**
   * 调整颜色亮度
   * @param {string} hexColor - HEX 颜色字符串
   * @param {number} percent - 亮度调整百分比
   * @returns {string} 调整后的 HEX 颜色
   */
  adjustBrightness(hexColor, percent) {
    const rgb = this.hexToRgb(hexColor);
    const hsl = this.rgbToHsl(rgb[0], rgb[1], rgb[2]);

    const newL = Math.max(0, Math.min(100, hsl[2] + percent));
    const newRgb = this.hslToRgb(hsl[0], hsl[1], newL);

    return this.rgbToHex(newRgb[0], newRgb[1], newRgb[2]);
  }

  /**
   * 调整颜色饱和度
   * @param {string} hexColor - HEX 颜色字符串
   * @param {number} percent - 饱和度调整百分比
   * @returns {string} 调整后的 HEX 颜色
   */
  adjustSaturation(hexColor, percent) {
    const rgb = this.hexToRgb(hexColor);
    const hsl = this.rgbToHsl(rgb[0], rgb[1], rgb[2]);

    const newS = Math.max(0, Math.min(100, hsl[1] + percent));
    const newRgb = this.hslToRgb(hsl[0], newS, hsl[2]);

    return this.rgbToHex(newRgb[0], newRgb[1], newRgb[2]);
  }

  /**
   * 混合两种颜色
   * @param {string} color1 - 第一种颜色
   * @param {string} color2 - 第二种颜色
   * @param {number} ratio - 混合比例
   * @returns {string} 混合后的颜色
   */
  blendColors(color1, color2, ratio) {
    const rgb1 = this.hexToRgb(color1);
    const rgb2 = this.hexToRgb(color2);

    const r = Math.round(rgb1[0] * (1 - ratio) + rgb2[0] * ratio);
    const g = Math.round(rgb1[1] * (1 - ratio) + rgb2[1] * ratio);
    const b = Math.round(rgb1[2] * (1 - ratio) + rgb2[2] * ratio);

    return this.rgbToHex(r, g, b);
  }

  /**
   * 获取互补色
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {string} 互补色
   */
  getComplementaryColor(hexColor) {
    const rgb = this.hexToRgb(hexColor);
    const hsl = this.rgbToHsl(rgb[0], rgb[1], rgb[2]);

    const complementaryH = (hsl[0] + 180) % 360;
    const newRgb = this.hslToRgb(complementaryH, hsl[1], hsl[2]);

    return this.rgbToHex(newRgb[0], newRgb[1], newRgb[2]);
  }

  /**
   * 获取类似色
   * @param {string} hexColor - HEX 颜色字符串
   * @param {number} angle - 角度偏移
   * @returns {string[]} 类似色列表
   */
  getAnalogousColors(hexColor, angle = 30) {
    const rgb = this.hexToRgb(hexColor);
    const hsl = this.rgbToHsl(rgb[0], rgb[1], rgb[2]);

    const color1H = (hsl[0] - angle + 360) % 360;
    const color2H = (hsl[0] + angle) % 360;

    const color1Rgb = this.hslToRgb(color1H, hsl[1], hsl[2]);
    const color2Rgb = this.hslToRgb(color2H, hsl[1], hsl[2]);

    return [
      this.rgbToHex(color1Rgb[0], color1Rgb[1], color1Rgb[2]),
      hexColor,
      this.rgbToHex(color2Rgb[0], color2Rgb[1], color2Rgb[2])
    ];
  }

  /**
   * 验证 HEX 颜色
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {boolean} 是否有效
   */
  isValidHexColor(hexColor) {
    const hex = hexColor.replace('#', '');
    return hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex);
  }

  /**
   * 获取颜色的亮度值
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {number} 亮度值
   */
  getLuminance(hexColor) {
    const rgb = this.hexToRgb(hexColor);
    return Math.round(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]);
  }

  /**
   * 判断颜色是否为浅色
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {boolean} 是否为浅色
   */
  isLightColor(hexColor) {
    return this.getLuminance(hexColor) > 128;
  }

  /**
   * 获取对比色
   * @param {string} hexColor - HEX 颜色字符串
   * @returns {string} 对比色
   */
  getContrastColor(hexColor) {
    return this.isLightColor(hexColor) ? '#000000' : '#FFFFFF';
  }
}

JavaScript 实现的特点

JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的 Math 对象提供了必要的数学函数。正则表达式用于验证 HEX 颜色的有效性,这是 JavaScript 中的标准方法。

ArkTS 调用代码

OpenHarmony 应用集成

// 文件: kmp_ceshiapp/entry/src/main/ets/pages/ColorProcessorPage.ets

import { ColorProcessor } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';

@Entry
@Component
struct ColorProcessorPage {
  @State selectedOperation: string = 'convert';
  @State inputColor: string = '#FF5733';
  @State result: string = '';
  @State resultTitle: string = '';
  @State resultColor: string = '#FF5733';
  @State parameter: string = '';

  private colorProcessor = new ColorProcessor();

  private operations = [
    { name: '格式转换', value: 'convert' },
    { name: '调整亮度', value: 'brightness' },
    { name: '调整饱和度', value: 'saturation' },
    { name: '混合颜色', value: 'blend' },
    { name: '互补色', value: 'complementary' },
    { name: '类似色', value: 'analogous' },
    { name: '亮度检测', value: 'luminance' },
    { name: '对比色', value: 'contrast' }
  ];

  build() {
    Column() {
      // 标题
      Text('🎨 颜色工具库')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
        .width('100%')
        .padding(20)
        .backgroundColor('#1A237E')
        .textAlign(TextAlign.Center)

      Scroll() {
        Column() {
          // 操作选择
          Column() {
            Text('选择操作')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#333333')
              .margin({ bottom: 12 })

            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(this.operations, (op: { name: string; value: string }) => {
                Button(op.name)
                  .layoutWeight(1)
                  .height(40)
                  .margin({ right: 8, bottom: 8 })
                  .backgroundColor(this.selectedOperation === op.value ? '#1A237E' : '#E0E0E0')
                  .fontColor(this.selectedOperation === op.value ? '#FFFFFF' : '#333333')
                  .fontSize(12)
                  .onClick(() => {
                    this.selectedOperation = op.value;
                    this.result = '';
                    this.resultTitle = '';
                  })
              })
            }
            .width('100%')
          }
          .width('95%')
          .margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
          .padding(12)
          .backgroundColor('#FFFFFF')
          .borderRadius(6)

          // 颜色选择
          Column() {
            Text('选择颜色')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#333333')
              .margin({ bottom: 8 })

            Row() {
              Column()
                .width(60)
                .height(60)
                .backgroundColor(this.inputColor)
                .borderRadius(6)
                .border({ width: 2, color: '#4DB6AC' })

              TextInput({ placeholder: '输入 HEX 颜色', text: this.inputColor })
                .onChange((value) => {
                  if (this.colorProcessor.isValidHexColor(value)) {
                    this.inputColor = value;
                  }
                })
                .width('100%')
                .height(50)
                .padding(12)
                .border({ width: 1, color: '#4DB6AC' })
                .borderRadius(6)
                .fontSize(12)
                .margin({ left: 12 })
            }
            .width('100%')
            .alignItems(VerticalAlign.Center)

            if (this.selectedOperation === 'brightness' || this.selectedOperation === 'saturation' || 
                this.selectedOperation === 'blend') {
              TextInput({ placeholder: '输入参数(-100 到 100)', text: this.parameter })
                .onChange((value) => this.parameter = value)
                .width('100%')
                .height(50)
                .padding(12)
                .border({ width: 1, color: '#4DB6AC' })
                .borderRadius(6)
                .fontSize(12)
                .margin({ top: 12 })
            }
          }
          .width('95%')
          .margin({ left: '2.5%', right: '2.5%', bottom: 16 })
          .padding(12)
          .backgroundColor('#FFFFFF')
          .borderRadius(6)

          // 操作按钮
          Row() {
            Button('✨ 处理')
              .layoutWeight(1)
              .height(44)
              .backgroundColor('#1A237E')
              .fontColor('#FFFFFF')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .borderRadius(6)
              .onClick(() => this.executeOperation())

            Blank()
              .width(12)

            Button('🔄 清空')
              .layoutWeight(1)
              .height(44)
              .backgroundColor('#F5F5F5')
              .fontColor('#1A237E')
              .fontSize(14)
              .border({ width: 1, color: '#4DB6AC' })
              .borderRadius(6)
              .onClick(() => {
                this.result = '';
                this.resultTitle = '';
              })
          }
          .width('95%')
          .margin({ left: '2.5%', right: '2.5%', bottom: 16 })

          // 结果显示
          if (this.resultTitle) {
            Column() {
              Text(this.resultTitle)
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .fontColor('#FFFFFF')
                .width('100%')
                .padding(12)
                .backgroundColor('#1A237E')
                .borderRadius(6)
                .textAlign(TextAlign.Center)
                .margin({ bottom: 12 })

              if (this.resultColor && this.resultColor !== this.inputColor) {
                Row() {
                  Column()
                    .width(80)
                    .height(80)
                    .backgroundColor(this.resultColor)
                    .borderRadius(6)
                    .border({ width: 2, color: '#4DB6AC' })

                  Column() {
                    Text(this.result)
                      .fontSize(12)
                      .fontColor('#333333')
                      .width('100%')
                      .padding(12)
                  }
                  .layoutWeight(1)
                  .margin({ left: 12 })
                }
                .width('100%')
              } else {
                Scroll() {
                  Text(this.result)
                    .fontSize(12)
                    .fontColor('#333333')
                    .fontFamily('monospace')
                    .textAlign(TextAlign.Start)
                    .width('100%')
                    .padding(12)
                }
                .width('100%')
                .height(250)
              }
            }
            .width('95%')
            .margin({ left: '2.5%', right: '2.5%', bottom: 16 })
            .padding(12)
            .backgroundColor('#FFFFFF')
            .borderRadius(6)
          }
        }
        .width('100%')
      }
      .layoutWeight(1)
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  private executeOperation() {
    if (!this.colorProcessor.isValidHexColor(this.inputColor)) {
      this.resultTitle = '❌ 错误';
      this.result = '请输入有效的 HEX 颜色';
      return;
    }

    try {
      switch (this.selectedOperation) {
        case 'convert':
          const rgb = this.colorProcessor.hexToRgb(this.inputColor);
          const hsl = this.colorProcessor.rgbToHsl(rgb[0], rgb[1], rgb[2]);
          this.resultTitle = '🔄 格式转换结果';
          this.result = `HEX: ${this.inputColor}\nRGB: rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})\nHSL: hsl(${Math.round(hsl[0])}, ${Math.round(hsl[1])}%, ${Math.round(hsl[2])}%)`;
          this.resultColor = this.inputColor;
          break;

        case 'brightness':
          const percent = parseInt(this.parameter) || 0;
          const brightColor = this.colorProcessor.adjustBrightness(this.inputColor, percent);
          this.resultTitle = `💡 亮度调整 ${percent > 0 ? '+' : ''}${percent}%`;
          this.result = `原色: ${this.inputColor}\n新色: ${brightColor}`;
          this.resultColor = brightColor;
          break;

        case 'saturation':
          const satPercent = parseInt(this.parameter) || 0;
          const satColor = this.colorProcessor.adjustSaturation(this.inputColor, satPercent);
          this.resultTitle = `🎨 饱和度调整 ${satPercent > 0 ? '+' : ''}${satPercent}%`;
          this.result = `原色: ${this.inputColor}\n新色: ${satColor}`;
          this.resultColor = satColor;
          break;

        case 'blend':
          const ratio = (parseInt(this.parameter) || 50) / 100;
          const blendColor = this.colorProcessor.blendColors(this.inputColor, '#FFFFFF', ratio);
          this.resultTitle = '🎭 颜色混合';
          this.result = `混合比例: ${Math.round(ratio * 100)}%\n混合后: ${blendColor}`;
          this.resultColor = blendColor;
          break;

        case 'complementary':
          const compColor = this.colorProcessor.getComplementaryColor(this.inputColor);
          this.resultTitle = '🔄 互补色';
          this.result = `原色: ${this.inputColor}\n互补色: ${compColor}`;
          this.resultColor = compColor;
          break;

        case 'analogous':
          const analogous = this.colorProcessor.getAnalogousColors(this.inputColor);
          this.resultTitle = '🎨 类似色';
          this.result = `颜色1: ${analogous[0]}\n颜色2: ${analogous[1]}\n颜色3: ${analogous[2]}`;
          this.resultColor = analogous[1];
          break;

        case 'luminance':
          const luminance = this.colorProcessor.getLuminance(this.inputColor);
          const isLight = this.colorProcessor.isLightColor(this.inputColor);
          this.resultTitle = '📊 亮度检测';
          this.result = `亮度值: ${luminance}\n颜色类型: ${isLight ? '浅色' : '深色'}`;
          this.resultColor = this.inputColor;
          break;

        case 'contrast':
          const contrastColor = this.colorProcessor.getContrastColor(this.inputColor);
          this.resultTitle = '⚫⚪ 对比色';
          this.result = `原色: ${this.inputColor}\n对比色: ${contrastColor}`;
          this.resultColor = contrastColor;
          break;
      }
    } catch (e) {
      this.resultTitle = '❌ 处理出错';
      this.result = `错误: ${e}`;
    }
  }
}

ArkTS 集成的关键要点

在 OpenHarmony 应用中集成颜色工具库需要考虑颜色的可视化展示。我们设计了一个灵活的 UI,能够直观地显示颜色处理的结果。

颜色选择界面使用了一个颜色预览框,用户可以直观地看到当前选择的颜色。TextInput 用于输入 HEX 颜色值,并进行实时验证。

结果显示根据操作类型动态调整。对于产生新颜色的操作(如亮度调整、互补色等),我们显示一个颜色预览框。对于返回文本信息的操作(如格式转换、亮度检测等),我们显示可滚动的文本区域。

工作流程详解

颜色处理的完整流程

  1. 操作选择: 用户在 ArkTS UI 中选择要执行的颜色操作
  2. 颜色输入: 用户输入或选择要处理的颜色
  3. 参数输入: 根据选择的操作输入必要的参数
  4. 处理执行: 调用 ColorProcessor 的相应方法
  5. 结果展示: 将处理结果显示在 UI 中,包括颜色预览和详细信息

跨平台一致性

通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,颜色处理的逻辑和结果都是完全相同的。

实际应用场景

UI 设计工具

在 UI 设计工具中,需要进行颜色的调整、混合和分析。这个工具库提供了所有必要的颜色处理功能。

数据可视化应用

在数据可视化应用中,需要生成配色方案和调整颜色的亮度。这个工具库提供了这些功能。

图像编辑应用

在图像编辑应用中,需要进行颜色的转换和调整。这个工具库提供了基础的颜色处理能力。

主题定制应用

在支持主题定制的应用中,需要生成互补色和类似色。这个工具库提供了这些功能。

性能优化

缓存颜色转换结果

在频繁进行颜色转换时,可以缓存转换结果以避免重复计算。

批量处理

在处理大量颜色时,应该考虑使用批量处理的方式以提高效率。

安全性考虑

输入验证

在处理用户输入的颜色值时,应该进行验证,确保颜色值的有效性。

精度处理

在进行浮点数计算时,应该考虑精度问题,避免出现舍入误差。

总结

这个 KMP OpenHarmony 颜色工具库展示了如何使用现代的跨平台技术来处理常见的颜色操作任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。

颜色处理是应用开发中的重要技能。通过使用这样的工具库,开发者可以快速、可靠地处理各种颜色操作,从而提高开发效率和代码质量。

在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的颜色空间支持、实现高级的颜色匹配算法等高级特性。同时,定期进行测试和验证,确保颜色处理的准确性和一致性。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐