1# OffscreenCanvas 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @sd-wu--> 5<!--Designer: @sunbees--> 6<!--Tester: @liuli0427--> 7<!--Adviser: @HelloCrease--> 8 9OffscreenCanvas组件用于绘制自定义图形。 10 11使用[Canvas](ts-components-canvas-canvas.md)组件或[CanvasRenderingContext2D](ts-canvasrenderingcontext2d.md)时,渲染、动画和用户交互通常发生在应用程序的主线程上,与画布动画和渲染相关的计算可能会影响应用程序性能。OffscreenCanvas提供了一个可以在屏幕外渲染的画布,这样可以在单独的线程中运行一些任务,从而避免影响应用程序主线程性能。 12 13> **说明:** 14> 15> 该组件从API version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 16> 17> OffscreenCanvas无法在ServiceExtensionAbility中使用,ServiceExtensionAbility中建议使用[Drawing模块](../../apis-arkgraphics2d/arkts-apis-graphics-drawing.md)进行离屏绘制。 18 19## 子组件 20 21不支持。 22 23## 构造函数 24 25### constructor 26 27constructor(width: number, height: number) 28 29构造用于创建离屏画布对象的OffscreenCanvas。 30 31**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。 32 33**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 34 35**系统能力:** SystemCapability.ArkUI.ArkUI.Full 36 37**参数:** 38 39| 参数名 | 类型 | 必填 | 说明 | 40| ------ | -------- | ---- | ------------------------------------- | 41| width | number | 是 | OffscreenCanvas组件的宽度。<br>默认单位为vp。 | 42| height | number | 是 | OffscreenCanvas组件的高度。<br>默认单位为vp。 | 43 44### constructor<sup>12+</sup> 45 46constructor(width: number, height: number, unit: LengthMetricsUnit) 47 48构造用于创建离屏画布对象的OffscreenCanvas,支持配置OffscreenCanvas的单位模式。 49 50**卡片能力:** 从API version 12开始,该接口支持在ArkTS卡片中使用。 51 52**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 53 54**系统能力:** SystemCapability.ArkUI.ArkUI.Full 55 56**参数:** 57 58| 参数名 | 类型 | 必填 | 说明 | 59| ------ | -------- | ---- | ------------------------------------- | 60| width | number | 是 | OffscreenCanvas组件的宽度。<br>默认单位为vp。 | 61| height | number | 是 | OffscreenCanvas组件的高度。<br>默认单位为vp。 | 62| unit | [LengthMetricsUnit](../js-apis-arkui-graphics.md#lengthmetricsunit12) | 是 | 用来配置OffscreenCanvas对象的单位模式,配置后无法动态更改,配置方法同[CanvasRenderingContext2D](ts-canvasrenderingcontext2d.md)。<br>默认值:DEFAULT | 63 64## 属性 65 66**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。 67 68**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 69 70**系统能力:** SystemCapability.ArkUI.ArkUI.Full 71 72OffscreenCanvas支持以下属性: 73 74| 名称 | 类型 | 只读 | 可选 | 说明 | 75| ------ | ------ | ------ | ------- | ---- | 76| width | number | 否 | 否 | OffscreenCanvas组件的宽度。<br>默认单位为vp。 | 77| height | number | 否 | 否 | OffscreenCanvas组件的高度。<br>默认单位为vp。 | 78 79### width 80 81```ts 82// xxx.ets 83@Entry 84@Component 85struct OffscreenCanvasPage { 86 private settings: RenderingContextSettings = new RenderingContextSettings(true); 87 private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 88 private offCanvas: OffscreenCanvas = new OffscreenCanvas(200, 300); 89 90 build() { 91 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) { 92 Column() { 93 Canvas(this.context) 94 .width('100%') 95 .height('100%') 96 .borderWidth(5) 97 .borderColor('#057D02') 98 .backgroundColor('#FFFFFF') 99 .onReady(() => { 100 let offContext = this.offCanvas.getContext("2d", this.settings) 101 offContext.fillStyle = '#CDCDCD' 102 offContext.fillRect(0, 0, this.offCanvas.width, 150) 103 let image = this.offCanvas.transferToImageBitmap() 104 this.context.setTransform(1, 0, 0, 1, 50, 200) 105 this.context.transferFromImageBitmap(image) 106 }) 107 } 108 }.width('100%').height('100%') 109 } 110} 111``` 112 113 114 115### height 116 117```ts 118// xxx.ets 119@Entry 120@Component 121struct OffscreenCanvasPage { 122 private settings: RenderingContextSettings = new RenderingContextSettings(true); 123 private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 124 private offCanvas: OffscreenCanvas = new OffscreenCanvas(200, 300); 125 126 build() { 127 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) { 128 Column() { 129 Canvas(this.context) 130 .width('100%') 131 .height('100%') 132 .borderWidth(5) 133 .borderColor('#057D02') 134 .backgroundColor('#FFFFFF') 135 .onReady(() => { 136 let offContext = this.offCanvas.getContext("2d", this.settings) 137 offContext.fillStyle = '#CDCDCD' 138 offContext.fillRect(0, 0, 100, this.offCanvas.height) 139 let image = this.offCanvas.transferToImageBitmap() 140 this.context.setTransform(1, 0, 0, 1, 50, 200) 141 this.context.transferFromImageBitmap(image) 142 }) 143 } 144 }.width('100%').height('100%') 145 } 146} 147``` 148 149 150 151## 方法 152 153### transferToImageBitmap 154 155transferToImageBitmap(): ImageBitmap 156 157从OffscreenCanvas组件中最近渲染的图像创建一个ImageBitmap对象。 158 159**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。 160 161**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 162 163**系统能力:** SystemCapability.ArkUI.ArkUI.Full 164 165**返回值:** 166 167| 类型 | 说明 | 168| -------------------------------------------------- | ----------------------- | 169| [ImageBitmap](ts-components-canvas-imagebitmap.md) | 创建的ImageBitmap对象。 | 170 171**示例:** 172 173```ts 174// xxx.ets 175@Entry 176@Component 177struct OffscreenCanvasPage { 178 private settings: RenderingContextSettings = new RenderingContextSettings(true); 179 private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 180 private offCanvas: OffscreenCanvas = new OffscreenCanvas(400, 600); 181 182 build() { 183 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 184 Canvas(this.context) 185 .width('100%') 186 .height('100%') 187 .borderWidth(5) 188 .borderColor('rgb(39,135,217)') 189 .backgroundColor('#FFFFFF') 190 .onReady(() => { 191 let offContext = this.offCanvas.getContext("2d", this.settings) 192 offContext.fillStyle = '#CDCDCD' 193 offContext.fillRect(0, 0, 400, 600) 194 offContext.fillStyle = '#000000' 195 offContext.font = '40px serif bold' 196 offContext.fillText("Offscreen : Hello World!", 20, 60) 197 let image = this.offCanvas.transferToImageBitmap() 198 this.context.transferFromImageBitmap(image) 199 }) 200 } 201 .width('100%') 202 .height('100%') 203 } 204} 205``` 206 207 208 209### getContext<sup>10+</sup> 210 211getContext(contextType: "2d", options?: RenderingContextSettings): OffscreenCanvasRenderingContext2D 212 213返回OffscreenCanvas组件的绘图上下文。 214 215**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 216 217**系统能力:** SystemCapability.ArkUI.ArkUI.Full 218 219**参数:** 220 221| 参数名 | 类型 | 必填 | 说明 | 222| ----------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 223| contextType | string | 是 | OffscreenCanvas组件绘图上下文的类型,当前仅支持"2d"类型。<br>"2d":创建一个表示二维渲染上下文的OffscreenCanvasRenderingContext2D对象。| 224| options | [RenderingContextSettings](ts-canvasrenderingcontext2d.md#renderingcontextsettings) | 否 | 用来配置OffscreenCanvasRenderingContext2D对象的参数,见[RenderingContextSettings](ts-canvasrenderingcontext2d.md#renderingcontextsettings)。<br>默认值:null | 225 226**返回值:** 227 228| 类型 | 说明 | 229| ------------------------------------------------------------ | --------------------------------- | 230| [OffscreenCanvasRenderingContext2D](ts-offscreencanvasrenderingcontext2d.md) | OffscreenCanvas组件的绘图上下文。如果getContext方法的入参contextType为"2d"以外类型(包括null或者undefined),返回null。 | 231 232**示例:** 233 234```ts 235@Entry 236@Component 237struct OffscreenCanvasExamplePage { 238 private settings: RenderingContextSettings = new RenderingContextSettings(true); 239 private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 240 private offscreenCanvas: OffscreenCanvas = new OffscreenCanvas(600, 800); 241 242 build() { 243 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) { 244 Column() { 245 Canvas(this.context) 246 .width('100%') 247 .height('100%') 248 .backgroundColor('#FFFFFF') 249 .onReady(() => { 250 let offContext = this.offscreenCanvas.getContext("2d", this.settings) 251 offContext.font = '70px sans-serif' 252 offContext.fillText("Offscreen : Hello World!", 20, 60) 253 offContext.fillStyle = "#0000ff" 254 offContext.fillRect(230, 350, 50, 50) 255 offContext.fillStyle = "#EE0077" 256 offContext.translate(70, 70) 257 offContext.fillRect(230, 350, 50, 50) 258 offContext.fillStyle = "#77EE0077" 259 offContext.translate(-70, -70) 260 offContext.fillStyle = "#00ffff" 261 offContext.rotate(45 * Math.PI / 180); 262 offContext.fillRect(180, 120, 50, 50); 263 offContext.rotate(-45 * Math.PI / 180); 264 offContext.beginPath() 265 offContext.moveTo(10, 150) 266 offContext.bezierCurveTo(20, 100, 200, 100, 200, 20) 267 offContext.stroke() 268 offContext.fillStyle = '#FF00FF' 269 offContext.fillRect(100, 100, 60, 60) 270 let imageData = this.offscreenCanvas.transferToImageBitmap() 271 this.context.transferFromImageBitmap(imageData) 272 }) 273 }.width('100%').height('100%') 274 } 275 .width('100%') 276 .height('100%') 277 } 278} 279``` 280 281 282 283 284## OffscreenCanvas支持并发线程绘制 285 286从API version 11开始,当应用创建[Worker线程](../../../arkts-utils/worker-introduction.md),支持使用postMessage将OffscreenCanvas实例传到Worker中进行绘制,并使用onmessage接收Worker线程发送的绘制结果进行显示。 287 288> **说明:** 289> 290> OffscreenCanvas对象使用getContext获取绘图上下文后,不允许通过postMessage传该对象给其他线程,否则抛出异常。 291> 292> 已经通过postMessage传OffscreenCanvas对象到某一线程,声明该对象的线程不允许该对象使用getContext和transferToImageBitmap方法,否则抛出异常。 293> 294> 已经通过postMessage传OffscreenCanvas对象到某一线程,不允许再将该对象通过postMessage传给其他线程,否则抛出异常。 295> 296> DevEco Studio的预览器不支持显示在worker线程中绘制的内容。 297 298**示例:** 299 300```ts 301import { worker } from '@kit.ArkTS'; 302import { image } from '@kit.ImageKit'; 303import { resourceManager } from '@kit.LocalizationKit'; 304import { common } from '@kit.AbilityKit'; 305 306@Entry 307@Component 308struct OffscreenCanvasExamplePage { 309 private settings: RenderingContextSettings = new RenderingContextSettings(true); 310 private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 311 private myWorker = new worker.ThreadWorker('entry/ets/workers/Worker.ts'); 312 private imgPixelMap: image.PixelMap | undefined = undefined 313 314 aboutToAppear(): void { 315 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; 316 const resourceMgr: resourceManager.ResourceManager = context.resourceManager; 317 this.imgPixelMap = resourceMgr.getDrawableDescriptor($r("app.media.startIcon").id).getPixelMap() 318 } 319 320 build() { 321 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) { 322 Column() { 323 Canvas(this.context) 324 .width('100%') 325 .height('100%') 326 .borderWidth(5) 327 .borderColor('#057D02') 328 .backgroundColor('#FFFFFF') 329 .onReady(() => { 330 let offCanvas = new OffscreenCanvas(600, 800) 331 this.myWorker.postMessage({ myOffCanvas: offCanvas, imgPixelMap: this.imgPixelMap }); 332 this.myWorker.onmessage = (e): void => { 333 if (e.data.myImage) { 334 let image: ImageBitmap = e.data.myImage 335 this.context.transferFromImageBitmap(image) 336 } 337 } 338 }) 339 } 340 .width('100%') 341 .height('100%') 342 } 343 .width('100%') 344 .height('100%') 345 } 346} 347``` 348 349Worker线程在onmessage中接收到主线程postMessage发送的OffscreenCanvas,并进行绘制。 350 351```ts 352import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; 353import { image } from '@kit.ImageKit'; 354 355const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 356 357workerPort.onmessage = (e: MessageEvents) => { 358 if (e.data.myOffCanvas) { 359 let offCanvas: OffscreenCanvas = e.data.myOffCanvas 360 let offContext = offCanvas.getContext("2d") 361 offContext.fillStyle = '#CDCDCD' 362 offContext.fillRect(0, 0, 200, 150) 363 364 let imgPixelMap: image.PixelMap = e.data.imgPixelMap 365 let imgBitmap: ImageBitmap = new ImageBitmap(imgPixelMap) 366 offContext.drawImage(imgBitmap, 0, 200) 367 368 let path2d = new Path2D("M250 150 L150 350 L350 350 Z") 369 offContext.stroke(path2d) 370 371 let matrix: Matrix2D = new Matrix2D() 372 matrix.scaleX = 1 373 matrix.scaleY = 1 374 matrix.rotateX = -0.5 375 matrix.rotateY = 0.5 376 matrix.translateX = 10 377 matrix.translateY = 10 378 offContext.setTransform(matrix) 379 offContext.fillStyle = "#707070" 380 offContext.fillRect(20, 20, 100, 100) 381 382 let image = offCanvas.transferToImageBitmap() 383 workerPort.postMessage({ myImage: image }); 384 } 385} 386``` 387 388 389 390