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 .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 .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.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 .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 .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 .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 .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  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  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 .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 .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  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-->