• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用画布绘制自定义图形 (Canvas)
2
3
4Canvas提供画布组件,用于自定义绘制图形,开发者使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制,绘制对象可以是基础形状、文本、图片等。
5
6
7## 使用画布组件绘制自定义图形
8
9可以由以下三种形式在画布绘制自定义图形:
10
11
12- 使用[CanvasRenderingContext2D](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md)对象在Canvas画布上绘制。
13
14  ```ts
15  @Entry
16  @Component
17  struct CanvasExample1 {
18    //用来配置CanvasRenderingContext2D对象的参数,包括是否开启抗锯齿,true表明开启抗锯齿。
19    private settings: RenderingContextSettings = new RenderingContextSettings(true)
20    //用来创建CanvasRenderingContext2D对象,通过在canvas中调用CanvasRenderingContext2D对象来绘制。
21    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
22
23    build() {
24      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
25        //在canvas中调用CanvasRenderingContext2D对象。
26        Canvas(this.context)
27          .width('100%')
28          .height('100%')
29          .backgroundColor('#F5DC62')
30          .onReady(() => {
31            //可以在这里绘制内容。
32            this.context.strokeRect(50, 50, 200, 150);
33          })
34      }
35      .width('100%')
36      .height('100%')
37    }
38  }
39  ```
40
41  ![2023022793003(1)](figures/2023022793003(1).jpg)
42
43- 离屏绘制是指将需要绘制的内容先绘制在缓存区,再将其转换成图片,一次性绘制到Canvas上,加快了绘制速度。过程为:
44  1. 通过transferToImageBitmap方法将离屏画布最近渲染的图像创建为一个ImageBitmap对象。
45  2. 通过CanvasRenderingContext2D对象的transferFromImageBitmap方法显示给定的ImageBitmap对象。
46
47    具体使用参考[OffscreenCanvasRenderingContext2D](../reference/apis-arkui/arkui-ts/ts-offscreencanvasrenderingcontext2d.md)对象。
48
49  ```ts
50  @Entry
51  @Component
52  struct CanvasExample2 {
53    //用来配置CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象的参数,包括是否开启抗锯齿。true表明开启抗锯齿
54    private settings: RenderingContextSettings = new RenderingContextSettings(true)
55    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
56    //用来创建OffscreenCanvas对象,width为离屏画布的宽度,height为离屏画布的高度。通过在canvas中调用OffscreenCanvasRenderingContext2D对象来绘制。
57    private offCanvas: OffscreenCanvas = new OffscreenCanvas(600, 600)
58
59    build() {
60      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
61        Canvas(this.context)
62          .width('100%')
63          .height('100%')
64          .backgroundColor('#F5DC62')
65          .onReady(() => {
66            let offContext = this.offCanvas.getContext("2d", this.settings)
67            //可以在这里绘制内容
68            offContext.strokeRect(50, 50, 200, 150);
69            //将离屏绘制渲染的图像在普通画布上显示
70            let image = this.offCanvas.transferToImageBitmap();
71            this.context.transferFromImageBitmap(image);
72          })
73      }
74      .width('100%')
75      .height('100%')
76    }
77  }
78  ```
79
80  ![2023022793003(1)](figures/2023022793003(1).jpg)
81
82  >**说明:**
83  >
84  >在画布组件中,通过CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制时调用的接口相同,另接口参数如无特别说明,单位均为vp。
85
86- 在Canvas上加载Lottie动画时,需要先按照如下方式下载Lottie。
87
88  ```ts
89  import lottie from '@ohos/lottie'
90  ```
91
92  具体接口请参考[Lottie](https://gitee.com/openharmony-tpc/lottieETS)93
94
95## 初始化画布组件
96
97onReady(event: () => void)是Canvas组件初始化完成时的事件回调,调用该事件后,可获取Canvas组件的确定宽高,进一步使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象调用相关API进行图形绘制。
98
99```ts
100Canvas(this.context)
101  .width('100%')
102  .height('100%')
103  .backgroundColor('#F5DC62')
104  .onReady(() => {
105    this.context.fillStyle = '#0097D4';
106    this.context.fillRect(50, 50, 100, 100);
107  })
108```
109
110![2023022793350(1)](figures/2023022793350(1).jpg)
111
112
113## 画布组件绘制方式
114
115在Canvas组件生命周期接口onReady()调用之后,开发者可以直接使用canvas组件进行绘制。或者可以脱离Canvas组件和onReady()生命周期,单独定义Path2d对象构造理想的路径,并在onReady()调用之后使用Canvas组件进行绘制。
116
117- 通过CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象直接调用相关API进行绘制。
118
119  ```ts
120  Canvas(this.context)
121    .width('100%')
122    .height('100%')
123    .backgroundColor('#F5DC62')
124    .onReady(() => {
125      this.context.beginPath();
126      this.context.moveTo(50, 50);
127      this.context.lineTo(280, 160);
128      this.context.stroke();
129     })
130  ```
131
132  ![2023022793719(1)](figures/2023022793719(1).jpg)
133
134- 先单独定义path2d对象构造理想的路径,再通过调用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象的stroke接口或者fill接口进行绘制,具体使用可以参考[Path2D](../reference/apis-arkui/arkui-ts/ts-components-canvas-path2d.md)对象。
135
136  ```ts
137  Canvas(this.context)
138    .width('100%')
139    .height('100%')
140    .backgroundColor('#F5DC62')
141    .onReady(() => {
142       let region = new Path2D();
143       region.arc(100, 75, 50, 0, 6.28);
144       this.context.stroke(region);
145    })
146  ```
147
148  ![2023022794031(1)](figures/2023022794031(1).jpg)
149
150
151## 画布组件常用方法
152
153OffscreenCanvasRenderingContext2D对象和CanvasRenderingContext2D对象提供了大量的属性和方法,可以用来绘制文本、图形,处理像素等,是Canvas组件的核心。常用接口有[fill](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#fill)(对封闭路径进行填充)、[clip](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#clip)(设置当前路径为剪切路径)、[stroke](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroke)(进行边框绘制操作)等等,同时提供了[fillStyle](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#fillstyle)(指定绘制的填充色)、[globalAlpha](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#globalalpha)(设置透明度)与[strokeStyle](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#strokestyle)(设置描边的颜色)等属性修改绘制内容的样式。将通过以下几个方面简单介绍画布组件常见使用方法:
154
155- 基础形状绘制。
156  可以通过[arc](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#arc)(绘制弧线路径)、 [ellipse](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#ellipse)(绘制一个椭圆)、[rect](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#rect)(创建矩形路径)等接口绘制基础形状。
157
158  ```ts
159  Canvas(this.context)
160    .width('100%')
161    .height('100%')
162    .backgroundColor('#F5DC62')
163    .onReady(() => {
164       //绘制矩形
165       this.context.beginPath();
166       this.context.rect(100, 50, 100, 100);
167       this.context.stroke();
168       //绘制圆形
169       this.context.beginPath();
170       this.context.arc(150, 250, 50, 0, 6.28);
171       this.context.stroke();
172       //绘制椭圆
173       this.context.beginPath();
174       this.context.ellipse(150, 450, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2);
175       this.context.stroke();
176    })
177  ```
178
179  ![2023022794521(1)](figures/2023022794521(1).jpg)
180
181- 文本绘制。
182
183  可以通过[fillText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#filltext)(文本填充)、[strokeText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroketext)(文本描边)等接口进行文本绘制,示例中设置了font为50像素高加粗的"sans-serif"字体,然后调用fillText方法在(50, 100)处绘制文本"Hello World!",设置strokeStyle为红色,lineWidth为2,font为50像素高加粗的"sans-serif"字体,然后调用strokeText方法在(50, 150)处绘制文本"Hello World!"的轮廓。
184
185  ```ts
186  Canvas(this.context)
187    .width('100%')
188    .height('100%')
189    .backgroundColor('#F5DC62')
190    .onReady(() => {
191      // 文本填充
192      this.context.font = '50px bolder sans-serif';
193      this.context.fillText("Hello World!", 50, 100);
194      // 文本描边
195      this.context.strokeStyle = "#ff0000"
196      this.context.lineWidth = 2
197      this.context.font = '50px bolder sans-serif';
198      this.context.strokeText("Hello World!", 50, 150);
199    })
200  ```
201
202  ![2023022795105(1)](figures/2023022795105(1).jpg)
203
204- 使用自定义字体绘制文本。
205
206  可以通过[font](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#font)接口(设置文本绘制中的字体样式)加载自定义字体,然后通过[fillText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#filltext)(绘制填充类文本)、[strokeText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroketext)(绘制描边类文本)等接口进行文本绘制。
207
208  ```ts
209  Canvas(this.context)
210    .width('100%')
211    .height('100%')
212    .backgroundColor('#F5DC62')
213    .onReady(() => {
214      //加载自定义字体
215      this.context.font = '30vp customFont'
216      this.context.fillText("Hello World!", 20, 50)
217      this.context.strokeText("Hello World!", 20, 100)
218    })
219  ```
220
221  ![customFont](figures/customFont.jpeg)
222
223- 绘制图片和图像像素信息处理。
224
225  可以通过[drawImage](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#drawimage)(图像绘制)、[putImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#putimagedata)(使用[ImageData](../reference/apis-arkui/arkui-ts/ts-components-canvas-imagedata.md)数据填充新的矩形区域)等接口绘制图片,通过[createImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createimagedata)(创建新的ImageData 对象)、[getPixelMap](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#getpixelmap)(以当前canvas指定区域内的像素创建[PixelMap](../reference/apis-image-kit/js-apis-image.md#pixelmap7)对象)、[getImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#getimagedata)(以当前canvas指定区域内的像素创建ImageData对象)等接口进行图像像素信息处理。
226
227  ```ts
228  @Entry
229  @Component
230  struct GetImageData {
231    private settings: RenderingContextSettings = new RenderingContextSettings(true)
232    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
233    private offCanvas: OffscreenCanvas = new OffscreenCanvas(600, 600)
234    private img: ImageBitmap = new ImageBitmap("/common/images/1234.png")
235
236    build() {
237      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
238        Canvas(this.context)
239          .width('100%')
240          .height('100%')
241          .backgroundColor('#F5DC62')
242          .onReady(() => {
243            let offContext = this.offCanvas.getContext("2d", this.settings)
244            // 使用drawImage接口将图片画在(0,0)为起点,宽高130的区域
245            offContext.drawImage(this.img, 0, 0, 130, 130);
246            // 使用getImageData接口,获得canvas组件区域中,(50,50)为起点,宽高130范围内的绘制内容
247            let imagedata = offContext.getImageData(50, 50, 130, 130);
248            // 使用putImageData接口将得到的ImageData画在起点为(150, 150)的区域中
249            offContext.putImageData(imagedata, 150, 150);
250            // 将离屏绘制的内容画到canvas组件上
251            let image = this.offCanvas.transferToImageBitmap();
252            this.context.transferFromImageBitmap(image);
253          })
254      }
255      .width('100%')
256      .height('100%')
257    }
258  }
259  ```
260
261  ![drawimage](figures/drawimage.PNG)
262
263- 其他方法。
264
265  Canvas中还提供其他类型的方法。渐变([CanvasGradient](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvasgradient.md)对象)相关的方法:[createLinearGradient](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createlineargradient)(创建一个线性渐变色)、[createRadialGradient](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createradialgradient)(创建一个径向渐变色)等。
266
267  ```ts
268  Canvas(this.context)
269    .width('100%')
270    .height('100%')
271    .backgroundColor('#F5DC62')
272    .onReady(() => {
273      //创建一个径向渐变色的CanvasGradient对象
274      let grad = this.context.createRadialGradient(200, 200, 50, 200, 200, 200)
275      //为CanvasGradient对象设置渐变断点值,包括偏移和颜色
276      grad.addColorStop(0.0, '#E87361');
277      grad.addColorStop(0.5, '#FFFFF0');
278      grad.addColorStop(1.0, '#BDDB69');
279      //用CanvasGradient对象填充矩形
280      this.context.fillStyle = grad;
281      this.context.fillRect(0, 0, 400, 400);
282    })
283  ```
284
285  ![2023022700701(1)](figures/2023022700701(1).jpg)
286
287
288## 场景示例
289
290- 规则基础形状绘制。
291
292  ```ts
293  @Entry
294  @Component
295  struct ClearRect {
296    private settings: RenderingContextSettings = new RenderingContextSettings(true);
297    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
298
299    build() {
300      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
301        Canvas(this.context)
302          .width('100%')
303          .height('100%')
304          .backgroundColor('#F5DC62')
305          .onReady(() => {
306            // 设定填充样式,填充颜色设为蓝色
307            this.context.fillStyle = '#0097D4';
308            // 以(50, 50)为左上顶点,画一个宽高200的矩形
309            this.context.fillRect(50, 50, 200, 200);
310            // 以(70, 70)为左上顶点,清除宽150高100的区域
311            this.context.clearRect(70, 70, 150, 100);
312          })
313      }
314      .width('100%')
315      .height('100%')
316    }
317  }
318  ```
319
320  ![2023022701120(1)](figures/2023022701120(1).jpg)
321
322- 不规则图形绘制。
323
324  ```ts
325  @Entry
326  @Component
327  struct Path2d {
328    private settings: RenderingContextSettings = new RenderingContextSettings(true);
329    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
330
331    build() {
332      Row() {
333        Column() {
334          Canvas(this.context)
335            .width('100%')
336            .height('100%')
337            .backgroundColor('#F5DC62')
338            .onReady(() => {
339              // 使用Path2D的接口构造一个五边形
340              let path = new Path2D();
341              path.moveTo(150, 50);
342              path.lineTo(50, 150);
343              path.lineTo(100, 250);
344              path.lineTo(200, 250);
345              path.lineTo(250, 150);
346              path.closePath();
347              // 设定填充色为蓝色
348              this.context.fillStyle = '#0097D4';
349              // 使用填充的方式,将Path2D描述的五边形绘制在canvas组件内部
350              this.context.fill(path);
351            })
352        }
353        .width('100%')
354      }
355      .height('100%')
356    }
357  }
358  ```
359
360  ![2023032422159](figures/2023032422159.jpg)
361
362## 相关实例
363
364使用画布绘制自定义图形,有以下相关实例可供参考:
365
366- [ArkTS组件集(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/UI/ArkTsComponentCollection/ComponentCollection)
367
368- [分布式五子棋(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/Solutions/Game/DistributedDataGobang)
369
370- [ArkTS时钟(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/Solutions/Tools/ArkTSClock)
371
372- [Lottie动画](https://gitee.com/openharmony/applications_app_samples/tree/master/code/Solutions/Game/Lottie)
373
374- [自定义抽奖转盘(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/CanvasComponent)
375<!--RP1--><!--RP1End-->