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 渲染等核心知识点。但如果要上架应用商店还需要补充图片取色、颜色历史、导出色板、色盲友好、渐变生成等内容。

相关新闻

GitHub Copilot SDK Rust系统级集成:安全高效的AI代理实现

GitHub Copilot SDK Rust系统级集成:安全高效的AI代理实现

GitHub Copilot SDK Rust系统级集成:安全高效的AI代理实现 【免费下载链接】copilot-sdk Multi-platform SDK for integrating GitHub Copilot Agent into apps and services 项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk GitHub Copilot …

2026/7/22 1:25:12阅读更多 →
鸿蒙 ArkTS 实战:Volunteer Photo Team 从志愿摄影团队到社群活动工具完整解析

鸿蒙 ArkTS 实战:Volunteer Photo Team 从志愿摄影团队到社群活动工具完整解析

鸿蒙 ArkTS 实战:Volunteer Photo Team 从志愿摄影团队到社群活动工具完整解析 前言 Volunteer Photo Team 是一个基于鸿蒙 ArkTS 与 ArkUI 声明式范式实现的单页应用,主题聚焦 公益活动摄影协作。 它把 维护活动报名、拍摄任务、交付状态、归档照片数…

2026/7/20 20:38:34阅读更多 →
鸿蒙 ArkTS 实战:Movie Club Screening 从电影社放映到兴趣社群工具完整解析

鸿蒙 ArkTS 实战:Movie Club Screening 从电影社放映到兴趣社群工具完整解析

鸿蒙 ArkTS 实战:Movie Club Screening 从电影社放映到兴趣社群工具完整解析 前言 Movie Club Screening 是一个围绕 电影社群放映 设计的鸿蒙 ArkTS 单页应用。 它把 维护影片投票、放映时间、讨论主题、评分和投票数量 这类真实活动需求,拆成状态字…

2026/7/20 20:38:34阅读更多 →
RocketMQ NameServer架构设计与实现原理

RocketMQ NameServer架构设计与实现原理

1. RocketMQ NameServer核心定位解析在分布式消息中间件领域,NameServer堪称RocketMQ的"中枢神经系统"。与常见的ZooKeeper等注册中心不同,NameServer采用去中心化设计,每个节点都是独立运行的个体,彼此间不进行数据同步…

2026/7/22 2:20:09阅读更多 →
2026最新5款基础版免费AI编程工具深度实测

2026最新5款基础版免费AI编程工具深度实测

花了两个周末,我把主流的几款 AI 编程工具挨个装了一遍,同一个项目用不同的工具写,记录下了各自的真实表现。作为一个全栈独立开发者,我最近在开发一款企业项目管理SaaS平台,对AI编程工具的需求就是能稳定提高开发效率…

2026/7/22 2:20:09阅读更多 →
ROS多线程订阅问题解决方案与性能优化

ROS多线程订阅问题解决方案与性能优化

1. ROS多线程订阅问题深度解析在机器人操作系统(ROS)开发中,多线程订阅是个让不少开发者头疼的典型问题。上周调试一个多传感器融合项目时,我就遇到了消息丢失的诡异情况——三个激光雷达的数据在回调函数里互相打架,最终导致建图出现断层。这…

2026/7/22 2:20:09阅读更多 →
AI论文写作工具测评:核心技术对比与学术场景应用

AI论文写作工具测评:核心技术对比与学术场景应用

1. 项目概述:AI论文写作工具的崛起与测评价值去年实验室新来的博士生小张用了某款AI写作工具,三天就完成了文献综述初稿,而传统方式至少需要两周。这个案例让我意识到,AI论文写作工具已经从概念验证阶段进入实际生产力提升阶段。2…

2026/7/22 2:20:09阅读更多 →
SAP系统核心功能与高频业务操作实战解析

SAP系统核心功能与高频业务操作实战解析

1. SAP系统核心功能与应用场景解析SAP作为全球领先的企业管理软件,其核心价值在于整合企业资源计划(ERP)各个模块的数据流与业务流程。我在制造业实施SAP的十年间,见证了从ECC到SANA的转型过程,最深刻的体会是&#xf…

2026/7/22 2:20:09阅读更多 →
Golang构建Kafka到ElasticSearch的高效日志管道实践

Golang构建Kafka到ElasticSearch的高效日志管道实践

1. 为什么选择Golang处理Kafka到ElasticSearch的日志管道在构建日志处理系统时,技术选型往往决定了后期维护成本和系统稳定性。Golang的并发模型与Kafka的高吞吐特性形成绝佳组合——每个Kafka分区可以由独立的goroutine消费,通过channel实现零拷贝数据传…

2026/7/22 2:18:09阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/21 18:53:30阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/21 18:53:30阅读更多 →