尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
HarmonyOS ArkTS 颜色提取器的色彩算法与 Builder 复用
引子取色不难配色才难颜色提取器有两件事一是让用户手动调 RGB 滑块选颜色二是根据当前颜色自动生成配色方案。前者是交互问题后者是算法问题。手动调滑块谁都会做但配色方案涉及色彩理论——互补色、类似色、三等分色每个概念背后都是 HSL 色彩模型的数学计算。今天就从代码出发拆解 RGB 和 HSL 的互转、配色方案的生成逻辑、Builder 的组件复用。完整效果数据模型三个 State 管理颜色Statered:number175;Stategreen:number82;Stateblue:number222;Statehex:string#AF52DE;StateextractedColors:string[][];Stateharmonies:string[][];为什么 RGB 和 hex 都要存RGB 用于滑块显示Slider的值是 0-255hex 用于显示和存储用户看到的是#AF52DE。两者是同一个颜色的不同表示需要同步更新。extractedColors 和 harmonies 的区别extractedColors随机色盘生成的 5 个颜色harmonies基于当前颜色的配色方案互补、类似、三等分两者独立计算互不影响。RGB 转 hex进制转换updateHex():void{constrthis.red.toString(16).padStart(2,0).toUpperCase();constgthis.green.toString(16).padStart(2,0).toUpperCase();constbthis.blue.toString(16).padStart(2,0).toUpperCase();this.hex#rgb;this.genHarmonies();}toString(16) 的作用把数字转成 16 进制字符串。比如175→af82→52222→de。padStart(2, ‘0’) 的作用如果 16 进制只有一位比如10→a前面补 0 变成0a。确保每个颜色分量都是两位。toUpperCase() 的作用16 进制默认小写af52de转成大写AF52DE更符合设计规范。为什么每次 updateHex 都要 genHarmoniesRGB 变了hex 就变了配色方案也要重新计算。保证颜色和配色方案始终同步。RGB 转 HSL色彩模型的转换hexToHsl(hex:string):[number,number,number]{constrparseInt(hex.slice(1,3),16)/255;constgparseInt(hex.slice(3,5),16)/255;constbparseInt(hex.slice(5,7),16)/255;constmaxMath.max(r,g,b);constminMath.min(r,g,b);leth0,s0,l(maxmin)/2;if(max!min){constdmax-min;sl0.5?d/(2-max-min):d/(maxmin);if(maxr)h((g-b)/d(gb?6:0))*60;elseif(maxg)h((b-r)/d2)*60;elseh((r-g)/d4)*60;}return[Math.round(h),Math.round(s*100),Math.round(l*100)];}什么是 HSLHSL 是另一种表示颜色的方式HHue色相0-360 度表示颜色种类0红120绿240蓝SSaturation饱和度0-100%表示颜色的鲜艳程度LLightness亮度0-100%表示颜色的明暗程度转换算法的原理先把 RGB 归一化到 0-1 范围除以 255找到最大值和最小值亮度 L (max min) / 2如果 max ≠ min不是灰色计算饱和度 S 和色相 H色相的计算色相根据最大值是 R、G 还是 B 来决定最大值是 RH ((G - B) / d) × 60最大值是 GH ((B - R) / d 2) × 60最大值是 BH ((R - G) / d 4) × 60d是最大值和最小值的差。如果 G BH 要加 360确保 H 在 0-360 范围内。饱和度的计算S l 0.5 ? d / (2 - max - min) : d / (max min)亮度高时l 0.5用d / (2 - max - min)亮度低时用d / (max min)。这样能保证 S 始终在 0-1 范围内。HSL 转 RGB逆向转换hslToHex(h:number,s:number,l:number):string{s/100;l/100;constas*Math.min(l,1-l);constf(n:number){constk(nh/30)%12;returnMath.round((l-a*Math.max(-1,Math.min(k-3,9-k,1)))*255);};return#[f(0),f(8),f(4)].map(vv.toString(16).padStart(2,0).toUpperCase()).join();}算法的核心这个算法比 RGB→HSL 简洁得多。核心公式k (n h/30) % 12 RGB l - a × max(-1, min(k-3, 9-k, 1))其中a s × min(l, 1-l)。为什么用 f(0)、f(8)、f(4)这三个值分别对应 R、G、B 三个通道。通过不同的n值让三个通道在色轮的不同位置取值实现颜色的转换。为什么不直接用 RGB 公式标准的 RGB→HSL 转换需要 6 个 if-else 分支根据色相所在的区间。这个算法用一个公式覆盖了所有区间代码更简洁。配色方案基于色轮的数学genHarmonies():void{consthslArrthis.hexToHsl(this.hex);consthhslArr[0],shslArr[1],lhslArr[2];constharm:string[][];harm.push(this.hslToHex((h180)%360,s,l));// 互补色harm.push(this.hslToHex((h30)%360,s,l));// 类似色harm.push(this.hslToHex((h60)%360,s,l));// 类似色harm.push(this.hslToHex((h120)%360,s,l));// 三等分harm.push(this.hslToHex((h240)%360,s,l));// 三等分this.harmoniesharm;}五种配色方案的原理方案色相偏移原理互补色180°色轮对面的颜色对比最强类似色30°色轮相邻的颜色和谐自然类似色60°稍远的相邻色有变化但不冲突三等分120°色轮三分之一处平衡感强三等分240°色轮三分之二处和三等分对称饱和度和亮度保持不变所有配色方案都保持原色的 S 和 L 不变只改变 H。这样生成的颜色在视觉上是同一色调的不同方向不会出现一个鲜艳一个灰暗的不协调。取余 360 的作用色相是 0-360 度的循环。偏移后可能超过 360比如 h350偏移 30 度变成 380取余后回到 20 度。随机色盘基于色相的随机生成pickPalette():void{constbaseHMath.floor(Math.random()*360);constcols:string[][];cols.push(this.hslToHex(baseH,70,55));cols.push(this.hslToHex((baseH30)%360,60,50));cols.push(this.hslToHex((baseH120)%360,65,55));cols.push(this.hslToHex((baseH180)%360,55,45));cols.push(this.hslToHex((baseH240)%360,70,50));this.extractedColorscols;}随机的策略不是完全随机——先随机一个基础色相0-360然后基于这个色相生成 5 个颜色。5 个颜色的色相偏移分别是 0°、30°、120°、180°、240°覆盖了色轮的主要区域。饱和度和亮度的微调每个颜色的 S 和 L 略有不同比如 70/55、60/50、65/55让色盘看起来更自然。如果所有颜色的 S 和 L 都一样会显得单调。点击色块的效果.onClick((){this.redparseInt(c.slice(1,3),16);this.greenparseInt(c.slice(3,5),16);this.blueparseInt(c.slice(5,7),16);this.updateHex();})点击随机色盘的颜色会把 RGB 滑块设成对应值用户可以在此基础上微调。Builder可复用的滑块组件BuildersliderRed(){Row(){Text(R).fontSize(12).fontWeight(FontWeight.Bold).fontColor(#FF2D55).width(20)Slider({value:this.red,min:0,max:255,style:SliderStyle.InSet}).blockColor(#FF2D55).trackColor(#2A2A3E).selectedColor(#FF2D55).onChange((v:number){this.redv;this.updateHex();}).layoutWeight(1).margin({left:10,right:10})Text(this.red.toFixed(0)).fontSize(12).fontColor(#FFFFFF).width(30).textAlign(TextAlign.End)}.width(100%).margin({bottom:14})}Builder 的作用Builder定义了一个可复用的 UI 片段。三个滑块R、G、B的结构完全一样只有颜色和绑定的变量不同。用Builder避免了三份重复代码。为什么不用 ComponentComponent是独立的组件有自己的生命周期和状态。滑块不需要独立的状态RGB 值由父组件管理用Builder更轻量。Slider 的样式SliderStyle.InSet滑块嵌入轨道内更紧凑blockColor滑块的颜色trackColor轨道的颜色深色背景selectedColor已选部分的颜色三个滑块分别用红、绿、蓝色视觉上直观。配色方案的展示ForEach(this.harmonies,(c:string,idx:number){Row(){Column().width(40).height(40).borderRadius(8).backgroundColor(c).margin({right:12})Column(){Text(this.harmonyLabel(idx)).fontSize(13).fontColor(#FFFFFF)Text(c).fontSize(11).fontColor(#888)}Blank()Button(用这个).fontSize(11).height(28).borderRadius(8).backgroundColor(#2A2A3E).fontColor(#AF52DE).onClick((){...})}.width(100%).padding({top:8,bottom:8}).border({width:{bottom:0.5},color:#333})})每行的结构左边40×40 的颜色方块中间配色方案名称 hex 值右边用这个按钮用这个按钮的功能点击后把当前配色方案的颜色设为 RGB 值用户可以在此基础上继续调整。底部分割线.border({width:{bottom:0.5}, color:#333})给每行加了 0.5px 的底部边框作为行与行之间的分隔。踩坑记录坑 1hexToHsl 的精度RGB 转 HSL 再转回 RGB可能有微小的精度误差。比如(175, 82, 222)→ HSL → hex 可能变成#AF51DE而不是#AF52DE。这是因为浮点数计算的舍入误差。坑 2padStart 的类型toString(16).padStart(2, 0)必须先toString(16)再padStart。如果顺序反了padStart会报错。坑 3Slider 的值类型Slider的onChange回调参数是number不是string。如果用字符串接收会报错。坑 4Builder 的 this 指向Builder里的this指向父组件可以直接访问this.red、this.updateHex()等。但Builder不能有自己的State。坑 5ForEach 的 keyForEach遍历配色方案时用索引idx作为隐式 key。如果数组顺序不变不会有问题。但如果数组会动态增删需要显式指定 key。代码改进建议1. 图片取色当前只有手动调滑块和随机色盘。可以加图片取色功能——用户选择图片点击某个像素获取颜色。2. 颜色历史保存最近使用的颜色方便用户回溯。3. 导出配色方案把配色方案导出为 JSON 或图片方便设计师使用。4. 色盲友好当前配色方案基于正常视觉。可以加色盲模拟功能让色弱用户也能看清。5. 渐变生成根据两个颜色生成渐变色用于 UI 设计。总结颜色提取器的核心是色彩模型的转换——RGB 用于显示和交互HSL 用于计算配色方案。配色方案基于色轮的数学关系互补、类似、三等分保持饱和度和亮度不变只改变色相。适用边界这个部分适合用作 ArkUI 颜色处理和 Builder 复用的学习案例涵盖了 RGB/HSL 互转、配色方案生成、Slider 组件、Builder 用法、ForEach 渲染等核心知识点。但如果要上架应用商店还需要补充图片取色、颜色历史、导出色板、色盲友好、渐变生成等内容。
RELATED

相关推荐

鸿蒙原生开发手记:徒步迹 - 隐式动画与属性动画

鸿蒙原生开发手记:徒步迹 - 隐式动画与属性动画

鸿蒙原生开发手记:徒步迹 - 隐式动画与属性动画 使用 animateTo 和属性动画实现流畅的 UI 动效 前言 动画是提升用户体验的关键元素。ArkUI 提供了隐式动画(animateTo)和属性动画(animation)两种方式,分别…

📅 2026/8/23 17:08:32
HarmonyOS ArkTS 二维码生成器的编码原理与 Canvas 渲染

HarmonyOS ArkTS 二维码生成器的编码原理与 Canvas 渲染

引子:二维码的黑白方块从哪来 扫码支付、加好友、打开链接——二维码每天都在用。但大多数人不知道二维码是怎么生成的:输入一段文字,输出一堆黑白方块,中间经历了什么? 这个二维码生成器是工具箱里技术含量最高的工具…

📅 2026/8/23 17:08:32
晶圆良率预测与根因分析实战:XGBoost + SHAP 全流程解析

晶圆良率预测与根因分析实战:XGBoost + SHAP 全流程解析

一、问题背景:良率是 FAB 的生命线在半导体晶圆制造(FAB)中,良率(Yield)是衡量产线健康度最核心的指标,没有之一。一片 12 英寸晶圆需要经历上千道工序、跨越光刻、刻蚀、离子注入、薄膜沉积、化…

📅 2026/8/23 17:08:32
MORE NEWS

更多资讯

📰

SPSS文件格式解析:.sav与.spv的核心区别与应用

1. 文件格式概述:.sav与.spv的前世今生 第一次接触SPSS软件的研究人员,往往会对软件生成的.sav和.spv文件产生困惑。这两种扩展名看似相似,实则承担着完全不同的使命。.sav文件是SPSS的"数据容器",存储着原始数据、变量…

📰

2025人力资源管理趋势:数字化与个性化实践

1. 2025典范雇主的人力资源管理趋势洞察 2025年的人力资源管理正在经历一场深刻的变革。随着数字化转型的深入和新生代员工成为职场主力,企业对人才管理的理念和方法都在发生根本性转变。作为从业15年的人力资源顾问,我观察到几个关键趋势正在重塑职场&a…

📰

拓扑光子学仿真:从能带计算到边界态分析

1. 项目概述:拓扑光子学仿真的核心价值十年前我第一次接触光子晶体仿真时,就被这种周期性介电材料展现出的奇异光学特性深深吸引。如今拓扑光子学作为光子晶体的进阶研究方向,正在重新定义我们对光操控的认知边界。这个项目要解决的&#xff…

📰

Python Lambda函数详解:从基础到高阶应用

1. Python Lambda函数基础解析在Python编程中,lambda函数是一种特殊的匿名函数定义方式。与常规def定义的函数不同,lambda函数没有显式的函数名,通常用于简化代码结构。我第一次接触lambda时,它给我的印象就像是一个"即用即抛…

📰

轻量级大模型全链路训练框架设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Matlab在新能源电力系统场景生成与削减中的应用实践

1. 新能源场景生成与削减的核心挑战在风电和光伏等可再生能源大规模并网的背景下,电力系统面临着前所未有的不确定性挑战。与传统火电不同,风能和太阳能的出力具有显著的间歇性和波动性特征——一片云飘过可能导致光伏电站输出功率瞬间下降30%&#xff0…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬