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