• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![zh-cn_image_0000001194032666](figures/offscreen_canvas_width.png)
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![zh-cn_image_0000001194032666](figures/offscreen_canvas_height.png)
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![zh-cn_image_0000001194032666](figures/offscreen_canvas_transferToImageBitmap.png)
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![zh-cn_image_0000001194032666](figures/offscreen_canvas.png)
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![offscreenCanvasPostMessage](figures/offscreen_canvas_postMessage.png)
389
390