• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using Same-Layer Rendering
2
3In the system, applications can use the **Web** component to load web pages. If the capability or performance of non-native UI components (same-layer components) is inferior to that of native components, you can use the ArkUI component to render these components.
4
5## When to Use
6### On the Web Page
7To improve the performance of an applet, you can use the ArkUI **XComponent** component to render the map component, and use the ArkUI **TextInput** component to render the input box component.
8- On the web page, you can render the UI components (same-layer tags) such as **\<embed>** and **\<object>** at the same layer based on certain rules. For details, see [Specifications and Constraints](#specifications-and-constraints).
9
10- On the application, you can use the same-layer rendering event reporting API of the **Web** component to detect the lifecycle and input event of the HTML5 same-layer tags, and process the service logic of the same-layer rendering components.
11
12- In addition, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendering components corresponding to HTML5 same-layer tags. Common ArkUI components that support same-layer rendering: [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/ts-basic-components-web.md). For details, see [Specifications and Constraints](#specifications-and-constraints).
13
14### On the Third-Party UI Framework
15Flutter provides the **PlatformView** and **Texture** abstract components that can be rendered using native components, which complete the functions of the Flutter components. Weex2.0 framework supports the **Camera**, **Video**, and **Canvas** components.
16
17- Since third-party frameworks such as Flutter and Weex are not operated in the OS, the available third-party framework UI components that can be rendered at the same layer are not listed in the following.
18
19- On the application, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendering components corresponding to third-party framework same-layer tags. Common ArkUI components that support same-layer rendering: [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/ts-basic-components-web.md). For details, see [Specifications and Constraints](#specifications-and-constraints).
20
21## Overall Architecture
22The ArkWeb same-layer rendering feature supports same-layer tag lifecycle and event hit forwarding.
23
24The lifecycle of same-layer tags is associated with front-end tags (\<embed>/\<object>). Events that hit the same-layer tags are reported to you, and you should distribute them to the corresponding component tree. The following figure shows the overall framework.
25
26**Figure 1** Overall architecture of same-layer rendering
27
28![web-same-layer](figures/web-same-layer-develop-architecture.png)
29
30## Specifications and Constraints
31### ArkUI Components That Can Be Rendered at the Same Layer
32
33The following specifications take effect in both web pages and third-party frameworks.
34
35**Supported Components**:
36
37- Basic components: [AlphabetIndexer](../reference/apis-arkui/arkui-ts/ts-container-alphabet-indexer.md), [Blank](../reference/apis-arkui/arkui-ts/ts-basic-components-blank.md), [Button](../reference/apis-arkui/arkui-ts/ts-basic-components-button.md), [CalendarPicker](../reference/apis-arkui/arkui-ts/ts-basic-components-calendarpicker.md), [Checkbox](../reference/apis-arkui/arkui-ts/ts-basic-components-checkbox.md), [CheckboxGroup](../reference/apis-arkui/arkui-ts/ts-basic-components-checkboxgroup.md), [ContainerSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-containerspan.md), [DataPanel](../reference/apis-arkui/arkui-ts/ts-basic-components-datapanel.md), [DatePicker](../reference/apis-arkui/arkui-ts/ts-basic-components-datepicker.md), [Divider](../reference/apis-arkui/arkui-ts/ts-basic-components-divider.md), [Gauge](../reference/apis-arkui/arkui-ts/ts-basic-components-gauge.md), [Hyperlink](../reference/apis-arkui/arkui-ts/ts-container-hyperlink.md), [Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md), [ImageAnimator](../reference/apis-arkui/arkui-ts/ts-basic-components-imageanimator.md), [ImageSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-imagespan.md), [LoadingProgress](../reference/apis-arkui/arkui-ts/ts-basic-components-loadingprogress.md), [Marquee](../reference/apis-arkui/arkui-ts/ts-basic-components-marquee.md), [PatternLock](../reference/apis-arkui/arkui-ts/ts-basic-components-patternlock.md), [Progress](../reference/apis-arkui/arkui-ts/ts-basic-components-progress.md), [QRCode](../reference/apis-arkui/arkui-ts/ts-basic-components-qrcode.md), [Radio](../reference/apis-arkui/arkui-ts/ts-basic-components-radio.md), [Rating](../reference/apis-arkui/arkui-ts/ts-basic-components-rating.md), [Refresh](../reference/apis-arkui/arkui-ts/ts-container-refresh.md), [ScrollBar](../reference/apis-arkui/arkui-ts/ts-container-scroll.md), [Search](../reference/apis-arkui/arkui-ts/ts-basic-components-search.md), [Span](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md), [Select](../reference/apis-arkui/arkui-ts/ts-basic-components-select.md), [Slider](../reference/apis-arkui/arkui-ts/ts-basic-components-slider.md), [Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md), [TextArea](../reference/apis-arkui/arkui-ts/ts-basic-components-textarea.md), [TextClock](../reference/apis-arkui/arkui-ts/ts-basic-components-textclock.md), [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [TextPicker](../reference/apis-arkui/arkui-ts/ts-basic-components-textpicker.md), [TextTimer](../reference/apis-arkui/arkui-ts/ts-basic-components-texttimer.md), [TimePicker](../reference/apis-arkui/arkui-ts/ts-basic-components-timepicker.md), [Toggle](../reference/apis-arkui/arkui-ts/ts-basic-components-toggle.md)
38
39- Container components: [Badge](../reference/apis-arkui/arkui-ts/ts-container-badge.md), [Column](../reference/apis-arkui/arkui-ts/ts-container-column.md), [ColumnSplit](../reference/apis-arkui/arkui-ts/ts-container-columnsplit.md), [Counter](../reference/apis-arkui/arkui-ts/ts-container-counter.md), [Flex](../reference/apis-arkui/arkui-ts/ts-container-flex.md), [GridCol](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md), [GridRow](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md), [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md), [GridItem](../reference/apis-arkui/arkui-ts/ts-container-griditem.md) and [List](../reference/apis-arkui/arkui-ts/ts-container-list.md), [ListItem](../reference/apis-arkui/arkui-ts/ts-container-listitem.md), [ListItemGroup](../reference/apis-arkui/arkui-ts/ts-container-listitemgroup.md), [RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md), [Row](../reference/apis-arkui/arkui-ts/ts-container-row.md), [RowSplit](../reference/apis-arkui/arkui-ts/ts-container-rowsplit.md), [Scroll](../reference/apis-arkui/arkui-ts/ts-container-scroll.md), [Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md), [Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md), [Tabs](../reference/apis-arkui/arkui-ts/ts-container-tabs.md), [TabContent](../reference/apis-arkui/arkui-ts/ts-container-tabcontent.md), [NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md), [SideBarContainer](../reference/apis-arkui/arkui-ts/ts-container-sidebarcontainer.md), [Stepper](../reference/apis-arkui/arkui-ts/ts-basic-components-stepper.md), [StepperItem](../reference/apis-arkui/arkui-ts/ts-basic-components-stepperitem.md), [WaterFlow](../reference/apis-arkui/arkui-ts/ts-container-waterflow.md), [FlowItem](../reference/apis-arkui/arkui-ts/ts-container-flowitem.md)
40
41- Self-drawing components: [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/ts-basic-components-web.md)
42
43- Command-based custom drawing nodes: [BuilderNode](../reference/apis-arkui/js-apis-arkui-builderNode.md), [ComponentContent](../reference/apis-arkui/js-apis-arkui-ComponentContent.md), [ContentSlot](../reference/apis-arkui/arkui-ts/ts-components-contentSlot.md), [FrameNode](../reference/apis-arkui/js-apis-arkui-frameNode.md), [Graphics](../reference/apis-arkui/js-apis-arkui-graphics.md), [NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md), [RenderNode](../reference/apis-arkui/js-apis-arkui-renderNode.md), [XComponentNode](../reference/apis-arkui/js-apis-arkui-xcomponentNode.md), [AttributeUpdater](../reference/apis-arkui/js-apis-arkui-AttributeUpdater.md) and [CAPI](../reference/apis-arkui/_ark_u_i___native_module.md) (The components that support same-layer rendering are the same as that of ArkTS.)
44
45**Supported Common Component Attributes and Events**:
46
47- Common attributes that are not supported: [restoreId](../reference/apis-arkui/arkui-ts/ts-universal-attributes-restoreId.md) and [Special Effect Drawing Combination](../reference/apis-arkui/arkui-ts/ts-universal-attributes-use-effect.md).
48
49- Other attributes, events, and component capabilities that are not clearly marked as not supported are supported by default.
50
51### Same-Layer Rendering Tags of the Web Page
52This specification applies only to web pages and does not apply to third-party frameworks.
53
54If an application needs to use the same-layer rendering on a web page loaded by the **Web** component, you need to specify the **\<embed>** and **\<object>** tags on the web page as the same-layer rendering components based on the following specifications.
55
56**Supported Devices**:
57Currently, only mobile phones and tablets are supported.
58
59**Supported HTML5 Tags**:
60- **\<embed>**: After same-layer rendering is enabled, only tags whose type is prefixed with **native** can be identified as same-layer components. Attributes cannot be customized.
61
62- **\<object>**: After the same-layer rendering is enabled, the **\<object>** tag of the non-standard **MIME** type can be identified as a same-layer component and parsed based on the custom **param**/**value** attribute.
63
64- W3C standard tags (such as **\<input>** and **\<video>**) cannot be defined as same-layer tags.
65
66- The **\<object>** and **\<embed>** tags cannot be configured as the same-layer tags at the same time.
67
68- The tag types contain only English characters and are case insensitive.
69
70**Supported CSS Attributes**:
71
72**display**, **position**, **z-index**, **visibility**, **opacity**,
73**background-color**, **background-image**, **width**, **height**, **padding**, **padding-left**, **padding-top**, **padding-right**, **padding-bottom**, **margin**, **margin-left**, **margin-top**, **margin-right**, **margin-bottom**, **border-width**, **border-style**, **border-color**, **border-left-width**, **border-left-style**, **border-left-color**, **border-top-width**, **border-top-style**, **border-top-color**, **border-right-width**, **border-right-style**, **border-right-color**, **border-bottom-width**, **border-bottom-style**, **border-bottom-color**, **border-left**, **border-right**, **border-top**, **border-bottom**, **border**, **border-top-left-radius**, **border-top-right-radius**, **border-bottom-left-radius**, **border-bottom-right-radius**, **border-radius**, **transition**, and **transform** (Only **translate** and **scale** are supported. The value of **scale** must be greater than or equal to 0.)
74
75 Other CSS attributes, such as **rotate** and **shew** in the **transform** attribute, may not meet the expectation.
76
77**Lifecycle Management**:
78The [onNativeEmbedLifecycleChange()](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) callback is triggered when the lifecycle of the **Embed** tag changes.
79
80- Creation, destruction, and position width and height change are supported. The visibility status change is not supported.
81
82- Web pages containing same-layer components support back-forward cache.
83
84**Distributing and Processing the Input Events**:
85- The **DOWN**, **UP**, **MOVE**, and **CANCEL** touch events are supported. The [onnativeembedgestureevent11](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) can be configured. By default, the touch event is consumed on the application side.
86
87- The application page containing same-layer components cannot be scaled, and the scaling APIs such as [initialScale](../reference/apis-arkweb/ts-basic-components-web.md#initialscale9), [zoom](../reference/apis-arkweb/js-apis-webview.md#zoom), [zoomIn](../reference/apis-arkweb/js-apis-webview.md#zoomin) and [zoomOut](../reference/apis-arkweb/js-apis-webview.md#zoomout) are not supported.
88
89- Mouse, keyboard, and touchpad events are not supported.
90
91**Constraints**:
92
93- Configure a maximum of five same-layer tags on a web page. Otherwise, the rendering performance deteriorates.
94
95- Due to GPU restrictions, the maximum height and texture size of a same-layer tag are 8000 px.
96
97- When same-layer rendering is enabled, web pages opened by the **Web** component do not support [RenderMode](../reference/apis-arkweb/ts-basic-components-web.md#rendermode12).
98
99- When the non-full-screen mode is changed to the full-screen mode, the **Video** component is exported through non-texture mode and the video playback status remains unchanged. When the non-full-screen mode is restored, the **Video** component is exported through texture mode and the video playback status remains unchanged.
100
101- The **Web** component supports only same-layer rendering nesting at one layer. The input events such as swipe, tap, zoom, and long-press are supported, while drag and rotate events are not supported.
102
103- In the page layout of ArkUI components (such as **TextInput**), you are advised to use a **Stack** component to wrap the same-layer **NodeContainer** and **BuilderNode** and ensure that they are in the same position. In addition, the **NodeContainer** must be aligned with the **\<embed>**/**\<object>** tag to ensure proper component interaction. If the positions of the two components are different, the following problems may occur: The position of the text selection box attached to the **TextInput**/**TextArea** component is incorrect (as shown in the following figure). The animation start and stop of the **LoadingProgress**/**Marquee** component do not match the visibility status of the component.
104
105  **Figure 2** Misplaced **TextInput** without **Stack**
106
107  ![web-same-layer-develop](figures/web-same-layer-develop-textinput1.png)
108
109  **Figure 3** Proper **TextInput** with **Stack**
110
111  ![web-same-layer-develop](figures/web-same-layer-develop-textinput2.png)
112
113## Rendering Text Boxes at the Same Layer on Web Pages
114On web pages, you can render the native ArkUI **TextInput** components at the same layer. The following figure shows the effect of three text boxes that are rendered at the same layer.
115
116**Figure 4** Same-layer rendering text boxes
117
118  ![web-same-layer-develop](figures/web-same-layer-develop-input.png)
119
1201. Mark the HTML tags that need to be rendered at the same layer on the web page.
121
122   The **\<embed>** and **\<object>** tags support same-layer rendering, and the **type** can be specified randomly. They are case insensitive and will be converted to lowercase letters by the ArkWeb kernel. The **tag** string is matched using the entire string, and the **type** string is matched using the prefix.
123
124   If this API is not used or receives an invalid string (empty string), the ArkWeb kernel uses the default setting, that is, "embed" + "native/" prefix. If the specified **type** is the same as the W3C standard **object** or **embed** type, for example, **registerNativeEmbedRule** ("**object**," "**application**/**pdf**"), ArkWeb will comply with the W3C standard behavior and will not identify it as a same-layer tag.
125
126   - Use the \<embed> tags.
127
128     ```html
129     <!--HAP's src/main/resources/rawfile/text.html-->
130     <!DOCTYPE html>
131     <html>
132     <head>
133         <title>Same-Layer Rendering Test HTML</title>
134         <meta name="viewport">
135     </head>
136
137     <body style="background:white">
138
139     <embed id = "input1" type="native/view" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"/>
140
141     <embed id = "input2" type="native/view2" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
142
143     <embed id = "input3" type="native/view3" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
144
145     </body>
146     </html>
147     ```
148
149   - Use the \<object> tags.
150
151     Call **registerNativeEmbedRule** to register a **\<object>** tag.
152     ```ts
153     // ...
154     Web({src: $rawfile("text.html"), controller: this.browserTabController})
155       // Register the same-layer tag of "object" and type of "test."
156       .registerNativeEmbedRule("object", "test")
157       // ...
158     ```
159
160     Example of using **registerNativeEmbedRule** on the frontend page, with the tag of "object" and type of "test":
161
162      ```html
163      <!--HAP's src/main/resources/rawfile/text.html-->
164      <!DOCTYPE html>
165      <html>
166      <head>
167          <title>Same-Layer Rendering Test HTML</title>
168          <meta name="viewport">
169      </head>
170
171      <body style="background:white">
172
173      <object id = "input1" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"></object>
174
175      <object id = "input2" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
176
177      <object id = "input3" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
178
179      </body>
180      </html>
181      ```
182
1832. Use **enableNativeEmbedMode** to enable the same-layer rendering on the application,
184
185   which is disabled by default.
186
187   ```ts
188   // xxx.ets
189   import { webview } from '@kit.ArkWeb';
190   @Entry
191   @Component
192   struct WebComponent {
193     controller: webview.WebviewController = new webview.WebviewController();
194
195     build() {
196       Column() {
197         Web({ src: 'www.example.com', controller: this.controller })
198           // Enable same-layer rendering.
199           .enableNativeEmbedMode(true)
200       }
201     }
202   }
203   ```
204
2053. Create a custom component,
206
207   which is displayed as a native component in the corresponding area after the same-layer rendering is enabled.
208
209   ```ts
210   @Component
211   struct TextInputComponent {
212     @Prop params: Params
213     @State bkColor: Color = Color.White
214
215     build() {
216       Column() {
217         TextInput({text: '', placeholder: 'please input your word...'})
218           .placeholderColor(Color.Gray)
219           .id(this.params?.elementId)
220           .placeholderFont({size: 13, weight: 400})
221           .caretColor(Color.Gray)
222           .width(this.params?.width)
223           .height(this.params?.height)
224           .fontSize(14)
225           .fontColor(Color.Black)
226       }
227       // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
228       .width(this.params.width)
229       .height(this.params.height)
230     }
231   }
232
233   @Builder
234   function TextInputBuilder(params:Params) {
235     TextInputComponent({params: params})
236       .width(params.width)
237       .height(params.height)
238       .backgroundColor(Color.White)
239   }
240   ```
241
2424. Create a node controller,
243
244   which is used to control and report node behaviors of the corresponding NodeContainer.
245
246   ```ts
247   class MyNodeController extends NodeController {
248     private rootNode: BuilderNode<[Params]> | undefined | null;
249     private embedId_: string = "";
250     private surfaceId_: string = "";
251     private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
252     private width_: number = 0;
253     private height_: number = 0;
254     private type_: string = "";
255     private isDestroy_: boolean = false;
256
257     setRenderOption(params: NodeControllerParams) {
258       this.surfaceId_ = params.surfaceId;
259       this.renderType_ = params.renderType;
260       this.embedId_ = params.embedId;
261       this.width_ = params.width;
262       this.height_ = params.height;
263       this.type_ = params.type;
264     }
265
266     // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
267     // Called when the corresponding NodeContainer is created or called by the rebuild method.
268     makeNode(uiContext: UIContext): FrameNode | null {
269       if (this.isDestroy_) { // rootNode is null.
270         return null;
271       }
272       if (!this.rootNode) { // When rootNode is set to undefined
273         this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
274         if(this.rootNode) {
275           this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
276           return this.rootNode.getFrameNode();
277         }else{
278           return null;
279         }
280       }
281       // Return the FrameNode object.
282       return this.rootNode.getFrameNode();
283     }
284
285     setBuilderNode(rootNode: BuilderNode<Params[]> | null): void {
286       this.rootNode = rootNode;
287     }
288
289     getBuilderNode(): BuilderNode<[Params]> | undefined | null {
290       return this.rootNode;
291     }
292
293     updateNode(arg: Object): void {
294       this.rootNode?.update(arg);
295     }
296
297     getEmbedId(): string {
298       return this.embedId_;
299     }
300
301     setDestroy(isDestroy: boolean): void {
302       this.isDestroy_ = isDestroy;
303       if (this.isDestroy_) {
304         this.rootNode = null;
305       }
306     }
307
308     postEvent(event: TouchEvent | undefined): boolean {
309       return this.rootNode?.postTouchEvent(event) as boolean
310     }
311   }
312   ```
313
3145. Call [onNativeEmbedLifecycleChange](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) to listen for the lifecycle changes of the same-layer rendering tags.
315
316   After this function is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedLifecycleChange](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) each time a same-layer rendering tag is used on a web page.
317
318
319
320    ```ts
321    build() {
322      Row() {
323        Column() {
324          Stack() {
325            ForEach(this.componentIdArr, (componentId: string) => {
326              NodeContainer(this.nodeControllerMap.get(componentId))
327                .position(this.positionMap.get(componentId))
328                .width(this.widthMap.get(componentId))
329                .height(this.heightMap.get(componentId))
330            }, (embedId: string) => embedId)
331            // Load the local text.html page.
332            Web({src: $rawfile("text.html"), controller: this.browserTabController})
333              // Enable same-layer rendering.
334              .enableNativeEmbedMode(true)
335                // Register the same-layer tag of "object" and type of "test."
336              .registerNativeEmbedRule("object", "test")
337                // Obtain the lifecycle change data of the embed tag.
338              .onNativeEmbedLifecycleChange((embed) => {
339                console.log("NativeEmbed surfaceId" + embed.surfaceId);
340                // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
341                const componentId = embed.info?.id?.toString() as string
342                if (embed.status == NativeEmbedStatus.CREATE) {
343                  console.log("NativeEmbed create" + JSON.stringify(embed.info));
344                  // Create a NodeController instance, set parameters, and rebuild.
345                  let nodeController = new MyNodeController()
346                  // The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
347                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
348                    type : embed.info?.type as string,
349                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
350                    embedId : embed.embedId as string,
351                    width : px2vp(embed.info?.width),
352                    height : px2vp(embed.info?.height)})
353                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
354                  nodeController.setDestroy(false);
355                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
356                  this.nodeControllerMap.set(componentId, nodeController);
357                  this.widthMap.set(componentId, px2vp(embed.info?.width));
358                  this.heightMap.set(componentId, px2vp(embed.info?.height));
359                  this.positionMap.set(componentId, this.edges);
360                  // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
361                  this.componentIdArr.push(componentId)
362                } else if (embed.status == NativeEmbedStatus.UPDATE) {
363                  let nodeController = this.nodeControllerMap.get(componentId);
364                  console.log("NativeEmbed update" + JSON.stringify(embed));
365                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
366                  this.positionMap.set(componentId, this.edges);
367                  this.widthMap.set(componentId, px2vp(embed.info?.width));
368                  this.heightMap.set(componentId, px2vp(embed.info?.height));
369                  nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
370                } else if (embed.status == NativeEmbedStatus.DESTROY) {
371                  console.log("NativeEmbed destroy" + JSON.stringify(embed));
372                  let nodeController = this.nodeControllerMap.get(componentId);
373                  nodeController?.setDestroy(true)
374                  this.nodeControllerMap.clear();
375                  this.positionMap.delete(componentId);
376                  this.widthMap.delete(componentId);
377                  this.heightMap.delete(componentId);
378                  this.componentIdArr.filter((value: string) => value != componentId)
379                } else {
380                  console.log("NativeEmbed status" + embed.status);
381                }
382              })
383          }.height("80%")
384        }
385      }
386    }
387    ```
388
3896. Call [onNativeEmbedGestureEvent](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) to listen for gesture events that are rendered at the same layer.
390
391   When gesture events are listened, the ArkWeb kernel triggers the callback registered by [onNativeEmbedGestureEvent](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) each time a touch operation is performed in the same-layer rendering region.
392
393
394
395    ```ts
396    build() {
397      Row() {
398        Column() {
399          Stack() {
400            ForEach(this.componentIdArr, (componentId: string) => {
401              NodeContainer(this.nodeControllerMap.get(componentId))
402                .position(this.positionMap.get(componentId))
403                .width(this.widthMap.get(componentId))
404                .height(this.heightMap.get(componentId))
405            }, (embedId: string) => embedId)
406            // Load the local text.html page.
407            Web({src: $rawfile("text.html"), controller: this.browserTabController})
408              // Enable same-layer rendering.
409              .enableNativeEmbedMode(true)
410                // Obtain the lifecycle change data of the embed tag.
411              .onNativeEmbedLifecycleChange((embed) => {
412                // Implement lifecycle changes.
413              })
414              .onNativeEmbedGestureEvent((touch) => {
415                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
416                this.componentIdArr.forEach((componentId: string) => {
417                  let nodeController = this.nodeControllerMap.get(componentId);
418                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
419                  if(nodeController?.getEmbedId() == touch.embedId) {
420                    let ret = nodeController?.postEvent(touch.touchEvent)
421                    if(ret) {
422                      console.log("onNativeEmbedGestureEvent success " + componentId);
423                    } else {
424                      console.log("onNativeEmbedGestureEvent fail " + componentId);
425                    }
426                    if(touch.result) {
427                      // Notify the Web component of the gesture event consumption result.
428                      touch.result.setGestureEventResult(ret);
429                    }
430                  }
431                })
432              })
433          }
434        }
435      }
436    }
437    ```
438
439**Sample Code**
440
441To start with, add the Internet permission to the **module.json5** file. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md).
442
443  ```
444  "requestPermissions":[
445      {
446        "name" : "ohos.permission.INTERNET"
447      }
448    ]
449  ```
450
451Code on the application side:
452
453  ```ts
454  // Create a NodeController instance.
455  import webview from '@ohos.web.webview';
456  import { UIContext } from '@ohos.arkui.UIContext';
457  import { NodeController, BuilderNode, NodeRenderType, FrameNode } from "@ohos.arkui.node";
458
459  @Observed
460  declare class Params{
461    elementId: string
462    textOne: string
463    textTwo: string
464    width: number
465    height: number
466  }
467
468  declare class NodeControllerParams {
469    surfaceId: string
470    type: string
471    renderType: NodeRenderType
472    embedId: string
473    width: number
474    height: number
475  }
476
477  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
478  class MyNodeController extends NodeController {
479    private rootNode: BuilderNode<[Params]> | undefined | null;
480    private embedId_: string = "";
481    private surfaceId_: string = "";
482    private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
483    private width_: number = 0;
484    private height_: number = 0;
485    private type_: string = "";
486    private isDestroy_: boolean = false;
487
488    setRenderOption(params: NodeControllerParams) {
489      this.surfaceId_ = params.surfaceId;
490      this.renderType_ = params.renderType;
491      this.embedId_ = params.embedId;
492      this.width_ = params.width;
493      this.height_ = params.height;
494      this.type_ = params.type;
495    }
496
497    // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
498    // Called when the corresponding NodeContainer is created or called by the rebuild method.
499    makeNode(uiContext: UIContext): FrameNode | null {
500      if (this.isDestroy_) { // rootNode is null.
501        return null;
502      }
503      if (!this.rootNode) { // When rootNode is set to undefined
504        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
505        if(this.rootNode) {
506          this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
507          return this.rootNode.getFrameNode();
508        }else{
509          return null;
510        }
511      }
512      // Return the FrameNode object.
513      return this.rootNode.getFrameNode();
514    }
515
516    setBuilderNode(rootNode: BuilderNode<Params[]> | null): void {
517      this.rootNode = rootNode;
518    }
519
520    getBuilderNode(): BuilderNode<[Params]> | undefined | null {
521      return this.rootNode;
522    }
523
524    updateNode(arg: Object): void {
525      this.rootNode?.update(arg);
526    }
527
528    getEmbedId(): string {
529      return this.embedId_;
530    }
531
532    setDestroy(isDestroy: boolean): void {
533      this.isDestroy_ = isDestroy;
534      if (this.isDestroy_) {
535        this.rootNode = null;
536      }
537    }
538
539    postEvent(event: TouchEvent | undefined): boolean {
540      return this.rootNode?.postTouchEvent(event) as boolean
541    }
542  }
543
544  @Component
545  struct TextInputComponent {
546    @Prop params: Params
547    @State bkColor: Color = Color.White
548
549    build() {
550      Column() {
551        TextInput({text: '', placeholder: 'please input your word...'})
552          .placeholderColor(Color.Gray)
553          .id(this.params?.elementId)
554          .placeholderFont({size: 13, weight: 400})
555          .caretColor(Color.Gray)
556          .fontSize(14)
557          .fontColor(Color.Black)
558      }
559      // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
560      .width(this.params.width)
561      .height(this.params.height)
562    }
563  }
564
565  // In @Builder, add the specific dynamic component content.
566  @Builder
567  function TextInputBuilder(params:Params) {
568    TextInputComponent({params: params})
569      .width(params.width)
570      .height(params.height)
571      .backgroundColor(Color.White)
572  }
573
574  @Entry
575  @Component
576  struct Page{
577    browserTabController: WebviewController = new webview.WebviewController()
578    private nodeControllerMap: Map<string, MyNodeController> = new Map();
579    @State componentIdArr: Array<string> = [];
580    @State posMap: Map<string, Position | undefined> = new Map();
581    @State widthMap: Map<string, number> = new Map();
582    @State heightMap: Map<string, number> = new Map();
583    @State positionMap: Map<string, Edges> = new Map();
584    @State edges: Edges = {};
585
586    build() {
587      Row() {
588        Column() {
589          Stack() {
590            ForEach(this.componentIdArr, (componentId: string) => {
591              NodeContainer(this.nodeControllerMap.get(componentId))
592                .position(this.positionMap.get(componentId))
593                .width(this.widthMap.get(componentId))
594                .height(this.heightMap.get(componentId))
595            }, (embedId: string) => embedId)
596            // Load the local text.html page.
597            Web({src: $rawfile("text.html"), controller: this.browserTabController})
598              // Enable same-layer rendering.
599              .enableNativeEmbedMode(true)
600              // Obtain the lifecycle change data of the embed tag.
601              .onNativeEmbedLifecycleChange((embed) => {
602                 console.log("NativeEmbed surfaceId" + embed.surfaceId);
603                 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
604                 const componentId = embed.info?.id?.toString() as string
605                 if (embed.status == NativeEmbedStatus.CREATE) {
606                   console.log("NativeEmbed create" + JSON.stringify(embed.info));
607                   // Create a NodeController instance, set parameters, and rebuild.
608                   let nodeController = new MyNodeController()
609                   // The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
610                   nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
611                     type : embed.info?.type as string,
612                     renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
613                     embedId : embed.embedId as string,
614                     width : px2vp(embed.info?.width),
615                     height : px2vp(embed.info?.height)})
616                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
617                   nodeController.setDestroy(false);
618                   // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
619                   this.nodeControllerMap.set(componentId, nodeController);
620                   this.widthMap.set(componentId, px2vp(embed.info?.width));
621                   this.heightMap.set(componentId, px2vp(embed.info?.height));
622                   this.positionMap.set(componentId, this.edges);
623                   // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
624                   this.componentIdArr.push(componentId)
625                 } else if (embed.status == NativeEmbedStatus.UPDATE) {
626                   let nodeController = this.nodeControllerMap.get(componentId);
627                   console.log("NativeEmbed update" + JSON.stringify(embed));
628                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
629                   this.positionMap.set(componentId, this.edges);
630                   this.widthMap.set(componentId, px2vp(embed.info?.width));
631                   this.heightMap.set(componentId, px2vp(embed.info?.height));
632                   nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
633                 } else if (embed.status == NativeEmbedStatus.DESTROY) {
634                   console.log("NativeEmbed destroy" + JSON.stringify(embed));
635                   let nodeController = this.nodeControllerMap.get(componentId);
636                   nodeController?.setDestroy(true)
637                   this.nodeControllerMap.clear();
638                   this.positionMap.delete(componentId);
639                   this.widthMap.delete(componentId);
640                   this.heightMap.delete(componentId);
641                   this.componentIdArr.filter((value: string) => value != componentId)
642                 } else {
643                   console.log("NativeEmbed status" + embed.status);
644                 }
645               })// Obtain the touch event information of components for same-layer rendering.
646              .onNativeEmbedGestureEvent((touch) => {
647                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
648                this.componentIdArr.forEach((componentId: string) => {
649                  let nodeController = this.nodeControllerMap.get(componentId);
650                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
651                  if(nodeController?.getEmbedId() == touch.embedId) {
652                    let ret = nodeController?.postEvent(touch.touchEvent)
653                    if(ret) {
654                      console.log("onNativeEmbedGestureEvent success " + componentId);
655                    } else {
656                      console.log("onNativeEmbedGestureEvent fail " + componentId);
657                    }
658                    if(touch.result) {
659                      // Notify the Web component of the gesture event consumption result.
660                      touch.result.setGestureEventResult(ret);
661                    }
662                  }
663                })
664              })
665          }
666        }
667      }
668    }
669  }
670  ```
671
672## Drawing the XComponent+AVPlayer and Button Components
673
674You can enable or disable same-layer rendering through [enableNativeEmbedMode()](../reference/apis-arkweb/ts-basic-components-web.md#enablenativeembedmode11). To use same-layer rendering, the **\<embed>** element must be explicitly used in the HTML file, and the **type** attribute of the element must start with **native/**. The background of the elements corresponding to the tags at the same layer is transparent.
675
676- Example of using same-layer rendering on the application side:
677
678  ```ts
679  // HAP's src/main/ets/pages/Index.ets
680  // Create a NodeController instance.
681  import { webview } from '@kit.ArkWeb';
682  import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI";
683  import { AVPlayerDemo } from './PlayerDemo';
684
685  @Observed
686  declare class Params {
687    textOne : string
688    textTwo : string
689    width : number
690    height : number
691  }
692
693  declare class NodeControllerParams {
694    surfaceId : string
695    type : string
696    renderType : NodeRenderType
697    embedId : string
698    width : number
699    height : number
700  }
701
702  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
703  class MyNodeController extends NodeController {
704    private rootNode: BuilderNode<[Params]> | undefined | null;
705    private embedId_ : string = "";
706    private surfaceId_ : string = "";
707    private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
708    private width_ : number = 0;
709    private height_ : number = 0;
710    private type_ : string = "";
711    private isDestroy_ : boolean = false;
712
713    setRenderOption(params : NodeControllerParams) {
714      this.surfaceId_ = params.surfaceId;
715      this.renderType_ = params.renderType;
716      this.embedId_ = params.embedId;
717      this.width_ = params.width;
718      this.height_ = params.height;
719      this.type_ = params.type;
720    }
721    // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
722    // Called when the corresponding NodeContainer is created or called by the rebuild method.
723    makeNode(uiContext: UIContext): FrameNode | null{
724      if (this.isDestroy_) { // rootNode is null.
725        return null;
726      }
727      if (!this.rootNode) { // When rootNode is set to undefined
728        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_});
729        if (this.type_ === 'native/video') {
730          this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_});
731        } else {
732          // other
733        }
734      }
735      // Return the FrameNode object.
736      return this.rootNode.getFrameNode();
737    }
738
739    setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
740      this.rootNode = rootNode;
741    }
742
743    getBuilderNode(): BuilderNode<[Params]> | undefined | null{
744      return this.rootNode;
745    }
746
747    updateNode(arg: Object): void {
748      this.rootNode?.update(arg);
749    }
750    getEmbedId() : string {
751      return this.embedId_;
752    }
753
754    setDestroy(isDestroy : boolean) : void {
755      this.isDestroy_ = isDestroy;
756      if (this.isDestroy_) {
757        this.rootNode = null;
758      }
759    }
760
761    postEvent(event: TouchEvent | undefined) : boolean {
762      return this.rootNode?.postTouchEvent(event) as boolean
763    }
764  }
765
766  @Component
767  struct VideoComponent {
768    @ObjectLink params: Params
769    @State bkColor: Color = Color.Red
770    mXComponentController: XComponentController = new XComponentController();
771    @State player_changed: boolean = false;
772    player?: AVPlayerDemo;
773
774    build() {
775      Column() {
776        Button(this.params.textOne)
777
778        XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController})
779          .border({width: 1, color: Color.Red})
780          .onLoad(() => {
781            this.player = new AVPlayerDemo();
782            this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
783            this.player_changed = !this.player_changed;
784            this.player.avPlayerLiveDemo()
785          })
786          .width(300)
787          .height(200)
788      }
789      // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
790      .width(this.params.width)
791      .height(this.params.height)
792    }
793  }
794  // In @Builder, add the specific dynamic component content.
795  @Builder
796  function VideoBuilder(params: Params) {
797    VideoComponent({ params: params })
798      .backgroundColor(Color.Gray)
799  }
800
801  @Entry
802  @Component
803  struct WebIndex {
804    browserTabController: WebviewController = new webview.WebviewController()
805    private nodeControllerMap: Map<string, MyNodeController> = new Map();
806    @State componentIdArr: Array<string> = [];
807
808    aboutToAppear() {
809      // Enable web frontend page debugging.
810      webview.WebviewController.setWebDebuggingAccess(true);
811    }
812
813    build(){
814      Row() {
815        Column() {
816          Stack() {
817            ForEach(this.componentIdArr, (componentId: string) => {
818              NodeContainer(this.nodeControllerMap.get(componentId))
819            }, (embedId: string) => embedId)
820            // Load the local test.html page.
821            Web({ src: $rawfile("test.html"), controller: this.browserTabController })
822              // Enable same-layer rendering.
823              .enableNativeEmbedMode(true)
824                // Obtain the lifecycle change data of the embed tag.
825              .onNativeEmbedLifecycleChange((embed) => {
826                console.log("NativeEmbed surfaceId" + embed.surfaceId);
827                // 1. If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
828                const componentId = embed.info?.id?.toString() as string
829                if (embed.status == NativeEmbedStatus.CREATE) {
830                  console.log("NativeEmbed create" + JSON.stringify(embed.info))
831                  // Create a NodeController instance, set parameters, and rebuild.
832                  let nodeController = new MyNodeController()
833                  // 1. The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
834                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string,
835                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string,
836                    width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)})
837                  nodeController.setDestroy(false);
838                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
839                  this.nodeControllerMap.set(componentId, nodeController)
840                  // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
841                  this.componentIdArr.push(componentId)
842                } else if (embed.status == NativeEmbedStatus.UPDATE) {
843                  let nodeController = this.nodeControllerMap.get(componentId)
844                  nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
845                } else {
846                  let nodeController = this.nodeControllerMap.get(componentId);
847                  nodeController?.setDestroy(true)
848                  this.nodeControllerMap.clear();
849                  this.componentIdArr.length = 0;
850                }
851              })// Obtain the touch event information of components for same-layer rendering.
852              .onNativeEmbedGestureEvent((touch) => {
853                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
854                this.componentIdArr.forEach((componentId: string) => {
855                  let nodeController = this.nodeControllerMap.get(componentId)
856                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
857                  if (nodeController?.getEmbedId() === touch.embedId) {
858                    let ret = nodeController?.postEvent(touch.touchEvent)
859                    if (ret) {
860                      console.log("onNativeEmbedGestureEvent success " + componentId)
861                    } else {
862                      console.log("onNativeEmbedGestureEvent fail " + componentId)
863                    }
864                    if (touch.result) {
865                      // Notify the Web component of the gesture event consumption result.
866                      touch.result.setGestureEventResult(ret);
867                    }
868                  }
869                })
870              })
871          }
872        }
873      }
874    }
875  }
876  ```
877
878- Example of video playback code on the application side. Replace the video URL with the correct one in practice.
879
880  ```ts
881  // HAP's src/main/ets/pages/PlayerDemo.ets
882  import { media } from '@kit.MediaKit';
883  import { BusinessError } from '@ohos.base';
884
885  export class AVPlayerDemo {
886    private count: number = 0;
887    private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through XComponent.
888    private isSeek: boolean = true; // Specify whether the seek operation is supported.
889
890    setSurfaceID(surface_id: string){
891      console.log('setSurfaceID : ' + surface_id);
892      this.surfaceID = surface_id;
893    }
894    // Set AVPlayer callback functions.
895    setAVPlayerCallback(avPlayer: media.AVPlayer) {
896      // Callback function for the seek operation.
897      avPlayer.on('seekDone', (seekDoneTime: number) => {
898        console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
899      })
900      // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer.
901      avPlayer.on('error', (err: BusinessError) => {
902        console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
903        avPlayer.reset();
904      })
905      // Callback for state changes.
906      avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
907        switch (state) {
908          case 'idle': // This state is reported upon a successful callback of reset().
909            console.info('AVPlayer state idle called.');
910            avPlayer.release(); // Call release() to release the instance.
911            break;
912          case 'initialized': // This state is reported when the AVPlayer sets the playback source.
913            console.info('AVPlayer state initialized called.');
914            avPlayer.surfaceId = this.surfaceID; // Set the window to display the video. This setting is not required when a pure audio asset is to be played.
915            avPlayer.prepare();
916            break;
917          case 'prepared': // This state is reported upon a successful callback of prepare().
918            console.info('AVPlayer state prepared called.');
919            avPlayer.play(); // Call play() to start playback.
920            break;
921          case 'playing': // This state is reported upon a successful callback of play().
922            console.info('AVPlayer state prepared called.');
923            if(this.count !== 0) {
924              if (this.isSeek) {
925                console.info('AVPlayer start to seek.');
926                avPlayer.seek(avPlayer.duration); // Call seek() to seek to the end of the video clip.
927              } else {
928                // When the seek operation is not supported, the playback continues until it reaches the end.
929                console.info('AVPlayer wait to play end.');
930              }
931            } else {
932              avPlayer.pause(); // Call pause() to pause the playback.
933            }
934            this.count++;
935            break;
936          case 'paused': // This state is reported upon a successful callback of pause().
937            console.info('AVPlayer state paused called.');
938            avPlayer.play(); // Call play() again to start playback.
939            break;
940          case 'completed': // This state is reported upon the completion of the playback.
941            console.info('AVPlayer state paused called.');
942            avPlayer.stop(); // Call stop() to stop the playback.
943            break;
944          case 'stopped': // This state is reported upon a successful callback of stop().
945            console.info('AVPlayer state stopped called.');
946            avPlayer.reset(); // Call reset() to reset the AVPlayer.
947            break;
948          case 'released': // This state is reported upon the release of the AVPlayer.
949            console.info('AVPlayer state released called.');
950            break;
951          default:
952            break;
953        }
954      })
955    }
956
957    // Set the live stream source through the URL.
958    async avPlayerLiveDemo(){
959      // Create an AVPlayer instance.
960      let avPlayer: media.AVPlayer = await media.createAVPlayer();
961      // Create a callback for state changes.
962      this.setAVPlayerCallback(avPlayer);
963      this.isSeek = false; // The seek operation is not supported.
964      // Replace the URL with the actual URL of the video source.
965      avPlayer.url = 'https://xxx.xxx/demo.mp4';
966    }
967  }
968  ```
969
970- Example of the frontend page:
971
972  ```html
973  <!--HAP's src/main/resources/rawfile/test.html-->
974  <!DOCTYPE html>
975  <html>
976  <head>
977      <title>Same-Layer Rendering Test HTML</title>
978      <meta name="viewport">
979  </head>
980  <body>
981  <div>
982      <div id="bodyId">
983          <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/>
984      </div>
985  </div>
986  </body>
987  </html>
988  ```
989
990  ![web-same-layer](figures/web-same-layer.png)
991