HarmonyOS APP实战-画图APP - 第8篇:图像效果应用(亮度/模糊/灰度)

📅 2026/7/12 22:29:53 👁️ 阅读次数 📝 编程学习
HarmonyOS APP实战-画图APP - 第8篇:图像效果应用(亮度/模糊/灰度)

HarmonyOS APP实战-画图APP - 第8篇:使用effectKit处理图像

1. 开篇

上一篇我们实现了在画布上添加文字的功能,用户可以通过对话框输入文字内容、选择字体大小和颜色,并通过拖拽将文字放置在画布任意位置。文字绘制基于CanvasRenderingContext2DfillText方法,配合measureText实现精准定位。至此,画图APP已经具备了形状绘制、手绘、颜色调节、文字标注等核心交互能力。

然而,用户还希望对自己的作品应用滤镜效果——比如调节整体亮度、添加模糊或变为灰度风格。HarmonyOS提供的@ohos.effectKit正能满足这一需求,它能对PixelMap图像高效地进行亮度调节、模糊化、灰度调节等操作。本篇我们将将这个能力集成到APP中:用户选择一个效果并调整参数后,画布图像会实时更新,并支持反复应用不同效果。

2. 核心实现

2.1 基础配置 – 导入模块与类型定义

首先在项目根目录的oh-package.json5中确认@ohos.effectKit已被包含(默认系统能力无需额外安装)。然后在需要使用的地方导入模块:

// utils/EffectProcessor.tsimport{effectKit}from'@ohos.effectKit';// 图像效果模块import{image}from'@ohos.multimedia.image';// PixelMap 类型

@ohos.effectKit提供了EffectKit类,通过createEffect方法为PixelMap创建图像效果对象,再调用blurbrightnessgrayscale等方法应用效果,最后通过getPixelMap获取结果。

关键点说明:

  • effectKitcreateEffect接受一个PixelMap参数,返回ImageEffect实例。
  • brightness(brightness: number):参数范围-1到1,-1为全黑,0为原始,1为全白。
  • blur(radius: number):参数为模糊半径,数值越大越模糊。
  • grayscale():无参数,直接转为灰度。
  • 每个方法返回ImageEffect自身,可以链式调用,但建议分开调用以保持清晰。

2.2 核心逻辑 – EffectProcessor封装

封装一个工具类EffectProcessor,包含三个静态方法,分别对传入的PixelMap进行处理并返回新的PixelMap。注意:原始PixelMap不应被修改,因此每次处理都通过createEffect创建副本。

// utils/EffectProcessor.tsimport{effectKit,ImageEffect}from'@ohos.effectKit';import{image}from'@ohos.multimedia.image';exportclassEffectProcessor{/** * 亮度调节 * @param source 源PixelMap * @param brightness 亮度值,-1~1 * @returns 处理后的PixelMap */staticasyncapplyBrightness(source:image.PixelMap,brightness:number):Promise<image.PixelMap>{// 创建效果对象consteffect:ImageEffect=effectKit.createEffect(source);// 应用亮度调节effect.brightness(brightness);// 获取结果PixelMapreturnawaiteffect.getPixelMap();}/** * 模糊化 * @param source 源PixelMap * @param radius 模糊半径 * @returns 处理后的PixelMap */staticasyncapplyBlur(source:image.PixelMap,radius:number):Promise<image.PixelMap>{consteffect:ImageEffect=effectKit.createEffect(source);effect.blur(radius);returnawaiteffect.getPixelMap();}/** * 灰度调节 * @param source 源PixelMap * @returns 处理后的PixelMap */staticasyncapplyGrayscale(source:image.PixelMap):Promise<image.PixelMap>{consteffect:ImageEffect=effectKit.createEffect(source);effect.grayscale();returnawaiteffect.getPixelMap();}}

关键点说明:

  • createEffect不会修改源PixelMap,它会基于源数据生成一个内部副本。
  • getPixelMap()是异步方法,返回Promise<PixelMap>,需要await。
  • 每次调用均应捕获异常,如参数越界可能导致崩溃。实际应用中建议增加参数校验和try-catch。

2.3 完整模块 – EffectDialog与集成

接下来实现EffectDialog自定义弹窗,让用户选择效果类型并调节对应参数。同时需要修改主页面,增加“效果”按钮并调用此对话框。

2.3.1 EffectDialog组件

创建pages/EffectDialog.ets文件,使用@CustomDialog装饰器。对话框包含三个单选按钮(亮度/模糊/灰度)、对应的滑块(亮度和模糊需要调节),以及确定/取消按钮。

// pages/EffectDialog.etsimport{EffectProcessor}from'../utils/EffectProcessor';import{image}from'@ohos.multimedia.image';@CustomDialogexportstruct EffectDialog{controller:CustomDialogController;// 传入当前画布的PixelMapcurrentPixelMap:image.PixelMap|null=null;// 回调函数,返回处理后的PixelMaponConfirm:(result:image.PixelMap)=>void;// 内部状态@StateselectedEffect:number=0;// 0:亮度 1:模糊 2:灰度@StatebrightnessValue:number=0;// -1~1@StateblurRadius:number=5;// 0~20build(){Column(){Text('选择图像效果').fontSize(18).margin({bottom:16});// 效果类型选择Radio({value:'brightness',group:'effectGroup'}).checked(true).onChange(()=>{this.selectedEffect=0;}).height(30)Text('亮度调节').margin({left:8});Radio({value:'blur',group:'effectGroup'}).onChange(()=>{this.selectedEffect=1;}).height(30)Text('模糊化').margin({left:8});Radio({value:'grayscale',group:'effectGroup'}).onChange(()=>{this.selectedEffect=2;}).height(30)Text('灰度').margin({left:8});// 动态参数调节if(this.selectedEffect===0){Row(){Text('亮度').width(60);Slider({value:this.brightnessValue,min:-1,max:1,step:0.01}).onChange((val:number)=>{this.brightnessValue=val;}).width(200);}.margin({top:12});}elseif(this.selectedEffect===1){Row(){Text('模糊半径').width(80);Slider({value:this.blurRadius,min:0,max:20,step:1}).onChange((val:number)=>{this.blurRadius=val;}).width(200);}.margin({top:12});}// 预览与确认按钮Row(){Button('取消').onClick(()=>{this.controller.close();});Button('应用').onClick(async()=>{if(!this.currentPixelMap){console.error('No pixelmap to process');return;}try{letresult:image.PixelMap;switch(this.selectedEffect){case0:result=awaitEffectProcessor.applyBrightness(this.currentPixelMap,this.brightnessValue);break;case1:result=awaitEffectProcessor.applyBlur(this.currentPixelMap,this.blurRadius);break;case2:result=awaitEffectProcessor.applyGrayscale(this.currentPixelMap);break;}this.onConfirm(result);this.controller.close();}catch(err){console.error('Effect application failed: '+err);}});}.justifyContent(FlexAlign.Center).width('100%').margin({top:20});}.padding(20).width('80%');}}

关键点说明:

  • @CustomDialog的组件需要传入controller和回调。
  • 通过selectedEffect变量控制显示对应的滑块。
  • 点击“应用”后调用EffectProcessor的静态方法,并通过onConfirm将结果传回主页面。
  • 由于getPixelMap()是异步,这里使用async/await。
2.3.2 主页面集成效果按钮与画布快照

修改主页面pages/Index.ets,在顶部工具栏添加一个“效果”按钮。点击时:

  1. 通过Canvas的snapshot方法获取当前画布的PixelMap
  2. 弹出EffectDialog,将PixelMap传入。
  3. 对话框确认后,使用CanvasRenderingContext2DdrawImage将处理后的PixelMap绘制回画布。
// pages/Index.ets(部分代码,仅展示新增部分)import{EffectDialog}from'./EffectDialog';import{image}from'@ohos.multimedia.image';// ... 已有状态和组件声明@StatecanvasContext:CanvasRenderingContext2D=newCanvasRenderingContext2D();privatecanvasController:CanvasController=newCanvasController();dialogController:CustomDialogController|null=null;// 效果按钮点击事件asynconEffectClick(){// 步骤1: 获取当前画布快照为PixelMaptry{constpixelMap:image.PixelMap=awaitthis.canvasController.snapshot();// 步骤2: 创建并打开效果对话框this.dialogController=newCustomDialogController({builder:EffectDialog({currentPixelMap:pixelMap,onConfirm:(result:image.PixelMap)=>{// 步骤3: 将处理后的PixelMap绘制到画布this.canvasContext.drawImage(result,0,0);}}),cancel:()=>{console.log('效果取消');},autoCancel:true});this.dialogController.open();}catch(err){console.error('Snapshot failed: '+err);}}// build()中添加效果按钮ToolbarButton({text:'效果',onClick:this.onEffectClick.bind(this)})

关键点说明:

  • CanvasControllersnapshot()方法返回Promise<PixelMap>,是HarmonyOS Canvas组件提供的官方API。
  • drawImage可以将PixelMap直接绘制到Canvas上,完整替换画布内容(之前绘制的图形都会保留,但被覆盖)。
  • 如果需要保留历史,建议在应用效果前将原始PixelMap推入撤销栈(下一系列会实现),这里为了简洁直接替换。
2.3.4 工具方法异常处理补充

EffectProcessor中添加基本的异常捕获,避免应用崩溃:

// 在applyBrightness中添加 try-catch 示例staticasyncapplyBrightness(source:image.PixelMap,brightness:number):Promise<image.PixelMap>{try{consteffect=effectKit.createEffect(source);effect.brightness(brightness);returnawaiteffect.getPixelMap();}catch(error){console.error(`applyBrightness error:${error.code},${error.message}`);throwerror;// 向上传递,由调用方处理}}

3. 运行验证

完成上述代码后,运行APP。在顶部工具栏可以看到新增的“效果”按钮(图标可自定义,本文略)。点击后弹出效果选择对话框,默认选中“亮度调节”,滑块范围-1到1。调节滑块并点击“应用”,画布上的所有内容(包括之前绘制的图形和文字)会实时应用亮度效果。选择“模糊化”并调节半径,画布变模糊;选择“灰度”后画布变为黑白风格。

可通过多次点击“效果”应用不同效果的组合(例如先模糊再调整亮度)。每次应用前都会基于当前画布快照进行处理,叠加效果自然。

注意:由于snapshot获取的是当前画布像素,因此应用效果会覆盖原来的绘制内容。若要保留原始图层,可考虑在前端维护一个单独的PixelMap副本,但本系列聚焦于功能完整性,暂不深入。

4. 小结与预告

第8篇我们成功将@ohos.effectKit集成到画图APP中,实现了亮度调节、模糊化和灰度调节三个实用图像效果。核心模块EffectProcessor封装了效果处理逻辑,EffectDialog提供了友好的交互界面。通过本次集成,APP的功能从“绘制”扩展到“图像后期处理”,用户体验更加丰富。

下一篇《智能取色与色板生成》将继续使用effectKit的智能取色能力,让用户从画布中提取主色并自动生成色板,进一步提升画图工具的专业性。我们将实现取色区域选择、主色提取算法以及色板UI展示,敬请期待。