• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Drawing Custom Graphics Using the Canvas (Canvas)
2
3
4**Canvas** provides a canvas component for drawing custom graphics. You can use the **CanvasRenderingContext2D** and **OffscreenCanvasRenderingContext2D** objects to draw graphics on the **Canvas** component. The drawing objects can be basic shapes, text, and images.
5
6
7## Drawing Custom Graphics on the Canvas
8
9You can draw custom graphics on the canvas in any of the following ways:
10
11
12- Use [CanvasRenderingContext2D](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md).
13
14  ```ts
15  @Entry
16  @Component
17  struct CanvasExample1 {
18    // Configure the parameters of the CanvasRenderingContext2D object, including whether to enable anti-aliasing. The value true indicates that anti-aliasing is enabled.
19    private settings: RenderingContextSettings = new RenderingContextSettings(true)
20    // Create a CanvasRenderingContext2D object by calling CanvasRenderingContext2D object in Canvas.
21    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
22
23    build() {
24      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
25        // Invoke the CanvasRenderingContext2D object in Canvas.
26        Canvas(this.context)
27          .width('100%')
28          .height('100%')
29          .backgroundColor('#F5DC62')
30          .onReady(() => {
31            // You can draw content here.
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- Drawing offscreen onto a canvas is a process where content to draw onto the canvas is first drawn in the buffer, and then converted into a picture, and finally the picture is drawn on the canvas. This process increases the drawing efficiency. Specifically, the implementation is as follows:
44  1. Use the **transferToImageBitmap** API to create an **ImageBitmap** object for the image that is recently rendered off the screen canvas.
45  2. Use the **transferFromImageBitmap** API of the **CanvasRenderingContext2D** object to display the given **ImageBitmap** object.
46
47    For details, see [OffscreenCanvasRenderingContext2D](../reference/apis-arkui/arkui-ts/ts-offscreencanvasrenderingcontext2d.md).
48
49  ```ts
50  @Entry
51  @Component
52  struct CanvasExample2 {
53    // Configure the parameters of the CanvasRenderingContext2D and OffscreenCanvasRenderingContext2D objects, including whether to enable anti-aliasing. The value true indicates that anti-aliasing is enabled.
54    private settings: RenderingContextSettings = new RenderingContextSettings(true)
55    private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
56    // Create an OffscreenCanvas object. width indicates the width of the offscreen canvas, and height indicates the height of the offscreen canvas.
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            // You can draw content here.
68            offContext.strokeRect(50, 50, 200, 150);
69            // Display the image rendered by the offscreen drawing value on the common canvas.
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  >**NOTE**
83  >
84  >The APIs called for drawing on the canvas through the **CanvasRenderingContext2D** and **OffscreenCanvasRenderingContext2D** objects are the same. Unless otherwise specified, the value unit of the parameters in these APIs is vp.
85
86- Before loading the Lottie animation on the canvas, download the Lottie as follows:
87
88  ```ts
89  import lottie from '@ohos/lottie'
90  ```
91
92  For details about the APIs, see [Lottie](https://gitee.com/openharmony-tpc/lottieETS).
93
94
95## Initializing the Canvas Component
96
97**onReady(event: () => void)** is the event callback when the **Canvas** component initialization is complete. After this event is called, the determined width and height of the **Canvas** component can be obtained. The **CanvasRenderingContext2D** and **OffscreenCanvasRenderingContext2D** objects can then be used to call related APIs to draw graphics.
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## Canvas Component Drawing Modes
114
115After **onReady()** is invoked, you can use the **Canvas** component for drawing. Alternatively, you can separately define the **Path2d** object to build an ideal path without the **Canvas** component and **onReady()** lifecycle callback, and then use the **Canvas** component for drawing after **onReady()** is invoked.
116
117- After the **onReady()** callback of the **Canvas** component is invoked, use the **CanvasRenderingContext2D** and **OffscreenCanvasRenderingContext2D** objects to call related APIs for drawing.
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- Define an individual **path2d** object to build an ideal path, and then call the **stroke** or **fill** API of the **CanvasRenderingContext2D** and **OffscreenCanvasRenderingContext2D** objects to draw the path. For details, see [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## Common Usage of the Canvas Component
152
153**OffscreenCanvasRenderingContext2D** and **CanvasRenderingContext2D** provide a large number of attributes and methods, which can be used to draw text and graphics and process pixels. They are the core of the **Canvas** component. Common APIs include [fill](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#fill), [clip](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#clip), and [stroke](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroke). In addition, attributes such as [fillStyle](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#fillstyle), [globalAlpha](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#globalalpha), and [strokeStyle](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#strokestyle) are provided to spruce up the graphics. This topic describes typical usage of the canvas.
154
155- Draw a basic shape.
156  You can draw a basic shape by calling APIs such as [arc](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#arc), [ellipse](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#ellipse), and [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       // Draw a rectangle.
165       this.context.beginPath();
166       this.context.rect(100, 50, 100, 100);
167       this.context.stroke();
168       // Draw a circle on the canvas.
169       this.context.beginPath();
170       this.context.arc(150, 250, 50, 0, 6.28);
171       this.context.stroke();
172       // Draw an oval on the canvas.
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- Draw text.
182
183  You can use APIs such as [fillText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#filltext) and [strokeText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroketext) to draw text. In the example, the **font** attribute is set to a bold, 50 px high "sans-serif" font. The **fillText** API is used to draw the text "Hello World!" at the position (50, 100). In addition, the **strokeText** API is used to draw the outline of the text "Hello World!" at the position (50, 150) with a red stroke style and a line width of 2.
184
185  ```ts
186  Canvas(this.context)
187    .width('100%')
188    .height('100%')
189    .backgroundColor('#F5DC62')
190    .onReady(() => {
191      // Draw filled text on the canvas.
192      this.context.font = '50px bolder sans-serif';
193      this.context.fillText("Hello World!", 50, 100);
194      // Draw a text stroke on the canvas.
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- Draw text with a custom font.
205
206  You can use the [font](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#font) API to load a custom font, and then use the [fillText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#filltext) and [strokeText](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#stroketext) APIs to draw text.
207
208  ```ts
209  Canvas(this.context)
210    .width('100%')
211    .height('100%')
212    .backgroundColor('#F5DC62')
213    .onReady(() => {
214      // Load a custom font.
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- Draw images and processes image pixel information.
224
225  You can draw an image by calling APIs such as [drawImage](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#drawimage) and [putImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#putimagedata). You can also process image pixel information by calling APIs such as [createImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createimagedata), [getPixelMap](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#getpixelmap), and [getImageData](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#getimagedata).
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            // Use the drawImage API to draw an image in the area with the width and height of 130 starting from (0, 0).
245            offContext.drawImage(this.img, 0, 0, 130, 130);
246            // Use the getImageData API to obtain the image data with the width and height of 130 starting from (50, 50).
247            let imagedata = offContext.getImageData(50, 50, 130, 130);
248            // Use the putImageData API to draw the obtained image data in the area starting from (150, 150).
249            offContext.putImageData(imagedata, 150, 150);
250            // Draw the offscreen drawing content to the 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- Other usage
264
265  **Canvas** also provides other usage. For example, regarding [CanvasGradient](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvasgradient.md), you can create a linear gradient with [createLinearGradient](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createlineargradient) or create a radial gradient with [createRadialGradient](../reference/apis-arkui/arkui-ts/ts-canvasrenderingcontext2d.md#createradialgradient), among others.
266
267  ```ts
268  Canvas(this.context)
269    .width('100%')
270    .height('100%')
271    .backgroundColor('#F5DC62')
272    .onReady(() => {
273      // Create a CanvasGradient object with radial gradient colors.
274      let grad = this.context.createRadialGradient(200, 200, 50, 200, 200, 200)
275      // Set the gradient color stop for the CanvasGradient object, including the offset and colors.
276      grad.addColorStop(0.0, '#E87361');
277      grad.addColorStop(0.5, '#FFFFF0');
278      grad.addColorStop(1.0, '#BDDB69');
279      // Fill the rectangle with the CanvasGradient object.
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## Example Scenario
289
290- Draw a basic shape.
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            // Set the fill color to blue.
307            this.context.fillStyle = '#0097D4';
308            // Take (50, 50) as the upper left corner and draw a rectangle with the width and height of 200.
309            this.context.fillRect(50, 50, 200, 200);
310            // Use (70, 70) as the upper left corner and clear the area with the width of 150 and height of 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- Draw an irregular shape.
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              // Use the Path2D API to create a pentagon.
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              // Set the fill color to blue.
348              this.context.fillStyle = '#0097D4';
349              // Draw the pentagon described by Path2D in the canvas in fill mode.
350              this.context.fill(path);
351            })
352        }
353        .width('100%')
354      }
355      .height('100%')
356    }
357  }
358  ```
359
360  ![2023032422159](figures/2023032422159.jpg)
361