#HarmonyOS NEXT体验官# 基于鸿蒙Next的App(创意工坊)解析7:实现图像特效 原创
本文会详细介绍如何在画布中为插入的外部图片添加特效,添加特效的界面如下图所示:
特效可以叠加使用(可以多选)。
这个窗口耨是使用Effects组件实现的,代码和详细解释如下:
这段 ArkTS 代码实现了一个用于应用各种图像特效的用户界面。界面包含多个复选框供用户选择不同的特效,并通过点击“确定”按钮将选择的特效应用。效果如您提供的图片所示。
代码的实现过程
-
引入依赖:
- 首先引入一些常量和实用函数,如面板背景颜色、按钮背景颜色,以及文档相关的工具函数。
-
组件定义:
- 使用
@Entry
和@Component
装饰器定义了一个名为Effects
的组件。这是一个入口组件,可以在 ArkTS 中直接展示。
- 使用
-
状态变量:
- 使用
@State
定义了一些布尔类型的状态变量(如grayValue
、blurValue
等),这些变量用于存储用户是否选择了某个特效。
- 使用
-
构建用户界面:
build
方法定义了组件的布局和UI元素,使用多个Row
和Column
布局来排列复选框、文本和按钮。
-
复选框组件:
- 每个复选框 (
Checkbox
) 对应一个图像特效,如“灰度效果”、“模糊效果”等。通过select
和onChange
方法,将复选框的状态与相应的状态变量绑定,确保用户选择能够实时反映在状态变量中。
- 每个复选框 (
-
文本组件:
- 每个复选框旁边都有一个
Text
组件,显示特效名称。点击Text
组件时,会切换相应的复选框状态。
- 每个复选框旁边都有一个
-
确定按钮:
- 一个按钮组件位于页面的底部,点击“确定”按钮时,会检查每个复选框的状态,将选中的特效名称添加到
effects
数组中,并通过回调函数effectSelected
传递这些特效。
- 一个按钮组件位于页面的底部,点击“确定”按钮时,会检查每个复选框的状态,将选中的特效名称添加到
详细代码注释
// 从常量文件中引入面板背景颜色和按钮背景颜色常量
import { PANEL_BACKGROUND_COLOR, PANEL_BUTTON_BACKGROUND_COLOR } from '../common/const'
// 引入文档尺寸和文档处理相关的函数和常量
import { DocumentSize, DocumentSizes } from '../data/Documents'
// 引入EntryAbility模块,处理入口相关的能力
import EntryAbility from '../entryability/EntryAbility'
// 引入常用的工具函数,用于处理文档目录相关的操作
import {documentExists, createDocumentDir} from '../common/common'
// 使用@Entry装饰器,标记这个组件为入口组件
@Entry
// 使用@Component装饰器,定义一个组件Effects
@Component
export struct Effects {
// 定义一个可选的回调函数,当用户选择了特效时调用,传递选中的特效数组
effectSelected?: (effects: string[]) => void
// 定义一个字符串类型的根路径变量,初始化为EntryAbility模块提供的根路径
rootPath: string = EntryAbility.rootPath;
// 使用@State定义多个布尔类型的状态变量,用于跟踪每个特效复选框的选择状态
@State grayValue: boolean = false;
@State blurValue: boolean = false;
@State invertValue: boolean = false;
@State sepiaValue: boolean = false;
@State brightnessValue: boolean = false;
@State sharpenValue: boolean = false;
@State embossValue: boolean = false;
// 定义一个字符串类型的状态变量,表示格式名称(在这里暂未使用)
@State formatName: string = '';
// build方法用于构建用户界面
build() {
// 创建一个Column组件,用于垂直排列子组件
Column() {
// 创建一个Row组件,包含一个复选框和文本,表示灰度效果选项
Row() {
// 创建一个复选框组件
Checkbox({ name: 'checkbox1' })
.selectedColor(0x39a2db) // 设置复选框选中时的颜色
.shape(CheckBoxShape.ROUNDED_SQUARE) // 设置复选框的形状为圆角矩形
.select(this.grayValue) // 将复选框的选中状态与grayValue变量绑定
.onChange((value: boolean) => { // 设置复选框状态变化时的回调函数
this.grayValue = value; // 更新状态变量的值
})
.width(25) // 设置复选框的宽度
.height(25) // 设置复选框的高度
// 创建一个文本组件,显示“灰度效果”字样
Text('灰度效果').fontSize(20).foregroundColor('#FFFFFF') // 设置字体大小和颜色
.onClick(() => { // 设置点击事件
this.grayValue = !this.grayValue; // 点击文本时,切换复选框的状态
})
}
// 接下来的几个Row组件都是类似的结构,只是复选框和文本对应不同的特效
// 模糊效果
Row() {
Checkbox({ name: 'checkbox2' })
.selectedColor(0x39a2db)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.select(this.blurValue)
.onChange((value: boolean) => {
this.blurValue = value;
})
.width(25)
.height(25)
Text('模糊效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.blurValue = !this.blurValue;
})
}
// 反色效果
Row() {
Checkbox({ name: 'checkbox3' })
.selectedColor(0x39a2db)
.select(this.invertValue)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.onChange((value: boolean) => {
this.invertValue = value;
})
.width(25)
.height(25)
Text('反色效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.invertValue = !this.invertValue;
})
}
// 棕褐色效果
Row() {
Checkbox({ name: 'checkbox4' })
.selectedColor(0x39a2db)
.select(this.sepiaValue)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.onChange((value: boolean) => {
this.sepiaValue = value;
})
.width(25)
.height(25)
Text('棕褐色效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.sepiaValue = !this.sepiaValue;
})
}
// 亮度效果
Row() {
Checkbox({ name: 'checkbox5' })
.selectedColor(0x39a2db)
.select(this.brightnessValue)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.onChange((value: boolean) => {
this.brightnessValue = value;
})
.width(25)
.height(25)
Text('亮度效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.brightnessValue = !this.brightnessValue;
})
}
// 锐化效果
Row() {
Checkbox({ name: 'checkbox6' })
.selectedColor(0x39a2db)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.onChange((value: boolean) => {
this.sharpenValue = value;
})
.select(this.sharpenValue)
.width(25)
.height(25)
Text('锐化效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.sharpenValue = !this.sharpenValue;
})
}
// 浮雕效果
Row() {
Checkbox({ name: 'checkbox7' })
.selectedColor(0x39a2db)
.select(this.embossValue)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.onChange((value: boolean) => {
this.embossValue = value;
})
.width(25)
.height(25)
Text('浮雕效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(() => {
this.embossValue = !this.embossValue;
})
}
// 创建一个“确定”按钮,当用户点击时,收集所有选中的特效并通过回调函数传递出去
Button({ type: ButtonType.Normal }) {
Text('确定')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.height('100%')
.textAlign(TextAlign.Center)
}
.borderRadius(5) // 设置按钮的圆角半径
.width('100%') // 设置按钮宽度
.height(30) // 设置按钮高度
.margin({bottom:10, top:10}) // 设置按钮的上下边距
.onClick(() => {
let effects: string[] = [];
// 创建一个数组用于存储选中的特效
// 根据每个复选框的状态,将相应的特效名称添加到数组中
if (this.grayValue) {
effects.push('gray');
}
if (this.blurValue) {
effects.push('blur');
}
if (this.invertValue) {
effects.push('invert');
}
if (this.sepiaValue) {
effects.push('sepia');
}
if (this.brightnessValue) {
effects.push('brightness');
}
if (this.sharpenValue) {
effects.push('sharpen');
}
if (this.embossValue) {
effects.push('emboss');
}
// 如果effectSelected回调函数存在,则调用该函数并传递选中的特效
if (this.effectSelected) {
this.effectSelected(effects);
}
})
}
// 设置Column组件的背景颜色和边框属性
.backgroundColor(PANEL_BACKGROUND_COLOR)
.border({ width: 0, color: '#000000', radius: 10 })
.width('100%') // 设置宽度
.height('100%') // 设置高度
.padding({top:10,left:10,right:10}) // 设置内边距
.alignItems(HorizontalAlign.Start) // 设置子元素的水平对齐方式
}
}
这段 ArkTS 代码实现了一个图像特效选择界面,界面如您提供的图片所示。用户可以通过勾选复选框来选择不同的特效(如灰度效果、模糊效果等),并点击“确定”按钮来应用这些特效。我们将更详细地逐行解释代码,并说明其背后的实现原理和逻辑。
引入依赖
import { PANEL_BACKGROUND_COLOR, PANEL_BUTTON_BACKGROUND_COLOR } from '../common/const'
import { DocumentSize, DocumentSizes } from '../data/Documents'
import EntryAbility from '../entryability/EntryAbility'
import {documentExists, createDocumentDir} from '../common/common'
- PANEL_BACKGROUND_COLOR 和 PANEL_BUTTON_BACKGROUND_COLOR:这些常量来自外部模块
../common/const
,用于设置面板和按钮的背景颜色,保证界面的一致性。 - DocumentSize 和 DocumentSizes:这些变量定义了文档尺寸相关的数据,可能会在创建或检查文档时使用(虽然在当前代码中未直接使用)。
- EntryAbility:这是一个管理入口相关功能的模块,提供了一些关于应用程序的全局路径信息,如
rootPath
。 - documentExists 和 createDocumentDir:这些是用于处理文件系统操作的实用函数,
documentExists
用于检查文档是否存在,createDocumentDir
用于创建文档目录。
组件定义
@Entry
@Component
export struct Effects {
- @Entry:标记这个组件为应用的入口组件。这意味着这个组件会在应用启动时自动加载和显示。
- @Component:这是 ArkTS 中的一个装饰器,用于定义一个组件。
Effects
是组件的名称,它包含了组件的逻辑和UI布局。
状态变量
effectSelected?: (effects: string[]) => void
rootPath: string = EntryAbility.rootPath;
@State grayValue: boolean = false;
@State blurValue: boolean = false;
@State invertValue: boolean = false;
@State sepiaValue: boolean = false;
@State brightnessValue: boolean = false;
@State sharpenValue: boolean = false;
@State embossValue: boolean = false;
@State formatName: string = '';
- effectSelected:一个可选的回调函数,用于在用户选择了特效并点击确定后,传递选中的特效数组。
- rootPath:从
EntryAbility
模块中获取应用的根路径,供文件操作使用。 - @State 装饰器:用于声明组件的状态变量,这些变量会自动触发UI更新。当这些变量的值改变时,界面会自动更新以反映最新状态。
- grayValue、blurValue 等:这些布尔值变量用于存储用户是否选中了对应的图像特效。初始值为
false
,表示未选中。
- grayValue、blurValue 等:这些布尔值变量用于存储用户是否选中了对应的图像特效。初始值为
构建界面
build() {
Column() {
- build() 方法:这是组件的核心方法,用于定义组件的UI布局和行为。它使用了 ArkTS 提供的 UI 组件,如
Column
、Row
、Checkbox
、Text
、Button
等。 - Column:这是一个垂直布局的容器组件,它的子组件会按照顺序从上到下排列。
灰度效果复选框和文本
Row() {
Checkbox({ name: 'checkbox1' })
.selectedColor(0x39a2db)
.shape(CheckBoxShape.ROUNDED_SQUARE)
.select(this.grayValue)
.onChange((value: boolean) => {
this.grayValue = value;
})
.width(25)
.height(25)
Text('灰度效果').fontSize(20).foregroundColor('#FFFFFF')
.onClick(()=>{
this.grayValue = !this.grayValue;
})
}
- Row:这是一个水平布局的容器组件,其子组件会从左到右排列。
- Checkbox:复选框组件,用于让用户选择“灰度效果”。
- selectedColor:定义复选框被选中后的颜色,这里使用了一个蓝色值
0x39a2db
。 - shape:定义复选框的形状,这里设置为
ROUNDED_SQUARE
(圆角矩形)。 - select:绑定到
this.grayValue
,控制复选框的选中状态。 - onChange:当复选框状态发生变化时,触发回调函数,将变化的值赋给
this.grayValue
。 - width 和 height:设置复选框的宽度和高度。
- selectedColor:定义复选框被选中后的颜色,这里使用了一个蓝色值
- Text:文本组件,用于显示“灰度效果”文字。
- fontSize 和 foregroundColor:设置文字的大小和颜色。
- onClick:为文本设置点击事件,点击文本时切换
grayValue
的状态(即复选框的状态)。
其他特效复选框和文本
接下来的几个 Row
组件(模糊效果、反色效果等)与上面描述的灰度效果结构类似,只是各自绑定到不同的状态变量(blurValue
、invertValue
等),用于不同的特效选项。
确定按钮
Button({ type: ButtonType.Normal }) {
Text('确定')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.height('100%')
.textAlign(TextAlign.Center)
}
.borderRadius(5)
.width('100%')
.height(30)
.margin({bottom:10, top:10})
.onClick(() => {
let effects: string[] = [];
if (this.grayValue) {
effects.push('gray');
}
if (this.blurValue) {
effects.push('blur');
}
if (this.invertValue) {
effects.push('invert');
}
if (this.sepiaValue) {
effects.push('sepia');
}
if (this.brightnessValue) {
effects.push('brightness');
}
if (this.sharpenValue) {
effects.push('sharpen');
}
if (this.embossValue) {
effects.push('emboss');
}
if (this.effectSelected) {
this.effectSelected(effects);
}
})
- Button:按钮组件,用于提交用户的特效选择。
- Text:按钮上的文本,这里显示为“确定”。
- borderRadius:按钮的圆角半径。
- width 和 height:按钮的宽度和高度。
- margin:按钮的上下边距,提供了一定的间距,使按钮不至于贴得太紧。
- onClick:点击按钮时触发的事件。
- effects 数组:用于存储用户选中的特效名称。
- 检查每个状态变量的值,如果为
true
,将对应的特效名称添加到effects
数组中。 - 如果
effectSelected
回调函数被定义,则调用该函数并将effects
数组作为参数传递出去。这样可以在外部处理用户的选择结果。
设置面板样式
}.backgroundColor(PANEL_BACKGROUND_COLOR)
.border({ width: 0, color: '#000000', radius: 10 })
.width('100%')
.height('100%')
.padding({top:10,left:10,right:10})
.alignItems(HorizontalAlign.Start)
}
}
- backgroundColor:设置面板的背景颜色,使用预定义的常量
PANEL_BACKGROUND_COLOR
。 - border:设置面板的边框属性,宽度为0,颜色为黑色,圆角半径为10。
- width 和 height:设置面板的宽度和高度为100%,即占据父容器的全部空间。
- padding:设置面板的内边距,给面板内容留出一定的空间。
- alignItems:设置子组件的水平对齐方式,这里使用
HorizontalAlign.Start
使子组件从左边开始对齐。
代码总结
这段代码实现了一个简单但功能齐全的图像特效选择界面。用户可以通过界面勾选不同的特效,并点击确定按钮应用这些特效。整个实现过程包括了状态管理(使用 @State
)、用户交互(通过 onChange
和 onClick
事件)、UI 布局(使用 Column
和 Row
)以及回调处理(通过 effectSelected
回调函数)。
这种结构化的代码设计使得UI组件既易于维护又具有灵活性,便于在实际应用中集成和使用。
当点击“确定”按钮,会调用effectSelected事件函数,代码如下:
effectSelected(effects: string[]) {
this.controller1.runJavaScript(`applyImageEffects(${JSON.stringify(effects)})`)
this.toggleEffectsPanel();
}
在这段代码中,涉及到一个appleImageEffect函数,该函数是使用js实现的,用于设置图像特效,该函数的代码如下:
function applyImageEffects(effects) {
const activeObject = canvas.getActiveObject();
var img;
// 检查是否选中了对象,并且这个对象是否为图片
if (activeObject && activeObject.type === 'image') {
img = activeObject;
return new Promise((resolve) => {
effects.forEach((effect) => {
switch (effect) {
case "gray":
img.filters.push(new fabric.Image.filters.Grayscale());
// 灰度效果
break;
case "blur":
img.filters.push(new fabric.Image.filters.Blur({ blur: 0.5 })); // 模糊效果
break;
case "invert":
img.filters.push(new fabric.Image.filters.Invert()); // 反色效果
break;
case "sepia":
img.filters.push(new fabric.Image.filters.Sepia()); // 棕褐色效果
break;
case "brightness":
img.filters.push(
new fabric.Image.filters.Brightness({ brightness: 0.2 })
); // 亮度效果
break;
case "sharpen":
// 锐化效果,通过一个简单的3x3卷积矩阵
img.filters.push(
new fabric.Image.filters.Convolute({
matrix: [0, -1, 0, -1, 5, -1, 0, -1, 0],
})
); // 锐化效果
break;
case "emboss":
// 浮雕效果,通过另一个卷积矩阵实现
img.filters.push(
new fabric.Image.filters.Convolute({
matrix: [
1,
1,
1,
1,
0.7,
-1, // 这个矩阵值可根据需要调整以获得不同的浮雕效果
-1,
-1,
-1,
],
})
); // 浮雕效果
break;
// 可以继续添加更多效果
}
});
img.applyFilters();
resolve();
canvas.requestRenderAll();
});
} else {
return
}
}
在这段代码中,使用了 Fabric.js
库中的 fabric.Image.filters
来为图像对象应用各种图像特效。下面我们详细讲解这些过滤器的功能及其实现原理,并举例说明它们的使用方式。
1. fabric.Image.filters.Grayscale
- 灰度效果
功能:将图像转换为灰度图像(黑白图像)。灰度处理会去除图像中的所有颜色信息,只保留亮度信息,使得图像变成黑白的灰度效果。
实现原理:灰度处理通过计算每个像素的红、绿、蓝 (RGB) 分量的加权平均值,并将这个值作为新的 RGB 值,使得每个像素都变成灰色。
示例:
img.filters.push(new fabric.Image.filters.Grayscale());
// 应用灰度效果后,图像中的所有颜色都会被去除,图像变成黑白色调。
2. fabric.Image.filters.Blur
- 模糊效果
功能:为图像添加模糊效果,模糊程度由 blur
参数控制。
实现原理:模糊效果通常通过在像素周围进行均值平滑处理实现。具体来说,是通过将每个像素的颜色值与其邻近像素的颜色值平均化来实现模糊。模糊程度越高,图像越不清晰。
示例:
img.filters.push(new fabric.Image.filters.Blur({ blur: 0.5 }));
// `blur` 参数的值越大,图像越模糊。这里的值0.5表示适度的模糊效果。
3. fabric.Image.filters.Invert
- 反色效果
功能:对图像进行反色处理,将图像的颜色进行反转,变成其补色。
实现原理:反色处理是通过将每个像素的 RGB 分量从 255 减去该分量的值来实现的。例如,原始像素的 RGB 值为 (100, 150, 200),反色后的值将为 (155, 105, 55)。
示例:
img.filters.push(new fabric.Image.filters.Invert());
// 应用反色效果后,图像会呈现出相反的颜色,形成类似负片的视觉效果。
4. fabric.Image.filters.Sepia
- 棕褐色效果
功能:为图像添加棕褐色效果,使图像看起来像旧照片一样。
实现原理:棕褐色效果通过将图像的 RGB 颜色分量调整为暖色调(红色和棕色调),模拟出一种古老的、复古的外观。
示例:
img.filters.push(new fabric.Image.filters.Sepia());
// 应用棕褐色效果后,图像将带有复古的棕褐色调,给人一种怀旧感。
5. fabric.Image.filters.Brightness
- 亮度效果
功能:调整图像的亮度,可以使图像变得更亮或更暗。
实现原理:亮度调整通过将每个像素的 RGB 分量值增加或减少一个特定的值来实现。正值增加亮度,负值则减小亮度。
示例:
img.filters.push(new fabric.Image.filters.Brightness({ brightness: 0.2 }));
// 这里的 `brightness` 参数为 0.2,使图像整体变亮。值范围通常为 [-1, 1],其中负值变暗,正值变亮。
6. fabric.Image.filters.Convolute
- 卷积滤镜(用于锐化和浮雕效果)
功能:使用卷积矩阵对图像进行复杂的滤波操作,可以实现如锐化和浮雕等效果。
实现原理:卷积滤波通过将一个卷积矩阵应用于图像中的每个像素及其邻近像素来改变图像的细节表现。不同的卷积矩阵会产生不同的图像效果。
锐化效果
img.filters.push(new fabric.Image.filters.Convolute({
matrix: [0, -1, 0, -1, 5, -1, 0, -1, 0],
}));
// 锐化效果使用的卷积矩阵突出图像的边缘,使图像看起来更清晰。
浮雕效果
img.filters.push(new fabric.Image.filters.Convolute({
matrix: [1, 1, 1, 1, 0.7, -1, -1, -1, -1],
}));
// 浮雕效果使用的卷积矩阵创造了光照和阴影的错觉,使图像呈现出浮雕的效果。
7. applyFilters()
和 requestRenderAll()
- applyFilters():这个方法会重新应用图像的所有过滤器。因为在每次添加新过滤器后,图像并不会自动更新,必须调用
applyFilters()
来使得过滤器的效果生效。 - requestRenderAll():请求重新渲染画布上的所有对象,确保应用的过滤器效果能够即时显示在画布上。
img.applyFilters();
canvas.requestRenderAll();
这段代码确保了在所有特效应用后,画布会重新渲染,以反映最新的效果。
总结
这些 fabric.Image.filters
过滤器提供了多种图像处理功能,可以轻松实现灰度、模糊、反色、棕褐色、亮度调整、锐化和浮雕等效果。这些功能通过简单的 API 调用就能实现复杂的图像处理操作,为开发者提供了极大的灵活性和方便性。