# API 16实战案例:鸿蒙元服务实战-笑笑五子棋(3)

 

接上篇。上一篇主要讲解了元服务的创建和 canvas 的一些基本使用,直线、矩形、弧形、文本、图像等。canvas 本身还有很多其他

 

的功能。这里继续围绕 canvas 进行讲解。

 

## createPattern

 

> createPattern(image: ImageBitmap, repetition: string | null): CanvasPattern | null

 

通过指定图像和重复方式创建图片填充的模板。

 

| 参数名     | 类型                                                                                                                    | 必填 | 说明                                                                                                                                                                                                                                                         |

| :--------- | :---------------------------------------------------------------------------------------------------------------------- | :--- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| image      | [ImageBitmap](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-components-canvas-imagebitmap-V5) | 是   | 图源对象,具体参考 ImageBitmap 对象。                                                                                                                                                                                                                        |

| repetition | string \| null                                                                                                          | 是   | 设置图像重复的方式:'repeat':沿 x 轴和 y 轴重复绘制图像;'repeat-x':沿 x 轴重复绘制图像;'repeat-y':沿 y 轴重复绘制图像;'no-repeat':不重复绘制图像;'clamp':在原始边界外绘制时,超出部分使用边缘的颜色绘制;'mirror':沿 x 轴和 y 轴重复翻转绘制图像。 |

 

提前准备好图片

 

![image-20250105085559069](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105085559069.png?x-image-process=style/style-8860)

 

### 基本使用

 

1. 基于图片创建填充模版

2. 设置到 canvas 的 fillStyle 中

3. 进行描绘

 

```jsx

@Entry

@Component

struct Index {

  private settings: RenderingContextSettings = new RenderingContextSettings(true)

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

  private img: ImageBitmap = new ImageBitmap("/images/2.png")

 

  build() {

    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {

      Canvas(this.context)

        .width('100%')

        .height('100%')

        .backgroundColor('#ffff00')

        .onReady(() => {

          // 1  基于图片创建填充模版

2.

          let pattern = this.context.createPattern(this.img, 'no-repeat') // 不平铺

          if (pattern) {

            // 2 设置到canvas的fillStyle中

            this.context.fillStyle = pattern

          }

          // 3 进行描绘

          this.context.fillRect(0, 0, 400, 400)

        })

    }

    .width('100%')

    .height('100%')

  }

}

```

 

**效果**:

 

![image-20250105085914214](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105085914214.png?x-image-process=style/style-8860)

 

### repetition:repeat

 

设置平铺

 

```jsx

let pattern = this.context.createPattern(this.img, "repeat");

```

 

**效果**

 

![image-20250105090015220](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105090015220.png?x-image-process=style/style-8860)

 

### clamp

 

> 在原始边界外绘制时,超出部分使用边缘的颜色绘制;

 

```jsx

let pattern = this.context.createPattern(this.img, "clamp");

```

 

![image-20250105090145764](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105090145764.png?x-image-process=style/style-8860)

 

### mirror

 

> 沿 x 轴和 y 轴重复翻转绘制图像。

 

```jsx

let pattern = this.context.createPattern(this.img, "mirror");

```

 

![image-20250105090203370](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105090203370.png?x-image-process=style/style-8860)

 

## quadraticCurveTo

 

> quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void

 

创建二次贝赛尔曲线的路径。

 

| 参数名 | 类型   | 必填 | 说明                                  |

| :----- | :----- | :--- | :------------------------------------ |

| cpx    | number | 是   | 贝塞尔参数的 x 坐标值。默认单位:vp。 |

| cpy    | number | 是   | 贝塞尔参数的 y 坐标值。默认单位:vp。 |

| x      | number | 是   | 路径结束时的 x 坐标值。默认单位:vp。 |

| y      | number | 是   | 路径结束时的 y 坐标值。默认单位:vp。 |

 

**示例代码**

 

```jsx

this.context.beginPath();

this.context.moveTo(20, 20);

this.context.quadraticCurveTo(100, 100, 200, 20);

this.context.stroke();

```

 

**效果**

 

![image-20250105091240519](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105091240519.png?x-image-process=style/style-8860)

 

**辅助理解**

 

![img](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/v2-c804989b7362556b57257f80d61f28bf_b.webp?x-image-process=style/style-8860)

 

---

 

![image-20250105091007706](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105091007706.png?x-image-process=style/style-8860)

 

## bezierCurveTo

 

> bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void

 

创建三次贝赛尔曲线的路径。

 

| 参数名 | 类型   | 必填 | 说明                                        |

| :----- | :----- | :--- | :------------------------------------------ |

| cp1x   | number | 是   | 第一个贝塞尔参数的 x 坐标值。默认单位:vp。 |

| cp1y   | number | 是   | 第一个贝塞尔参数的 y 坐标值。默认单位:vp。 |

| cp2x   | number | 是   | 第二个贝塞尔参数的 x 坐标值。默认单位:vp。 |

| cp2y   | number | 是   | 第二个贝塞尔参数的 y 坐标值。默认单位:vp。 |

| x      | number | 是   | 路径结束时的 x 坐标值。默认单位:vp。       |

| y      | number | 是   | 路径结束时的 y 坐标值。默认单位:vp。       |

 

**示例代码**

 

```

  this.context.beginPath()

  this.context.moveTo(10, 10)

  this.context.bezierCurveTo(20, 100, 200, 100, 200, 20)

  this.context.stroke()

```

 

**效果**

 

![image-20250105091324817](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105091324817.png?x-image-process=style/style-8860)

 

**辅助理解**

 

![img](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/v2-03d033438586009d74ef41c46abbbf9b_b.webp?x-image-process=style/style-8860)

 

![image-20250105091128806](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105091128806.png?x-image-process=style/style-8860)

 

## ImageData

 

[ImageData](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData)对象可以存储 canvas 渲染的像素数据。也就是说 ImageData 可以让我们使用 canvas 对画布中的每一个像素进行操作。提

 

供了强大的控制能力。

 

### [实例属性](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData#属性)

 

- [`ImageData.data`](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData/data) 只读

 

  [`Uint8ClampedArray`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) 描述了一个一维数组,包含以 RGBA 顺序的数据,数据使用 `0` 至 `255`(包含)的整数表示。

 

- [`ImageData.height`](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData/height) 只读

 

  无符号长整型(`unsigned long`),使用像素描述 **ImageData** 的实际高度。

 

- [`ImageData.width`](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData/width) 只读

 

  无符号长整型(`unsigned long`),使用像素描述 **ImageData** 的实际宽度。

 

这里通过 canvas 的[getImageData]()方法快速获取 ImageData 数据。然后通过[putImageData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V13/ts-offscreencanvasrenderingcontext2d-V13#putimagedata)把处理好的内容重新描绘到画图上。

 

```jsx

@Entry

@Component

struct Index {

  private settings: RenderingContextSettings = new RenderingContextSettings(true)

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

  private img: ImageBitmap = new ImageBitmap("/images/2.png")

 

  build() {

    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {

      Canvas(this.context)

        .width('100%')

        .height('100%')

        .backgroundColor('#ffff00')

        .onReady(() => {

          this.context.drawImage(this.img, 0, 0, 130, 130)

          // 获取了 ImageData 示例

          let imagedata = this.context.getImageData(50, 50, 130, 130)

          // 又重新描绘到canvas上

          this.context.putImageData(imagedata, 150, 150)

        })

    }

    .width('100%')

    .height('100%')

  }

}

```

 

**效果**

 

![image-20250105092307492](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105092307492.png?x-image-process=style/style-8860)

 

## ImageData 反色

 

```jsx

this.context.drawImage(this.img, 0, 0, 130, 130);

// 获取了 ImageData 示例

let imagedata = this.context.getImageData(50, 50, 130, 130);

// console.log("xxx,", JSON.stringify(imagedata.data))

Object.keys(imagedata.data).forEach((k) => {

  // 反色

  imagedata.data[k] = 255 - imagedata.data[k];

});

// 又重新描绘到canvas上

this.context.putImageData(imagedata, 150, 150);

```

 

![image-20250105092847039](https://wsy996.obs.cn-east-3.myhuaweicloud.com/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89/%E9%B8%BF%E8%92%99%E5%85%83%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98-%E7%AC%91%E7%AC%91%E4%BA%94%E5%AD%90%E6%A3%8B%EF%BC%883%EF%BC%89.assets/image-20250105092847039.png?x-image-process=style/style-8860)

 

## ImageData 其他效果

 

反转效果:

 

- 原理:通过将每个像素的 RGB 值取反来实现反转效果。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,将每个像素的 RGB 值取反,再使用`putImageData`将修改后的数据绘制回 Canvas。

 

黑白效果:

 

- 原理:将每个像素的 RGB 值转换为灰度值,使图像变为黑白。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,将每个像素的 RGB 值转换为灰度值(R、G、B 三个分量取平均值),再使用`putImageData`将修改后的数据绘制回 Canvas。

 

亮度效果:

 

- 原理:调整每个像素的亮度值,使图像变亮或变暗。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,调整每个像素的亮度值,再使用`putImageData`将修改后的数据绘制回 Canvas。

 

复古效果:

 

- 原理:通过调整每个像素的色调、饱和度和亮度,使图像呈现复古效果。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,调整每个像素的色调、饱和度和亮度,再使用`putImageData`将修改后的数据绘制回 Canvas。

 

红色、绿色、蓝色效果:

 

- 原理:增加或减少每个像素的红色、绿色、蓝色分量的值,使图像呈现相应颜色的效果。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,增加或减少每个像素的红色、绿色、蓝色分量的值,再使用`putImageData`将修改后的数据绘制回 Canvas。

 

透明效果:

 

- 原理:调整每个像素的透明度值,使图像呈现透明效果。

- 实现方式:使用`getImageData`获取图像数据,然后遍历每个像素,调整每个像素的透明度值,再使用`putImageData`将修改后的数据绘制回 Canvas。

 

马赛克效果:

 

- 原理:将图像分割为小块,每个小块的像素值设置为该小块内像素的平均值,从而实现马赛克效果。

- 实现方式:使用`getImageData`获取图像数据,然后将图像分割为小块,计算每个小块内像素的平均值,再将该小块内所有像素的值设置为该平均值,最后使用`putImageData`将修改后的数据绘制回 Canvas。

 

马赛克效果

 

- 由于实际操作过程中,上述马赛克效果处理性能比较底下,这里用来一个取巧的效果来实现。就是先用 canvas 将画面画小,然后再将画面缩放来实现一个模糊效果,间接实现马赛克效果

 

渐变滤镜效果:

 

- 原理:通过在图像上应用渐变效果,使图像呈现渐变色的效果。

- 实现方式:使用`createLinearGradient`或`createRadialGradient`创建渐变对象,然后使用渐变对象作为填充样式,绘制图像到 Canvas 上。

 

## 参考链接

 

1. [数学曲线之一:贝塞尔曲线](https://zhuanlan.zhihu.com/p/711975272)

2. [神奇 canvas 带你实现魔法摄像头](https://juejin.cn/post/7264125562393788473#heading-8)

3. [ImageData](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData)

 

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐