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 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  108 109 **Figure 3** Proper **TextInput** with **Stack** 110 111  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  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  991