HarmonyOS APP实战-画图APP - 第9篇智能取色与色板生成1. 开篇在上一篇中我们成功集成了ohos.effectKit为画布内容实现了亮度调节、模糊化和灰度调节三大图像处理功能。用户可以在绘制完成后通过底部工具栏的“效果”按钮调出调节面板实时预览并应用滤镜效果。这使得画图APP具备了基础的图像后期能力。本篇将在此基础上进一步挖掘ohos.effectKit的潜力——智能取色。我们将实现一个“智能取色器”用户可以在画布上框选任意区域APP自动提取该区域的主色并生成一个包含主色及其相近色的5色色板。提取的颜色可以直接设置为当前画笔颜色或填充颜色。这是画图APP中非常实用的功能尤其在参考图片配色、制作色板场景中不可或缺。2. 核心实现2.1 基础配置 — 导入模块与类型定义首先我们需要导入ohos.effectKit中与取色相关的模块。从文档可知effectKit提供ColorPicker类用于智能取色。// utils/ColorUtils.tsimport{image}fromkit.ImageKit;import{effectKit}fromkit.ArkGraphics2D;/** * 提取自 ohos.effectKit 文档 * ColorPicker 用于从 PixelMap 中提取主色 */exportinterfaceExtractColorResult{mainColor:string;// 主色格式 #AARRGGBBpalette:string[];// 色板5个相近色包含主色}exportclassColorUtils{/** * 从图片中提取主色与色板 * param pixelMap 待分析的 PixelMap * returns 提取结果 */staticasyncextractColors(pixelMap:image.PixelMap):PromiseExtractColorResult{// 实际取色逻辑在核心实现中thrownewError(Not implemented);}}关键点说明effectKit的ColorPicker构造需要传入PixelMap参数因此我们需要确保调用侧传入的是有效的 PixelMap。取色结果包含主色和预生成的调色板我们将其封装为ExtractColorResult接口便于后续使用。当前仅定义接口和类壳核心实现在下一节完成。2.2 核心逻辑 — 实现智能取色本节实现extractColors方法使用effectKit的ColorPicker完成取色并手动生成5色调色板主色 4个亮度/饱和度渐变。// utils/ColorUtils.tsimport{image}fromkit.ImageKit;import{effectKit}fromkit.ArkGraphics2D;exportinterfaceExtractColorResult{mainColor:string;// 主色格式 #AARRGGBBpalette:string[];// 色板包含主色 4个相近色共5个}exportclassColorUtils{/** * 从图片中提取主色与色板 * param pixelMap 待分析的 PixelMap * returns 提取结果 */staticasyncextractColors(pixelMap:image.PixelMap):PromiseExtractColorResult{// 1. 创建 ColorPicker 实例// 文档 API: effectKit.createColorPicker(pixelMap)constcolorPickerawaiteffectKit.createColorPicker(pixelMap);// 2. 获取主色// ColorPicker 的 getMainColorSync() 返回 Color 对象constmainColorObjcolorPicker.getMainColorSync();// 3. 将 Color 对象转换为 #AARRGGBB 格式字符串// Color 的 alpha、red、green、blue 属性范围 0-255constmainColorthis.colorToString(mainColorObj);// 4. 生成调色板// 基于主色通过 HSL 变换生成 4 个相近色constpalettethis.generatePalette(mainColorObj,4);return{mainColor:mainColor,palette:[mainColor,...palette]};}/** * 将 Color 对象转为 #AARRGGBB 字符串 */staticcolorToString(color:effectKit.Color):string{constacolor.alpha.toString(16).padStart(2,0);constrcolor.red.toString(16).padStart(2,0);constgcolor.green.toString(16).padStart(2,0);constbcolor.blue.toString(16).padStart(2,0);return#${a}${r}${g}${b}.toUpperCase();}/** * 基于主色生成相似色板 * param baseColor 主色 Color * param count 生成颜色数量 * returns 颜色字符串数组 */staticgeneratePalette(baseColor:effectKit.Color,count:number):string[]{constpalette:string[][];// 将 RGB 转为 HSL 进行变换// 这里简化实现等比例调整亮度和饱和度consththis.rgbToHsl(baseColor.red,baseColor.green,baseColor.blue)[0];consts0.7;// 固定饱和度 0.7constlStep0.15;// 亮度步长for(leti1;icount;i){// 亮度分别在主色亮度基础上 ± 步长constld(i2)?-(i*lStep):(i-2)*lStep;constlMath.max(0,Math.min(1,0.5ld));// 限制在 0~1// 同时轻微调整色相使颜色有变化但不偏离太远consthd(i%20)?10:-10;constnewH(hhd360)%360;constrgbthis.hslToRgb(newH,s,l);constcolor#FF${rgb[0].toString(16).padStart(2,0)}${rgb[1].toString(16).padStart(2,0)}${rgb[2].toString(16).padStart(2,0)};palette.push(color.toUpperCase());}returnpalette;}// RGB ↔ HSL 转换辅助函数省略具体实现标准算法staticrgbToHsl(r:number,g:number,b:number):[number,number,number]{...}statichslToRgb(h:number,s:number,l:number):[number,number,number]{...}}关键点说明文档明确指出effectKit.createColorPicker(pixelMap)创建ColorPicker实例getMainColorSync()同步获取主色。Color对象包含alpha、red、green、blue四个分量均为number类型范围 0-255。生成调色板时我们采用 HSL 变换策略使色板既包含主色又有合理的色相偏移和亮度变化符合设计工具中“相似色”的定义。实际项目中可根据需要增加饱和度扰动生成更丰富的调色板。2.3 完整模块 — SmartColorPicker 组件我们将智能取色封装为一个完整的 ArkUI 组件集成到画图APP的主界面中。用户点击“取色”按钮后显示取色面板在画布上拖拽选择区域松开后提取主色并显示色板。// components/SmartColorPicker.etsimport{image}fromkit.ImageKit;import{effectKit}fromkit.ArkGraphics2D;import{ColorUtils,ExtractColorResult}from../utils/ColorUtils;Componentstruct SmartColorPicker{// 接收外部画布的 PixelMapprivatecanvasPixelMap?:image.PixelMap;// 取色结果StateprivateextractResult?:ExtractColorResult;StateprivateselectedColor:string#FF000000;// 默认黑色// 取色状态StateprivateisPicking:booleanfalse;privatestartX:number0;privatestartY:number0;privateendX:number0;privateendY:number0;// 回调选择颜色后应用到画笔privateonColorSelected:(color:string)void;build(){Column(){// 取色按钮Button(智能取色).onClick((){this.isPickingtrue;// 取色前需确保画布有内容且已转换为 PixelMapif(this.canvasPixelMap){this.startPicking();}}).margin({bottom:10})// 取色结果展示if(this.extractResult){Text(主色this.extractResult.mainColor).fontSize(14).margin({bottom:8})// 色板展示Row({space:6}){ForEach(this.extractResult.palette,(color:string){Column(){// 颜色块Stack().width(40).height(40).backgroundColor(color).border({width:1,color:#999}).borderRadius(4).onClick((){this.selectedColorcolor;// 通知父组件应用颜色this.onColorSelected?.(color);})// 颜色值小字Text(color.length7?color.substring(0,7)...:color).fontSize(10).margin({top:2})}})}.padding(10).border({width:1,color:#ccc}).borderRadius(8)}// 当前选中颜色指示if(this.selectedColor!#FF000000){Row(){Text(当前画笔颜色)Stack().width(20).height(20).backgroundColor(this.selectedColor).border({width:1,color:#666}).margin({left:8})}.margin({top:10})}}.padding(12)}/** * 开始智能取色流程 * 实际应用中需要用户选择区域这里简化取整个画布的主色 */asyncstartPicking(){try{// 将 PixalMap 传给取色工具constresultawaitColorUtils.extractColors(this.canPixelMap!);this.extractResultresult;this.isPickingfalse;}catch(error){console.error(取色失败:,error);this.isPickingfalse;}}/** * 从画布组件获取 PixelMap * 由外部调用传入 Canvas 组件的 PixelMap */publicsetCanvasPixelMap(pixelMap:image.PixelMap){this.canvasPixelMappixelMap;}}关键点说明startPicking方法调用ColorUtils.extractColors完成取色结果存储在State变量中驱动UI更新。色板展示部分使用ForEach遍历palette数组每个颜色块可点击点击后触发onColorSelected回调通知父组件。实际项目中取色区域应通过触摸事件限制为画布上的矩形区域。这里为聚焦于effectKit使用简化取色范围为整个画布。后续可结合触摸事件实现区域取色。2.4 集成到主页面在主页面Index.ets中引用SmartColorPicker组件并建立画布 PixelMap 的传递通道。// pages/Index.etsimport{image}fromkit.ImageKit;importSmartColorPickerfrom../components/SmartColorPicker;EntryComponentstruct Index{StateprivatecanvasPixelMap?:image.PixelMap;privatesmartColorPickerRef:SmartColorPicker|nullnull;build(){Column(){// ... 工具栏、画布等已有代码 ...// 智能取色面板SmartColorPicker({onColorSelected:(color:string){// 将选中的颜色设置到全局画笔属性AppStorage.setOrCreate(drawColor,color);}}).onAttach((component:SmartColorPicker){this.smartColorPickerRefcomponent;})// ... 底部颜色栏等 ...}}/** * 在画布绘制完成或切换时获取当前画布的 PixelMap * 通过 Canvas 组件 getPixelMap API 获取 */asyncupdateCanvasPixelMap(){// 假设 canvasContext 是已绑定的 RenderingContextconstpixelMapawaitthis.canvasContext.getPixelMap(0,0,this.canvasWidth,this.canvasHeight);this.canvasPixelMappixelMap;this.smartColorPickerRef?.setCanvasPixelMap(pixelMap);}}关键点说明通过AppStorage将选中的颜色传递给画图模块实现颜色统一管理。canvasContext.getPixelMap是 Canvas 组件提供的方法用于截取当前画布内容为 PixelMap传入取色组件。在实际应用场景中建议在每次画布内容变化后绘制、擦除、效果应用等调用updateCanvasPixelMap刷新 PixelMap保证取色基于最新画布。3. 运行验证完成上述代码后在 DevEco Studio 中运行应用在主界面上绘制一些彩色图形如红色圆形、蓝色矩形。点击底部的“智能取色”按钮触发取色流程。观察控制台日志确认ColorPicker.getMainColorSync成功返回主色对象。查看页面上方出现色板区域包含主色和4个相近色块。点击色板中的任意颜色块底部颜色指示更新后续绘制线条变为所选颜色。预期效果色板应该显示画布中最具代表性的颜色主色以及基于主色生成的和谐色板。点击色板后画笔颜色立即切换。4. 小结与预告本篇实现了画图APP的智能取色与色板生成功能。核心是使用ohos.effectKit的ColorPicker类提取图像主色并通过 HSL 变换算法生成5色调色板。提取的颜色可以一键应用到画笔大大提升了配色效率。智能取色是直接服务于用户创作流程的功能让画图APP从单纯的绘图工具升级为具备色彩分析能力的设计工具。下一篇将开启一个新的技术维度——色彩空间转换与HDR支持。我们将集成ohos.graphics.colorSpaceManager实现 sRGB、Display P3、Adobe RGB 等色彩空间间的转换并将画图APP的输出适配到支持 HDR 的屏幕上。这会让用户的画作在高端设备上呈现更鲜艳、更准确的颜色。敬请期待。