当前位置: 首页 > news >正文

【鸿蒙HarmonyOS Next App实战开发】​​​​ArkUI纯色图生成器

本文基于鸿蒙ArkUI框架实现了一个支持​​自定义颜色、尺寸、透明度及圆角​​的纯色图生成工具,并集成了图片保存与分享功能。以下从架构设计、核心功能与性能优化三个维度展开解析。应用商店搜索【图影工具箱】,点击“纯色图”即可查看实际效果。


​一、技术架构与设计理念​
  1. ​分层架构设计​

    • ​UI层​​:通过声明式UI构建预览区(Canvas)和参数控制区(Slider/Button)。
    • ​逻辑层​​:状态管理(@State)驱动参数实时更新,如颜色值、透明度等。
    • ​服务层​​:沙箱文件操作(CustomImageUtil)、设备能力调用(AppUtil)。
  2. ​核心依赖模块​

    import { CanvasRenderingContext2D } from '@arkui.canvas'; // Canvas绘图核心
    import { PreferencesUtil } from './Utils/PreferencesUtil'; // 本地持久化存储
    import { CustomImageUtil } from './Utils/customUtils/CustomImageUtil'; // 图片处理工具
    • ​状态管理​​:@State变量(如selectedColorcolorOpacity)绑定UI动态更新。
    • ​生命周期控制​​:aboutToAppear恢复用户设置,aboutToDisappear保存当前配置。

​二、核心功能实现解析​
​1. 动态参数控制模块​
  • ​颜色选择​​:
    通过ColorPickDialog组件实现取色器交互,点击预览区弹出对话框:

    .onClick(() => this.colorPickDialogController?.open())

    选色结果同步至Canvas渲染(drawColor()方法)。

  • ​尺寸调节​​:
    双滑动条控制宽高(40px~2000px),实时显示像素值:

    Slider({ value: this.imageWidth, min: 40, max: 2000 }).onChange((value) => this.imageWidth = value)

    配合+/-按钮微调尺寸,确保操作精度。

  • ​透明度与圆角​​:

    • 透明度范围(0~1,步长0.01):Slider绑定colorOpacity变量。
    • 圆角范围(0~100px):通过drawRoundedRect方法生成圆角路径。
​2. Canvas绘图与优化​
  • ​自适应预览区​​:
    根据屏幕尺寸动态计算预览图比例,保持宽高比:
    const maxWidth = vp2px(this.screenWidth) * 0.9; // 限制最大宽度为屏幕90%
    const previewHeight = (previewWidth * this.imageHeight) / this.imageWidth; // 等比缩放
  • ​离屏渲染技术​​:
    保存图片时使用OffscreenCanvasRenderingContext2D生成高清原图:
    const tempCanvas = new OffscreenCanvasRenderingContext2D(vpWidth, vpHeight);
    this.drawRoundedRect(tempCanvas, 0, 0, vpWidth, vpHeight, actualBorderRadius);
    避免主线程渲染阻塞,提升性能。
​3. 图片保存与分享​
  • ​沙箱存储​​:
    CustomImageUtil.saveCanvasToSandbox将图片写入应用沙箱目录,文件名用UUID防冲突。
  • ​系统分享能力​​:
    调用CustomImageUtil.shareImage唤起系统分享菜单,支持社交平台分发。

​三、性能优化与体验设计​
​1. 渲染性能提升​
  • ​按需重绘​​:
    参数变化时仅更新Canvas(updateCanvasSize()),而非全局重渲染。
  • ​圆角路径算法优化​​:
    使用quadraticCurveTo绘制平滑圆角,替代arc减少计算量:
    canvas.quadraticCurveTo(0, 0, cornerRadius, 0); // 贝塞尔曲线优化
​2. 用户体验增强​
  • ​实时预览反馈​​:
    所有参数调整(如透明度滑块)即时触发Canvas重绘,实现“所见即所得”。
  • ​持久化存储​​:
    PreferencesUtil保存用户最后一次配置(颜色/尺寸等),提升使用连贯性。
  • ​防误触设计​​:
    尺寸按钮设置边界限制(>40px且<2000px),避免无效操作。
​3. 视觉层次设计​
  • ​卡片化布局​​:
    @Extend装饰器定义settingsCard样式,统一阴影与圆角:
    .shadow({ radius: 8, color: '#1A000000' }) // 柔和阴影提升层次感
    .backgroundColor($r('sys.color.ohos_id_color_sub_background')) // 动态主题适配
  • ​交互反馈​​:
    颜色预览按钮添加borderColor高亮边框,增强可识别性。

​结语​

本文实现的纯色图生成器,通过​​状态驱动UI​​、​​Canvas离屏渲染​​与​​本地化存储​​,在保障性能的同时提供了极致交互体验。其模块化设计(如分离CustomImageUtil工具类)可复用于其他图像处理场景(如证件照生成、海报设计)。

​设计原则总结​​:

原则实现方式
​实时性​状态绑定+按需重绘机制
​轻量化​纯前端实现,无额外依赖
​用户友好​持久化存储+沙箱文件管理
​扩展性​模块化工具类设计

具体代码如下:

import { ColorPickDialog } from './ColorPicker/components/ColorPickerDialog';
import { AppUtil } from './Utils/AppUtil';
import { PreferencesUtil } from './Utils/PreferencesUtil';
import { promptAction } from '@kit.ArkUI';
import CustomImageUtil from './Utils/customUtils/CustomImageUtil';
import { TitleBar } from '../components/TitleBar';
import { BusinessError } from '@kit.BasicServicesKit';
import { CustomSaveButton } from '../components/SaveButton';@Extend(Text)
function sectionTitle() {.fontSize(18).fontWeight(FontWeight.Medium).fontColor($r('sys.color.ohos_id_color_text_primary')).margin({ bottom: 12 })
}@Extend(Column)
function settingsCard() {.width('100%').padding(20).borderRadius(16).backgroundColor($r('sys.color.ohos_id_color_sub_background')).shadow({radius: 8,color: '#1A000000',offsetX: 2,offsetY: 4})
}@Entry
@Component
struct SolidColorPage {@State @Watch('updateCanvasSize') selectedColor: string = '#E3F2FD';@State imageWidth: number = 500;@State imageHeight: number = 500;@State previewWidth: number = 0;@State previewHeight: number = 0;@State colorOpacity: number = 1.0;@State colorBorderRadius: number = 0;canvas: CanvasRenderingContext2D = new CanvasRenderingContext2D();colorPickDialogController: CustomDialogController | null = new CustomDialogController({builder: ColorPickDialog({ color: this.selectedColor }),alignment: DialogAlignment.Center,width: '80%',cornerRadius: 15,backgroundColor: $r('sys.color.background_primary')});screenWidth: number = 0;screenHeight: number = 0;async aboutToAppear(): Promise<void> {AppUtil.setWindowKeepScreenOn(true);// 恢复上次的设置this.selectedColor = await PreferencesUtil.getString('solidColor') || '#E3F2FD';this.imageWidth = await PreferencesUtil.getNumber('solidColorWidth') || 500;this.imageHeight = await PreferencesUtil.getNumber('solidColorHeight') || 500;this.colorOpacity = await PreferencesUtil.getNumber('solidColorOpacity') || 1.0;this.colorBorderRadius = await PreferencesUtil.getNumber('solidColorBorderRadius') || 0;this.updateCanvasSize();}aboutToDisappear(): void {// 保存设置PreferencesUtil.put('solidColor', this.selectedColor);PreferencesUtil.put('solidColorWidth', this.imageWidth);PreferencesUtil.put('solidColorHeight', this.imageHeight);PreferencesUtil.put('solidColorOpacity', this.colorOpacity);PreferencesUtil.put('solidColorBorderRadius', this.colorBorderRadius);this.colorPickDialogController?.close();}updateCanvasSize(): void {// 计算预览尺寸,保持宽高比const maxWidth: number = vp2px(this.screenWidth) * 0.9;const maxHeight: number = vp2px(this.screenHeight) * 0.9;// 计算保持宽高比的预览尺寸let previewWidth: number = maxWidth;let previewHeight: number = (previewWidth * this.imageHeight) / this.imageWidth;// 如果高度超出限制,则按高度缩放if (previewHeight > maxHeight) {previewHeight = maxHeight;previewWidth = (previewHeight * this.imageWidth) / this.imageHeight;}this.previewWidth = previewWidth;this.previewHeight = previewHeight;this.drawColor();}drawColor(): void {this.canvas.reset();this.canvas.fillStyle = this.selectedColor;this.canvas.globalAlpha = this.colorOpacity;this.drawRoundedRect(this.canvas, 0, 0, px2vp(this.previewWidth), px2vp(this.previewHeight),this.colorBorderRadius);this.canvas.fill();}private drawRoundedRect(canvas: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, x: number, y: number,vpWidth: number, vpHeight: number, cornerRadius: number): void {// 创建圆角路径canvas.beginPath();canvas.moveTo(cornerRadius, 0);canvas.lineTo(vpWidth - cornerRadius, 0);canvas.quadraticCurveTo(vpWidth, 0, vpWidth, cornerRadius);canvas.lineTo(vpWidth, vpHeight - cornerRadius);canvas.quadraticCurveTo(vpWidth, vpHeight, vpWidth - cornerRadius, vpHeight);canvas.lineTo(cornerRadius, vpHeight);canvas.quadraticCurveTo(0, vpHeight, 0, vpHeight - cornerRadius);canvas.lineTo(0, cornerRadius);canvas.quadraticCurveTo(0, 0, cornerRadius, 0);canvas.closePath();canvas.clip();}async saveImage(): Promise<void> {let vpWidth = px2vp(this.imageWidth);let vpHeight = px2vp(this.imageHeight);const tempCanvas: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(vpWidth, vpHeight);tempCanvas.fillStyle = this.selectedColor;tempCanvas.globalAlpha = this.colorOpacity;// 计算实际图片的圆角大小const scaleRatio = this.imageWidth / this.previewWidth;const actualBorderRadius = this.colorBorderRadius * scaleRatio;this.drawRoundedRect(tempCanvas, 0, 0, vpWidth, vpHeight, actualBorderRadius);tempCanvas.fill();try {// 保存到沙箱目录const filePath = await CustomImageUtil.saveCanvasToSandbox(tempCanvas, vpWidth, vpHeight);// 分享图片await CustomImageUtil.shareImage(filePath);} catch (error) {promptAction.showToast({ message: '保存或分享失败:' + (error as BusinessError).message });}}build() {Column() {// 顶部栏TitleBar({title: '纯色图生成'})// 预览区域Column() {Canvas(this.canvas).width(px2vp(this.previewWidth)).height(px2vp(this.previewHeight)).onClick(() => {this.colorPickDialogController?.open();})}.width('100%').height('40%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {this.screenWidth = Number(newValue.width);this.screenHeight = Number(newValue.height);this.updateCanvasSize();})// 设置区域Scroll() {Column({ space: 20 }) {// 颜色选择Column() {Text('选择颜色').sectionTitle()Row({ space: 15 }) {Button().width(60).height(60).backgroundColor(this.selectedColor).borderRadius(30).borderWidth(3).borderColor($r('app.color.border_color')).shadow({radius: 8,color: '#1A000000',offsetX: 0,offsetY: 2})Text(this.selectedColor).fontSize(16).fontWeight(FontWeight.Medium).fontColor($r('sys.color.ohos_id_color_text_primary'))}}.settingsCard().onClick(() => {this.colorPickDialogController?.open();})// 尺寸设置Column() {Text('图片尺寸').sectionTitle()Row({ space: 8 }) {Column() {Text('宽度:').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))Text(`${this.imageWidth}px`).fontSize(16).fontWeight(FontWeight.Medium).fontColor($r('sys.color.ohos_id_color_text_primary'))}.width(60)Button() {SymbolGlyph($r('sys.symbol.minus')).fontSize(20).fontColor(['#FF6B6B'])}.backgroundColor('#FFF0F0').borderColor('#FF6B6B').borderWidth(1).padding(5).onClick(() => {if (this.imageWidth > 40) {this.imageWidth -= 1;this.updateCanvasSize();}})Slider({value: this.imageWidth,min: 40,max: 2000,step: 1,}).showTips(true, this.imageWidth + '').layoutWeight(1).onChange((value: number) => {this.imageWidth = value;this.updateCanvasSize();})Button() {SymbolGlyph($r('sys.symbol.plus')).fontSize(20).fontColor(['#4CAF50'])}.backgroundColor('#F0FFF0').borderColor('#4CAF50').borderWidth(1).padding(5).onClick(() => {if (this.imageWidth < 2000) {this.imageWidth += 1;this.updateCanvasSize();}})}.justifyContent(FlexAlign.Center).width('100%').margin({ bottom: 15 })Row({ space: 8 }) {Column() {Text('高度:').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))Text(`${this.imageHeight}px`).fontSize(16).fontWeight(FontWeight.Medium).fontColor($r('sys.color.ohos_id_color_text_primary'))}.width(60)Button() {SymbolGlyph($r('sys.symbol.minus')).fontSize(20).fontColor(['#FF6B6B'])}.backgroundColor('#FFF0F0').borderColor('#FF6B6B').borderWidth(1).padding(5).onClick(() => {if (this.imageHeight > 40) {this.imageHeight -= 1;this.updateCanvasSize();}})Slider({value: this.imageHeight,min: 40,max: 2000,step: 1}).showTips(true, this.imageHeight + '').layoutWeight(1).onChange((value: number) => {this.imageHeight = value;this.updateCanvasSize();})Button() {SymbolGlyph($r('sys.symbol.plus')).fontSize(20).fontColor(['#4CAF50'])}.backgroundColor('#F0FFF0').borderColor('#4CAF50').borderWidth(1).padding(5).onClick(() => {if (this.imageHeight < 2000) {this.imageHeight += 1;this.updateCanvasSize();}})}.justifyContent(FlexAlign.Center).width('100%')}.settingsCard()// 透明度设置Column() {Text('透明度').sectionTitle()Row({ space: 12 }) {Text('0').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))Slider({value: this.colorOpacity,min: 0,max: 1,step: 0.01}).showTips(true, this.colorOpacity.toFixed(2)).width(200).onChange((value: number) => {this.colorOpacity = value;this.updateCanvasSize();})Text('1').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))}.justifyContent(FlexAlign.Center).width('100%').margin({ bottom: 10 })}.settingsCard()// 圆角设置Column() {Text('圆角大小').sectionTitle()Row({ space: 12 }) {Text('0').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))Slider({value: this.colorBorderRadius,min: 0,max: 100,step: 1}).showTips(true, this.colorBorderRadius + '').width(200).onChange((value: number) => {this.colorBorderRadius = value;this.updateCanvasSize();})Text('100').fontSize(14).fontColor($r('sys.color.ohos_id_color_text_secondary'))}.justifyContent(FlexAlign.Center).width('100%').margin({ bottom: 10 })}.settingsCard()CustomSaveButton().onClick(()=>{this.saveImage();})}.width('100%').padding(20)}.edgeEffect(EdgeEffect.Spring).layoutWeight(1).height('50%')}.width('100%').height('100%').backgroundColor($r('app.color.index_tab_bar'))}
} 

相关文章:

  • 硬件-DAY04(ds18b20、ARM内核)
  • 回溯----8.N皇后
  • 日本生活:日语语言学校-日语作文-沟通无国界(3)-题目:わたしの友達
  • 深度学习-164-MCP技术之开发本地MCP服务器和异步客户端
  • JETBRAINS IDE 开发环境自定义设置快捷键
  • JMeter 高阶玩法:分布式压测的技术核心技术要点
  • 04、谁发明了深度学习的方法,是怎么发明的?
  • docker安装mysql8.0详细步骤教程
  • Rsync实操
  • 适配器模式:接口转换的神奇魔法[特殊字符],让不兼容的类和谐共处!
  • 锂电池保护板测试仪:守护电池安全的幕后保障
  • Transformer+CNN特征提取与跨注意力特征融合
  • 功能安全时钟切换:关键考量与实施策略
  • [Data Pipeline] Kafka消息 | Redis缓存 | Docker部署(Lambda架构)
  • jquery 赋值时不触发change事件解决——仙盟创梦IDE
  • 将多个Excel合并到一个Excel中的方法
  • 【嵌入式硬件实例】-555定时器控制舵机/伺服电机
  • MySQL 三大日志:Redo、Undo 与 Binlog 详解
  • Spring Boot 集成 Elasticsearch(含 ElasticsearchRestTemplate 示例)
  • 102页满分PPT | 汽车设备制造业企业信息化业务解决方案智能制造汽车黑灯工厂解决方案
  • 网站建设广州市/网推获客平台
  • 淄博网站建设培训学校/seo诊断站长
  • 最新手机资讯/广州网站优化方式
  • 江苏优化网站公司哪家好/交换友情链接的平台有哪些
  • 北京哪家做网站好/武汉网站seo推广公司
  • 搭建网站费用/精准营销系统