HarmonyOS APP实战-画图APP - 第8篇:使用effectKit处理图像
1. 开篇
上一篇我们实现了在画布上添加文字的功能,用户可以通过对话框输入文字内容、选择字体大小和颜色,并通过拖拽将文字放置在画布任意位置。文字绘制基于CanvasRenderingContext2D的fillText方法,配合measureText实现精准定位。至此,画图APP已经具备了形状绘制、手绘、颜色调节、文字标注等核心交互能力。
然而,用户还希望对自己的作品应用滤镜效果------比如调节整体亮度、添加模糊或变为灰度风格。HarmonyOS提供的@ohos.effectKit正能满足这一需求,它能对PixelMap图像高效地进行亮度调节、模糊化、灰度调节等操作。本篇我们将将这个能力集成到APP中:用户选择一个效果并调整参数后,画布图像会实时更新,并支持反复应用不同效果。
2. 核心实现
2.1 基础配置 -- 导入模块与类型定义
首先在项目根目录的oh-package.json5中确认@ohos.effectKit已被包含(默认系统能力无需额外安装)。然后在需要使用的地方导入模块:
typescript
// utils/EffectProcessor.ts
import { effectKit } from '@ohos.effectKit'; // 图像效果模块
import { image } from '@ohos.multimedia.image'; // PixelMap 类型
@ohos.effectKit提供了EffectKit类,通过createEffect方法为PixelMap创建图像效果对象,再调用blur、brightness、grayscale等方法应用效果,最后通过getPixelMap获取结果。
关键点说明:
effectKit的createEffect接受一个PixelMap参数,返回ImageEffect实例。brightness(brightness: number):参数范围-1到1,-1为全黑,0为原始,1为全白。blur(radius: number):参数为模糊半径,数值越大越模糊。grayscale():无参数,直接转为灰度。- 每个方法返回
ImageEffect自身,可以链式调用,但建议分开调用以保持清晰。
2.2 核心逻辑 -- EffectProcessor封装
封装一个工具类EffectProcessor,包含三个静态方法,分别对传入的PixelMap进行处理并返回新的PixelMap。注意:原始PixelMap不应被修改,因此每次处理都通过createEffect创建副本。
typescript
// utils/EffectProcessor.ts
import { effectKit, ImageEffect } from '@ohos.effectKit';
import { image } from '@ohos.multimedia.image';
export class EffectProcessor {
/**
* 亮度调节
* @param source 源PixelMap
* @param brightness 亮度值,-1~1
* @returns 处理后的PixelMap
*/
static async applyBrightness(source: image.PixelMap, brightness: number): Promise<image.PixelMap> {
// 创建效果对象
const effect: ImageEffect = effectKit.createEffect(source);
// 应用亮度调节
effect.brightness(brightness);
// 获取结果PixelMap
return await effect.getPixelMap();
}
/**
* 模糊化
* @param source 源PixelMap
* @param radius 模糊半径
* @returns 处理后的PixelMap
*/
static async applyBlur(source: image.PixelMap, radius: number): Promise<image.PixelMap> {
const effect: ImageEffect = effectKit.createEffect(source);
effect.blur(radius);
return await effect.getPixelMap();
}
/**
* 灰度调节
* @param source 源PixelMap
* @returns 处理后的PixelMap
*/
static async applyGrayscale(source: image.PixelMap): Promise<image.PixelMap> {
const effect: ImageEffect = effectKit.createEffect(source);
effect.grayscale();
return await effect.getPixelMap();
}
}
关键点说明:
createEffect不会修改源PixelMap,它会基于源数据生成一个内部副本。getPixelMap()是异步方法,返回Promise<PixelMap>,需要await。- 每次调用均应捕获异常,如参数越界可能导致崩溃。实际应用中建议增加参数校验和try-catch。
2.3 完整模块 -- EffectDialog与集成
接下来实现EffectDialog自定义弹窗,让用户选择效果类型并调节对应参数。同时需要修改主页面,增加"效果"按钮并调用此对话框。
2.3.1 EffectDialog组件
创建pages/EffectDialog.ets文件,使用@CustomDialog装饰器。对话框包含三个单选按钮(亮度/模糊/灰度)、对应的滑块(亮度和模糊需要调节),以及确定/取消按钮。
typescript
// pages/EffectDialog.ets
import { EffectProcessor } from '../utils/EffectProcessor';
import { image } from '@ohos.multimedia.image';
@CustomDialog
export struct EffectDialog {
controller: CustomDialogController;
// 传入当前画布的PixelMap
currentPixelMap: image.PixelMap | null = null;
// 回调函数,返回处理后的PixelMap
onConfirm: (result: image.PixelMap) => void;
// 内部状态
@State selectedEffect: number = 0; // 0:亮度 1:模糊 2:灰度
@State brightnessValue: number = 0; // -1~1
@State blurRadius: number = 5; // 0~20
build() {
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 });
} else if (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 {
let result: image.PixelMap;
switch (this.selectedEffect) {
case 0:
result = await EffectProcessor.applyBrightness(this.currentPixelMap, this.brightnessValue);
break;
case 1:
result = await EffectProcessor.applyBlur(this.currentPixelMap, this.blurRadius);
break;
case 2:
result = await EffectProcessor.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,在顶部工具栏添加一个"效果"按钮。点击时:
- 通过Canvas的
snapshot方法获取当前画布的PixelMap。 - 弹出
EffectDialog,将PixelMap传入。 - 对话框确认后,使用
CanvasRenderingContext2D的drawImage将处理后的PixelMap绘制回画布。
typescript
// pages/Index.ets(部分代码,仅展示新增部分)
import { EffectDialog } from './EffectDialog';
import { image } from '@ohos.multimedia.image';
// ... 已有状态和组件声明
@State canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();
private canvasController: CanvasController = new CanvasController();
dialogController: CustomDialogController | null = null;
// 效果按钮点击事件
async onEffectClick() {
// 步骤1: 获取当前画布快照为PixelMap
try {
const pixelMap: image.PixelMap = await this.canvasController.snapshot();
// 步骤2: 创建并打开效果对话框
this.dialogController = new CustomDialogController({
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) })

关键点说明:
CanvasController的snapshot()方法返回Promise<PixelMap>,是HarmonyOS Canvas组件提供的官方API。drawImage可以将PixelMap直接绘制到Canvas上,完整替换画布内容(之前绘制的图形都会保留,但被覆盖)。- 如果需要保留历史,建议在应用效果前将原始PixelMap推入撤销栈(下一系列会实现),这里为了简洁直接替换。
2.3.4 工具方法异常处理补充
在EffectProcessor中添加基本的异常捕获,避免应用崩溃:
typescript
// 在applyBrightness中添加 try-catch 示例
static async applyBrightness(source: image.PixelMap, brightness: number): Promise<image.PixelMap> {
try {
const effect = effectKit.createEffect(source);
effect.brightness(brightness);
return await effect.getPixelMap();
} catch (error) {
console.error(`applyBrightness error: ${error.code}, ${error.message}`);
throw error; // 向上传递,由调用方处理
}
}
3. 运行验证
完成上述代码后,运行APP。在顶部工具栏可以看到新增的"效果"按钮(图标可自定义,本文略)。点击后弹出效果选择对话框,默认选中"亮度调节",滑块范围-1到1。调节滑块并点击"应用",画布上的所有内容(包括之前绘制的图形和文字)会实时应用亮度效果。选择"模糊化"并调节半径,画布变模糊;选择"灰度"后画布变为黑白风格。
可通过多次点击"效果"应用不同效果的组合(例如先模糊再调整亮度)。每次应用前都会基于当前画布快照进行处理,叠加效果自然。
注意:由于
snapshot获取的是当前画布像素,因此应用效果会覆盖原来的绘制内容。若要保留原始图层,可考虑在前端维护一个单独的PixelMap副本,但本系列聚焦于功能完整性,暂不深入。

4. 小结与预告
第8篇我们成功将@ohos.effectKit集成到画图APP中,实现了亮度调节、模糊化和灰度调节三个实用图像效果。核心模块EffectProcessor封装了效果处理逻辑,EffectDialog提供了友好的交互界面。通过本次集成,APP的功能从"绘制"扩展到"图像后期处理",用户体验更加丰富。
下一篇《智能取色与色板生成》将继续使用effectKit的智能取色能力,让用户从画布中提取主色并自动生成色板,进一步提升画图工具的专业性。我们将实现取色区域选择、主色提取算法以及色板UI展示,敬请期待。