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-system UI components is inferior to that of system components, you can use the ArkUI component to render these components (same-layer 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 rendered components. 11 12- In addition, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendered 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/arkts-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 system components, which complete the functionalities of the Flutter components. In the Weex 2.0 framework, the **Camera**, **Video**, and **Canvas** components can be rendered using system components to enhance functionality and performance. 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 rendered 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/arkts-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/arkts-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 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 rendered components based on the following specifications. 55 56**Supported HTML5 Tags**: 57- **\<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. 58 59- **\<object>**: After 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. 60 61- W3C standard tags (such as **\<input>** and **\<video>**) cannot be defined as same-layer tags. 62 63- The **\<object>** and **\<embed>** tags cannot be configured as the same-layer tags at the same time. 64 65- The tag types contain only English characters and are case insensitive. 66 67**Supported CSS Attributes**: 68 69**display**, **position**, **z-index**, **visibility**, **opacity**, **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.) 70 71 Other CSS attributes, such as **rotate** and **skew** in the **transform** attribute, may not meet the expectation. 72 73**Lifecycle Management**: 74When the lifecycle of a tag at the same layer changes, the [onNativeEmbedLifecycleChange()](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) callback is triggered. 75 76- Creation, destruction, and position width and height change are supported. 77 78- Web pages containing same-layer components support back-forward cache. 79 80**Distribution and Processing of the Input Events**: 81- The **DOWN**, **UP**, **MOVE**, and **CANCEL** touch events are supported. The [onnativeembedgestureevent11](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) can be configured. By default, the touch event is consumed on the application side. 82 83- Application pages with same-layer tags cannot be scaled, and scaling APIs such as [initialScale](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#initialscale9), [zoom](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoom), [zoomIn](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoomin) and [zoomOut](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoomout) are not supported. 84 85- Mouse, keyboard, and touchpad events are not supported. 86 87- By default, the mouse and touchpad left button events (**MousePress**/**MouseRelease**/**MouseMOVE**) can be converted into touch events (**TouchDOWN**/**TouchUP**/**TouchMOVE**) for reporting. 88 89**Visibility Changes**: 90The [onNativeEmbedVisibilityChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedvisibilitychange12) callback is triggered when the visibility of the same-layer tag changes. 91 92- The visibility of the same-layer tag relative to the viewport can be reported. 93 94- By default, visibility changes caused by the CSS style or size change of the same-layer tag are not reported. For details, see [onNativeEmbedVisibilityChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedvisibilitychange12). 95 96**Constraints**: 97 98- A maximum of five same-layer tag can be displayed on a web page. Otherwise, the rendering performance deteriorates. 99 100- Due to GPU restrictions, the maximum height and texture size of a same-layer tag are 8000 px. 101 102- After same-layer rendering is enabled, all web pages opened by the **Web** component do not support the synchronous [rendering mode](../reference/apis-arkweb/arkts-basic-components-web-e.md#rendermode12). 103 104- 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. 105 106- The **Web** component supports only same-layer rendering nesting at one layer. Input events such as swiping, tapping, and touching and holding are supported. Dragging, rotating, and zooming are not supported. 107 108- 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. 109 110 **Figure 2** Misplaced **TextInput** without **Stack** 111 112  113 114 **Figure 3** Proper **TextInput** with **Stack** 115 116  117 118## Rendering Text Boxes at the Same Layer on Web Pages 119On web pages, you can render the system ArkUI **TextInput** components at the same layer. The following figure shows the effect of three text boxes that are rendered at the same layer. 120 121**Figure 4** Same-layer rendering text boxes 122 123  124 1251. Mark the HTML tags that need to be rendered at the same layer on the web page. 126 127 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. 128 129 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. 130 131 - Use the \<embed> tags. 132 133 ```html 134 <!--HAP's src/main/resources/rawfile/text.html--> 135 <!DOCTYPE html> 136 <html> 137 <head> 138 <title>Same-Layer Rendering HTML</title> 139 <meta name="viewport"> 140 </head> 141 142 <body style="background:white"> 143 144 <embed id = "input1" type="native/view" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"/> 145 146 <embed id = "input2" type="native/view2" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/> 147 148 <embed id = "input3" type="native/view3" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/> 149 150 </body> 151 </html> 152 ``` 153 154 - Use the \<object> tags. 155 156 Call **registerNativeEmbedRule** to register a **\<object>** tag. 157 ```ts 158 // ... 159 Web({src: $rawfile("text.html"), controller: this.browserTabController}) 160 // Register the same-layer tag of "object" and type of "test." 161 .registerNativeEmbedRule("object", "test") 162 // ... 163 ``` 164 165 Example of using **registerNativeEmbedRule** on the frontend page, with the tag of "object" and type of "test": 166 167 ```html 168 <!--HAP's src/main/resources/rawfile/text.html--> 169 <!DOCTYPE html> 170 <html> 171 <head> 172 <title>Same-Layer Rendering HTML</title> 173 <meta name="viewport"> 174 </head> 175 176 <body style="background:white"> 177 178 <object id = "input1" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"></object> 179 180 <object id = "input2" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object> 181 182 <object id = "input3" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object> 183 184 </body> 185 </html> 186 ``` 187 1882. Use **enableNativeEmbedMode** to enable same-layer rendering on the application. 189 190 The same-layer rendering feature is disabled by default. To enable this feature, set [enableNativeEmbedMode](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#enablenativeembedmode11). 191 192 ```ts 193 // xxx.ets 194 import { webview } from '@kit.ArkWeb'; 195 @Entry 196 @Component 197 struct WebComponent { 198 controller: webview.WebviewController = new webview.WebviewController(); 199 200 build() { 201 Column() { 202 Web({ src: 'www.example.com', controller: this.controller }) 203 // Enable same-layer rendering. 204 .enableNativeEmbedMode(true) 205 } 206 } 207 } 208 ``` 209 2103. Create a custom component. 211 212 The custom component is displayed as a system component in the corresponding area after same-layer rendering is enabled. 213 214 ```ts 215 @Component 216 struct TextInputComponent { 217 @Prop params: Params 218 @State bkColor: Color = Color.White 219 220 build() { 221 Column() { 222 TextInput({text: '', placeholder: 'please input your word...'}) 223 .placeholderColor(Color.Gray) 224 .id(this.params?.elementId) 225 .placeholderFont({size: 13, weight: 400}) 226 .caretColor(Color.Gray) 227 .width(this.params?.width) 228 .height(this.params?.height) 229 .fontSize(14) 230 .fontColor(Color.Black) 231 } 232 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 233 .width(this.params.width) 234 .height(this.params.height) 235 } 236 } 237 238 @Builder 239 function TextInputBuilder(params:Params) { 240 TextInputComponent({params: params}) 241 .width(params.width) 242 .height(params.height) 243 .backgroundColor(Color.White) 244 } 245 ``` 246 2474. Create a node controller. 248 249 The node controller is used to control and report node behaviors of the corresponding NodeContainer. 250 251 ```ts 252 class MyNodeController extends NodeController { 253 private rootNode: BuilderNode<[Params]> | undefined | null; 254 private embedId_: string = ""; 255 private surfaceId_: string = ""; 256 private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; 257 private width_: number = 0; 258 private height_: number = 0; 259 private type_: string = ""; 260 private isDestroy_: boolean = false; 261 262 setRenderOption(params: NodeControllerParams) { 263 this.surfaceId_ = params.surfaceId; 264 this.renderType_ = params.renderType; 265 this.embedId_ = params.embedId; 266 this.width_ = params.width; 267 this.height_ = params.height; 268 this.type_ = params.type; 269 } 270 271 // 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. 272 // Called when the corresponding NodeContainer is created or called by the rebuild method. 273 makeNode(uiContext: UIContext): FrameNode | null { 274 if (this.isDestroy_) { // rootNode is null. 275 return null; 276 } 277 if (!this.rootNode) { // When rootNode is set to undefined 278 this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ }); 279 if(this.rootNode) { 280 this.rootNode.build(wrapBuilder(TextInputBuilder), { textOne: "myTextInput", width: this.width_, height: this.height_ }) 281 return this.rootNode.getFrameNode(); 282 }else{ 283 return null; 284 } 285 } 286 // Return the FrameNode object. 287 return this.rootNode.getFrameNode(); 288 } 289 290 updateNode(arg: Object): void { 291 this.rootNode?.update(arg); 292 } 293 294 getEmbedId(): string { 295 return this.embedId_; 296 } 297 298 setDestroy(isDestroy: boolean): void { 299 this.isDestroy_ = isDestroy; 300 if (this.isDestroy_) { 301 this.rootNode = null; 302 } 303 } 304 305 postEvent(event: TouchEvent | undefined): boolean { 306 return this.rootNode?.postTouchEvent(event) as boolean 307 } 308 } 309 ``` 310 3115. Listen for the lifecycle changes of the same-layer rendered tags. 312 313 After this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedLifecycleChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) each time a same-layer rendered tag is used on the web page. 314 315 Call [onNativeEmbedLifecycleChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) to listen for the lifecycle changes of the same-layer rendered tags. 316 317 ```ts 318 build() { 319 Row() { 320 Column() { 321 Stack() { 322 ForEach(this.componentIdArr, (componentId: string) => { 323 NodeContainer(this.nodeControllerMap.get(componentId)) 324 .position(this.positionMap.get(componentId)) 325 .width(this.widthMap.get(componentId)) 326 .height(this.heightMap.get(componentId)) 327 }, (embedId: string) => embedId) 328 // Load the local text.html page. 329 Web({src: $rawfile("text.html"), controller: this.browserTabController}) 330 // Enable same-layer rendering. 331 .enableNativeEmbedMode(true) 332 // Register the same-layer tag of "object" and type of "test." 333 .registerNativeEmbedRule("object", "test") 334 // Obtain the lifecycle change data of the embed tag. 335 .onNativeEmbedLifecycleChange((embed) => { 336 console.log("NativeEmbed surfaceId" + embed.surfaceId); 337 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page. 338 const componentId = embed.info?.id?.toString() as string 339 if (embed.status == NativeEmbedStatus.CREATE) { 340 console.log("NativeEmbed create" + JSON.stringify(embed.info)); 341 // Create a NodeController instance, set parameters, and rebuild. 342 let nodeController = new MyNodeController() 343 // 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. 344 nodeController.setRenderOption({surfaceId : embed.surfaceId as string, 345 type : embed.info?.type as string, 346 renderType : NodeRenderType.RENDER_TYPE_TEXTURE, 347 embedId : embed.embedId as string, 348 width : this.uiContext.px2vp(embed.info?.width), 349 height : this.uiContext.px2vp(embed.info?.height)}) 350 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 351 nodeController.setDestroy(false); 352 // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key. 353 this.nodeControllerMap.set(componentId, nodeController); 354 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 355 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 356 this.positionMap.set(componentId, this.edges); 357 // 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. 358 this.componentIdArr.push(componentId) 359 } else if (embed.status == NativeEmbedStatus.UPDATE) { 360 let nodeController = this.nodeControllerMap.get(componentId); 361 console.log("NativeEmbed update" + JSON.stringify(embed)); 362 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 363 this.positionMap.set(componentId, this.edges); 364 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 365 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 366 nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject) 367 } else if (embed.status == NativeEmbedStatus.DESTROY) { 368 console.log("NativeEmbed destroy" + JSON.stringify(embed)); 369 let nodeController = this.nodeControllerMap.get(componentId); 370 nodeController?.setDestroy(true) 371 this.nodeControllerMap.clear(); 372 this.positionMap.delete(componentId); 373 this.widthMap.delete(componentId); 374 this.heightMap.delete(componentId); 375 this.componentIdArr.filter((value: string) => value != componentId) 376 } else { 377 console.log("NativeEmbed status" + embed.status); 378 } 379 }) 380 }.height("80%") 381 } 382 } 383 } 384 ``` 385 3866. Listen for gesture events of same-layer rendered region. 387 388 When this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedGestureEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) each time a touch operation is performed in the same-layer rendered region. 389 390 Call [onNativeEmbedGestureEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) to listen for gesture events of the same-layer rendered region. 391 392 ```ts 393 build() { 394 Row() { 395 Column() { 396 Stack() { 397 ForEach(this.componentIdArr, (componentId: string) => { 398 NodeContainer(this.nodeControllerMap.get(componentId)) 399 .position(this.positionMap.get(componentId)) 400 .width(this.widthMap.get(componentId)) 401 .height(this.heightMap.get(componentId)) 402 }, (embedId: string) => embedId) 403 // Load the local text.html page. 404 Web({src: $rawfile("text.html"), controller: this.browserTabController}) 405 // Enable same-layer rendering. 406 .enableNativeEmbedMode(true) 407 // Obtain the lifecycle change data of the embed tag. 408 .onNativeEmbedLifecycleChange((embed) => { 409 // Implement lifecycle changes. 410 }) 411 .onNativeEmbedGestureEvent((touch) => { 412 console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent)); 413 this.componentIdArr.forEach((componentId: string) => { 414 let nodeController = this.nodeControllerMap.get(componentId); 415 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 416 if(nodeController?.getEmbedId() == touch.embedId) { 417 let ret = nodeController?.postEvent(touch.touchEvent) 418 if(ret) { 419 console.log("onNativeEmbedGestureEvent success " + componentId); 420 } else { 421 console.log("onNativeEmbedGestureEvent fail " + componentId); 422 } 423 if(touch.result) { 424 // Notify the Web component of the consumption result of the gesture event. 425 touch.result.setGestureEventResult(ret); 426 } 427 } 428 }) 429 }) 430 } 431 } 432 } 433 } 434 ``` 435 4367. Listen for mouse events of the same-layer rendered region. 437 438 After this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedMouseEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedmouseevent20) when the following operations are performed in the same-layer rendered area: 439 440 - Tapping or holding with the left, middle, or right mouse button. 441 - Tapping or holding the left, middle, or right mouse button using the touchpad. 442 443 Call [onNativeEmbedMouseEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedmouseevent20) to listen for the mouse events of the same-layer rendered region. 444 445 ```ts 446 build() { 447 Row() { 448 Column() { 449 Stack() { 450 ForEach(this.componentIdArr, (componentId: string) => { 451 NodeContainer(this.nodeControllerMap.get(componentId)) 452 .position(this.positionMap.get(componentId)) 453 .width(this.widthMap.get(componentId)) 454 .height(this.heightMap.get(componentId)) 455 }, (embedId: string) => embedId) 456 // Load the local text.html page. 457 Web({src: $rawfile("text.html"), controller: this.browserTabController}) 458 // Enable same-layer rendering. 459 .enableNativeEmbedMode(true) 460 // Obtain the lifecycle change data of the embed tag. 461 .onNativeEmbedLifecycleChange((embed) => { 462 // Implement lifecycle changes. 463 }) 464 .onNativeEmbedGestureEvent((touch) => { 465 // Process the gesture event at the same layer. 466 }) 467 .onNativeEmbedMouseEvent((mouse) => { 468 console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent)); 469 this.componentIdArr.forEach((componentId: string) => { 470 let nodeController = this.nodeControllerMap.get(componentId); 471 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 472 if(nodeController?.getEmbedId() == mouse.embedId) { 473 let ret = nodeController?.postInputEvent(mouse.touchEvent) 474 if(ret) { 475 console.log("onNativeEmbedMouseEvent success " + componentId); 476 } else { 477 console.log("onNativeEmbedMouseEvent fail " + componentId); 478 } 479 if(mouse.result) { 480 // Notify the Web component of the consumption result of the mouse event. 481 mouse.result.setMouseEventResult(ret); 482 } 483 } 484 }) 485 }) 486 } 487 } 488 } 489 } 490 ``` 491**Sample Code** 492 493To start with, add the Internet permission to the **module.json5** file. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 494 495 ``` 496 "requestPermissions":[ 497 { 498 "name" : "ohos.permission.INTERNET" 499 } 500 ] 501 ``` 502 503Code on the application side: 504 505 ```ts 506 // Create a NodeController instance. 507 import { webview } from '@kit.ArkWeb'; 508 import { UIContext } from '@kit.ArkUI'; 509 import { NodeController, BuilderNode, NodeRenderType, FrameNode } from '@kit.ArkUI'; 510 511 @Observed 512 declare class Params{ 513 elementId: string 514 textOne: string 515 textTwo: string 516 width: number 517 height: number 518 } 519 520 declare class NodeControllerParams { 521 surfaceId: string 522 type: string 523 renderType: NodeRenderType 524 embedId: string 525 width: number 526 height: number 527 } 528 529 // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container. 530 class MyNodeController extends NodeController { 531 private rootNode: BuilderNode<[Params]> | undefined | null; 532 private embedId_: string = ""; 533 private surfaceId_: string = ""; 534 private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; 535 private width_: number = 0; 536 private height_: number = 0; 537 private type_: string = ""; 538 private isDestroy_: boolean = false; 539 540 setRenderOption(params: NodeControllerParams) { 541 this.surfaceId_ = params.surfaceId; 542 this.renderType_ = params.renderType; 543 this.embedId_ = params.embedId; 544 this.width_ = params.width; 545 this.height_ = params.height; 546 this.type_ = params.type; 547 } 548 549 // 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. 550 // Called when the corresponding NodeContainer is created or called by the rebuild method. 551 makeNode(uiContext: UIContext): FrameNode | null { 552 if (this.isDestroy_) { // rootNode is null. 553 return null; 554 } 555 if (!this.rootNode) { // When rootNode is set to undefined 556 this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ }); 557 if(this.rootNode) { 558 this.rootNode.build(wrapBuilder(TextInputBuilder), { textOne: "myTextInput", width: this.width_, height: this.height_ }) 559 return this.rootNode.getFrameNode(); 560 }else{ 561 return null; 562 } 563 } 564 // Return the FrameNode object. 565 return this.rootNode.getFrameNode(); 566 } 567 568 updateNode(arg: Object): void { 569 this.rootNode?.update(arg); 570 } 571 572 getEmbedId(): string { 573 return this.embedId_; 574 } 575 576 setDestroy(isDestroy: boolean): void { 577 this.isDestroy_ = isDestroy; 578 if (this.isDestroy_) { 579 this.rootNode = null; 580 } 581 } 582 583 postEvent(event: TouchEvent | undefined): boolean { 584 return this.rootNode?.postTouchEvent(event) as boolean 585 } 586 587 postInputEvent(event: MouseEvent | undefined): boolean { 588 return this.rootNode?.postInputEvent(event) as boolean 589 } 590 } 591 592 @Component 593 struct TextInputComponent { 594 @Prop params: Params 595 @State bkColor: Color = Color.White 596 597 build() { 598 Column() { 599 TextInput({text: '', placeholder: 'please input your word...'}) 600 .placeholderColor(Color.Gray) 601 .id(this.params?.elementId) 602 .placeholderFont({size: 13, weight: 400}) 603 .caretColor(Color.Gray) 604 .fontSize(14) 605 .fontColor(Color.Black) 606 } 607 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 608 .width(this.params.width) 609 .height(this.params.height) 610 } 611 } 612 613 // In @Builder, add the specific dynamic component content. 614 @Builder 615 function TextInputBuilder(params:Params) { 616 TextInputComponent({params: params}) 617 .width(params.width) 618 .height(params.height) 619 .backgroundColor(Color.White) 620 } 621 622 @Entry 623 @Component 624 struct Page{ 625 browserTabController: WebviewController = new webview.WebviewController() 626 private nodeControllerMap: Map<string, MyNodeController> = new Map(); 627 @State componentIdArr: Array<string> = []; 628 @State widthMap: Map<string, number> = new Map(); 629 @State heightMap: Map<string, number> = new Map(); 630 @State positionMap: Map<string, Edges> = new Map(); 631 @State edges: Edges = {}; 632 uiContext: UIContext = this.getUIContext(); 633 634 build() { 635 Row() { 636 Column() { 637 Stack() { 638 ForEach(this.componentIdArr, (componentId: string) => { 639 NodeContainer(this.nodeControllerMap.get(componentId)) 640 .position(this.positionMap.get(componentId)) 641 .width(this.widthMap.get(componentId)) 642 .height(this.heightMap.get(componentId)) 643 }, (embedId: string) => embedId) 644 // Load the local text.html page. 645 Web({src: $rawfile("text.html"), controller: this.browserTabController}) 646 // Enable same-layer rendering. 647 .enableNativeEmbedMode(true) 648 // Obtain the lifecycle change data of the embed tag. 649 .onNativeEmbedLifecycleChange((embed) => { 650 console.log("NativeEmbed surfaceId" + embed.surfaceId); 651 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page. 652 const componentId = embed.info?.id?.toString() as string 653 if (embed.status == NativeEmbedStatus.CREATE) { 654 console.log("NativeEmbed create" + JSON.stringify(embed.info)); 655 // Create a NodeController instance, set parameters, and rebuild. 656 let nodeController = new MyNodeController() 657 // 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. 658 nodeController.setRenderOption({surfaceId : embed.surfaceId as string, 659 type : embed.info?.type as string, 660 renderType : NodeRenderType.RENDER_TYPE_TEXTURE, 661 embedId : embed.embedId as string, 662 width : this.uiContext.px2vp(embed.info?.width), 663 height : this.uiContext.px2vp(embed.info?.height)}) 664 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 665 nodeController.setDestroy(false); 666 // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key. 667 this.nodeControllerMap.set(componentId, nodeController); 668 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 669 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 670 this.positionMap.set(componentId, this.edges); 671 // 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. 672 this.componentIdArr.push(componentId) 673 } else if (embed.status == NativeEmbedStatus.UPDATE) { 674 let nodeController = this.nodeControllerMap.get(componentId); 675 console.log("NativeEmbed update" + JSON.stringify(embed)); 676 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 677 this.positionMap.set(componentId, this.edges); 678 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 679 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 680 nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject) 681 } else if (embed.status == NativeEmbedStatus.DESTROY) { 682 console.log("NativeEmbed destroy" + JSON.stringify(embed)); 683 let nodeController = this.nodeControllerMap.get(componentId); 684 nodeController?.setDestroy(true) 685 this.nodeControllerMap.clear(); 686 this.positionMap.delete(componentId); 687 this.widthMap.delete(componentId); 688 this.heightMap.delete(componentId); 689 this.componentIdArr.filter((value: string) => value != componentId) 690 } else { 691 console.log("NativeEmbed status" + embed.status); 692 } 693 })// Obtain the touch event information of components for same-layer rendering. 694 .onNativeEmbedGestureEvent((touch) => { 695 console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent)); 696 this.componentIdArr.forEach((componentId: string) => { 697 let nodeController = this.nodeControllerMap.get(componentId); 698 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 699 if(nodeController?.getEmbedId() == touch.embedId) { 700 let ret = nodeController?.postEvent(touch.touchEvent) 701 if(ret) { 702 console.log("onNativeEmbedGestureEvent success " + componentId); 703 } else { 704 console.log("onNativeEmbedGestureEvent fail " + componentId); 705 } 706 if(touch.result) { 707 // Notify the Web component of the consumption result of the gesture event. 708 touch.result.setGestureEventResult(ret); 709 } 710 } 711 }) 712 }) 713 .onNativeEmbedMouseEvent((mouse) => { 714 console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent)); 715 this.componentIdArr.forEach((componentId: string) => { 716 let nodeController = this.nodeControllerMap.get(componentId); 717 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 718 if(nodeController?.getEmbedId() == mouse.embedId) { 719 let ret = nodeController?.postInputEvent(mouse.mouseEvent) 720 if(ret) { 721 console.log("onNativeEmbedMouseEvent success " + componentId); 722 } else { 723 console.log("onNativeEmbedMouseEvent fail " + componentId); 724 } 725 if(mouse.result) { 726 // Notify the Web component of the consumption result of the mouse event. 727 mouse.result.setMouseEventResult(ret); 728 } 729 } 730 }) 731 }) 732 } 733 } 734 } 735 } 736 } 737 ``` 738 739## Drawing the XComponent+AVPlayer and Button Components 740 741- Example of using same-layer rendering on the application side: 742 743 ```ts 744 // HAP's src/main/ets/pages/Index.ets 745 // Create a NodeController instance. 746 import { webview } from '@kit.ArkWeb'; 747 import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI"; 748 import { AVPlayerDemo } from './PlayerDemo'; 749 750 @Observed 751 declare class Params { 752 textOne : string 753 textTwo : string 754 width : number 755 height : number 756 } 757 758 declare class NodeControllerParams { 759 surfaceId : string 760 type : string 761 renderType : NodeRenderType 762 embedId : string 763 width : number 764 height : number 765 } 766 767 // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container. 768 class MyNodeController extends NodeController { 769 private rootNode: BuilderNode<[Params]> | undefined | null; 770 private embedId_ : string = ""; 771 private surfaceId_ : string = ""; 772 private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; 773 private width_ : number = 0; 774 private height_ : number = 0; 775 private type_ : string = ""; 776 private isDestroy_ : boolean = false; 777 778 setRenderOption(params : NodeControllerParams) { 779 this.surfaceId_ = params.surfaceId; 780 this.renderType_ = params.renderType; 781 this.embedId_ = params.embedId; 782 this.width_ = params.width; 783 this.height_ = params.height; 784 this.type_ = params.type; 785 } 786 // 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. 787 // Called when the corresponding NodeContainer is created or called by the rebuild method. 788 makeNode(uiContext: UIContext): FrameNode | null{ 789 if (this.isDestroy_) { // rootNode is null. 790 return null; 791 } 792 if (!this.rootNode) { // When rootNode is set to undefined 793 this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_}); 794 if (this.type_ === 'native/video') { 795 this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_}); 796 } else { 797 // other 798 } 799 } 800 // Return the FrameNode object. 801 return this.rootNode.getFrameNode(); 802 } 803 804 updateNode(arg: Object): void { 805 this.rootNode?.update(arg); 806 } 807 getEmbedId() : string { 808 return this.embedId_; 809 } 810 811 setDestroy(isDestroy : boolean) : void { 812 this.isDestroy_ = isDestroy; 813 if (this.isDestroy_) { 814 this.rootNode = null; 815 } 816 } 817 818 postEvent(event: TouchEvent | undefined) : boolean { 819 return this.rootNode?.postTouchEvent(event) as boolean 820 } 821 822 postInputEvent(event: MouseEvent | undefined): boolean { 823 return this.rootNode?.postInputEvent(event) as boolean 824 } 825 } 826 827 @Component 828 struct VideoComponent { 829 @ObjectLink params: Params 830 @State bkColor: Color = Color.Red 831 mXComponentController: XComponentController = new XComponentController(); 832 @State player_changed: boolean = false; 833 player?: AVPlayerDemo; 834 835 build() { 836 Column() { 837 Button(this.params.textOne) 838 839 XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController}) 840 .border({width: 1, color: Color.Red}) 841 .onLoad(() => { 842 this.player = new AVPlayerDemo(); 843 this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId()); 844 this.player_changed = !this.player_changed; 845 this.player.avPlayerLiveDemo() 846 }) 847 .width(300) 848 .height(200) 849 } 850 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 851 .width(this.params.width) 852 .height(this.params.height) 853 } 854 } 855 // In @Builder, add the specific dynamic component content. 856 @Builder 857 function VideoBuilder(params: Params) { 858 VideoComponent({ params: params }) 859 .backgroundColor(Color.Gray) 860 } 861 862 @Entry 863 @Component 864 struct WebIndex { 865 browserTabController: WebviewController = new webview.WebviewController() 866 private nodeControllerMap: Map<string, MyNodeController> = new Map(); 867 @State componentIdArr: Array<string> = []; 868 @State widthMap: Map<string, number> = new Map(); 869 @State heightMap: Map<string, number> = new Map(); 870 @State positionMap: Map<string, Edges> = new Map(); 871 @State edges: Edges = {}; 872 uiContext: UIContext = this.getUIContext(); 873 874 aboutToAppear() { 875 // Enable web frontend page debugging. 876 webview.WebviewController.setWebDebuggingAccess(true); 877 } 878 879 build(){ 880 Row() { 881 Column() { 882 Stack() { 883 ForEach(this.componentIdArr, (componentId: string) => { 884 NodeContainer(this.nodeControllerMap.get(componentId)) 885 .position(this.positionMap.get(componentId)) 886 .width(this.widthMap.get(componentId)) 887 .height(this.heightMap.get(componentId)) 888 }, (embedId: string) => embedId) 889 // Load the local test.html page. 890 Web({ src: $rawfile("test.html"), controller: this.browserTabController }) 891 // Enable same-layer rendering. 892 .enableNativeEmbedMode(true) 893 // Obtain the lifecycle change data of the embed tag. 894 .onNativeEmbedLifecycleChange((embed) => { 895 console.log("NativeEmbed surfaceId" + embed.surfaceId); 896 // 1. If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page. 897 const componentId = embed.info?.id?.toString() as string 898 if (embed.status == NativeEmbedStatus.CREATE) { 899 console.log("NativeEmbed create" + JSON.stringify(embed.info)) 900 // Create a NodeController instance, set parameters, and rebuild. 901 let nodeController = new MyNodeController() 902 // 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. 903 nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string, 904 renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string, 905 width : this.uiContext.px2vp(embed.info?.width), height : this.uiContext.px2vp(embed.info?.height)}) 906 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 907 nodeController.setDestroy(false); 908 // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key. 909 this.nodeControllerMap.set(componentId, nodeController) 910 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 911 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 912 this.positionMap.set(componentId, this.edges); 913 // 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. 914 this.componentIdArr.push(componentId) 915 } else if (embed.status == NativeEmbedStatus.UPDATE) { 916 let nodeController = this.nodeControllerMap.get(componentId) 917 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 918 this.positionMap.set(componentId, this.edges); 919 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 920 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 921 nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject) 922 } else if (embed.status == NativeEmbedStatus.DESTROY) { 923 let nodeController = this.nodeControllerMap.get(componentId); 924 nodeController?.setDestroy(true) 925 this.nodeControllerMap.clear(); 926 this.positionMap.delete(componentId); 927 this.widthMap.delete(componentId); 928 this.heightMap.delete(componentId); 929 this.componentIdArr.filter((value: string) => value != componentId) 930 } else { 931 console.log("NativeEmbed status" + embed.status); 932 } 933 })// Obtain the touch event information of components for same-layer rendering. 934 .onNativeEmbedGestureEvent((touch) => { 935 console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent)); 936 this.componentIdArr.forEach((componentId: string) => { 937 let nodeController = this.nodeControllerMap.get(componentId) 938 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 939 if (nodeController?.getEmbedId() === touch.embedId) { 940 let ret = nodeController?.postEvent(touch.touchEvent) 941 if (ret) { 942 console.log("onNativeEmbedGestureEvent success " + componentId) 943 } else { 944 console.log("onNativeEmbedGestureEvent fail " + componentId) 945 } 946 if (touch.result) { 947 // Notify the Web component of the consumption result of the gesture event. 948 touch.result.setGestureEventResult(ret); 949 } 950 } 951 }) 952 }) 953 .onNativeEmbedMouseEvent((mouse) => { 954 console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent)); 955 this.componentIdArr.forEach((componentId: string) => { 956 let nodeController = this.nodeControllerMap.get(componentId); 957 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 958 if(nodeController?.getEmbedId() == mouse.embedId) { 959 let ret = nodeController?.postInputEvent(mouse.mouseEvent) 960 if(ret) { 961 console.log("onNativeEmbedMouseEvent success " + componentId); 962 } else { 963 console.log("onNativeEmbedMouseEvent fail " + componentId); 964 } 965 if(mouse.result) { 966 // Notify the Web component of the consumption result of the mouse event. 967 mouse.result.setMouseEventResult(ret); 968 } 969 } 970 }) 971 }) 972 } 973 } 974 } 975 } 976 } 977 ``` 978 979- Code example of video playback on the application side. Replace the URL with the correct video URL in practice. 980 981 ```ts 982 // HAP's src/main/ets/pages/PlayerDemo.ets 983 import { media } from '@kit.MediaKit'; 984 import { BusinessError } from '@ohos.base'; 985 986 export class AVPlayerDemo { 987 private count: number = 0; 988 private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through XComponent. 989 private isSeek: boolean = true; // Specify whether the seek operation is supported. 990 991 setSurfaceID(surface_id: string){ 992 console.log('setSurfaceID : ' + surface_id); 993 this.surfaceID = surface_id; 994 } 995 // Set AVPlayer callback functions. 996 setAVPlayerCallback(avPlayer: media.AVPlayer) { 997 // Callback function for the seek operation. 998 avPlayer.on('seekDone', (seekDoneTime: number) => { 999 console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`); 1000 }) 1001 // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer. 1002 avPlayer.on('error', (err: BusinessError) => { 1003 console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); 1004 avPlayer.reset(); 1005 }) 1006 // Callback for state changes. 1007 avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => { 1008 switch (state) { 1009 case 'idle': // This state is reported upon a successful callback of reset(). 1010 console.info('AVPlayer state idle called.'); 1011 avPlayer.release(); // Call release() to release the instance. 1012 break; 1013 case 'initialized': // This state is reported when the AVPlayer sets the playback source. 1014 console.info('AVPlayer state initialized called.'); 1015 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. 1016 avPlayer.prepare(); 1017 break; 1018 case 'prepared': // This state is reported upon a successful callback of prepare(). 1019 console.info('AVPlayer state prepared called.'); 1020 avPlayer.play(); // Call play() to start playback. 1021 break; 1022 case 'playing': // This state is reported upon a successful callback of play(). 1023 console.info('AVPlayer state prepared called.'); 1024 if(this.count !== 0) { 1025 if (this.isSeek) { 1026 console.info('AVPlayer start to seek.'); 1027 avPlayer.seek(avPlayer.duration); // Call seek() to seek to the end of the video clip. 1028 } else { 1029 // When the seek operation is not supported, the playback continues until it reaches the end. 1030 console.info('AVPlayer wait to play end.'); 1031 } 1032 } else { 1033 avPlayer.pause(); // Call pause() to pause the playback. 1034 } 1035 this.count++; 1036 break; 1037 case 'paused': // This state is reported upon a successful callback of pause(). 1038 console.info('AVPlayer state paused called.'); 1039 avPlayer.play(); // Call play() again to start playback. 1040 break; 1041 case 'completed': // This state is reported upon the completion of the playback. 1042 console.info('AVPlayer state paused called.'); 1043 avPlayer.stop(); // Call the playback API. 1044 break; 1045 case 'stopped': // This state is reported upon a successful callback of stop(). 1046 console.info('AVPlayer state stopped called.'); 1047 avPlayer.reset(); // Call reset() to reset the AVPlayer. 1048 break; 1049 case 'released': // This state is reported upon the release of the AVPlayer. 1050 console.info('AVPlayer state released called.'); 1051 break; 1052 default: 1053 break; 1054 } 1055 }) 1056 } 1057 1058 // Set the live stream source through the URL. 1059 async avPlayerLiveDemo(){ 1060 // Create an AVPlayer instance. 1061 let avPlayer: media.AVPlayer = await media.createAVPlayer(); 1062 // Create a callback for state changes. 1063 this.setAVPlayerCallback(avPlayer); 1064 this.isSeek = false; // The seek operation is not supported. 1065 // Replace the URL with the actual URL of the video source. 1066 avPlayer.url = 'https://xxx.xxx/demo.mp4'; 1067 } 1068 } 1069 ``` 1070 1071- Example of the frontend page: 1072 1073 ```html 1074 <!--HAP's src/main/resources/rawfile/test.html--> 1075 <!DOCTYPE html> 1076 <html> 1077 <head> 1078 <title>Same-Layer Rendering Test HTML</title> 1079 <meta name="viewport"> 1080 </head> 1081 <body> 1082 <div> 1083 <div id="bodyId"> 1084 <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/> 1085 </div> 1086 </div> 1087 </body> 1088 </html> 1089 ``` 1090 1091- Demo: 1092 1093  1094 1095## Setting the Same-Layer Tag to the Highest Level 1096 1097The same-layer rendering supports the private attribute **arkwebnativestyle**, which takes effect only in **\<embed>** and **\<object>** after same-layer rendering is enabled. The **display** attribute of **arkwebnativestyle** is used to set the level of the same-layer tag to be above other web elements. When multiple same-layer tags have the same **display** value in **arkwebnativestyle**, their stacking order follows the W3C standard: first by **z-index**, then by their order in the DOM if **z-index** values are the same. The values of the **display** attribute are listed in the following table. 1098 1099| Value| Description| 1100| - | - | 1101| overlay | Sets the level of the same-layer tag to be higher than that of other web elements.| 1102| overlay-infinity | Sets the level of the same-layer tag to be higher than that of other web elements and the same-layer tag with the **overlay** value.| 1103 1104- Code on the application side: 1105 ```ts 1106 import { webview } from '@kit.ArkWeb'; 1107 import { UIContext } from '@kit.ArkUI'; 1108 import { NodeController, BuilderNode, NodeRenderType, FrameNode } from '@kit.ArkUI'; 1109 1110 @Observed 1111 declare class Params{ 1112 elementId: string 1113 textOne: string 1114 textTwo: string 1115 width: number 1116 height: number 1117 } 1118 1119 declare class NodeControllerParams { 1120 surfaceId: string 1121 type: string 1122 renderType: NodeRenderType 1123 embedId: string 1124 width: number 1125 height: number 1126 } 1127 1128 // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container. 1129 class MyNodeController extends NodeController { 1130 private rootNode: BuilderNode<[Params]> | undefined | null; 1131 private embedId_: string = ""; 1132 private surfaceId_: string = ""; 1133 private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; 1134 private width_: number = 0; 1135 private height_: number = 0; 1136 private type_: string = ""; 1137 private isDestroy_: boolean = false; 1138 1139 setRenderOption(params: NodeControllerParams) { 1140 this.surfaceId_ = params.surfaceId; 1141 this.renderType_ = params.renderType; 1142 this.embedId_ = params.embedId; 1143 this.width_ = params.width; 1144 this.height_ = params.height; 1145 this.type_ = params.type; 1146 } 1147 1148 // 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. 1149 // Called when the corresponding NodeContainer is created or called by the rebuild method. 1150 makeNode(uiContext: UIContext): FrameNode | null { 1151 if (this.isDestroy_) { // rootNode is null. 1152 return null; 1153 } 1154 if (!this.rootNode) { // When rootNode is set to undefined 1155 this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ }); 1156 if (this.type_ == 'native/view1') { 1157 this.rootNode.build(wrapBuilder(TextInputBuilder1), { textOne: "myTextInput", width: this.width_, height: this.height_ }) 1158 return this.rootNode.getFrameNode(); 1159 } else if (this.type_ == 'native/view2') { 1160 this.rootNode.build(wrapBuilder(TextInputBuilder2), { textOne: "myTextInput", width: this.width_, height: this.height_ }) 1161 return this.rootNode.getFrameNode(); 1162 } else{ 1163 return null; 1164 } 1165 } 1166 // Return the FrameNode object. 1167 return this.rootNode.getFrameNode(); 1168 } 1169 1170 updateNode(arg: Object): void { 1171 this.rootNode?.update(arg); 1172 } 1173 1174 getEmbedId(): string { 1175 return this.embedId_; 1176 } 1177 1178 setDestroy(isDestroy: boolean): void { 1179 this.isDestroy_ = isDestroy; 1180 if (this.isDestroy_) { 1181 this.rootNode = null; 1182 } 1183 } 1184 1185 postEvent(event: TouchEvent | undefined): boolean { 1186 return this.rootNode?.postTouchEvent(event) as boolean 1187 } 1188 } 1189 1190 @Component 1191 struct TextInputComponent1 { 1192 @Prop params: Params; 1193 @State bkColor: Color = Color.White; 1194 1195 build() { 1196 Column() { 1197 Text("display:overlay-infinity") 1198 TextInput({text: '', placeholder: 'please input your word...'}) 1199 .placeholderColor(Color.Gray) 1200 .id(this.params?.elementId) 1201 .placeholderFont({size: 13, weight: 400}) 1202 .caretColor(Color.Gray) 1203 .fontSize(14) 1204 .fontColor(Color.Black) 1205 } 1206 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 1207 .width(this.params.width) 1208 .height(this.params.height) 1209 } 1210 } 1211 1212 // In @Builder, add the specific dynamic component content. 1213 @Builder 1214 function TextInputBuilder1(params:Params) { 1215 TextInputComponent1({params: params}) 1216 .width(params.width) 1217 .height(params.height) 1218 .backgroundColor(Color.Pink) 1219 } 1220 1221 @Component 1222 struct TextInputComponent2 { 1223 @Prop params: Params; 1224 @State bkColor: Color = Color.White; 1225 1226 build() { 1227 Column() { 1228 Text("display:overlay") 1229 TextInput({text: '', placeholder: 'please input your word...'}) 1230 .placeholderColor(Color.Gray) 1231 .id(this.params?.elementId) 1232 .placeholderFont({size: 13, weight: 400}) 1233 .caretColor(Color.Gray) 1234 .fontSize(14) 1235 .fontColor(Color.Black) 1236 } 1237 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 1238 .width(this.params.width) 1239 .height(this.params.height) 1240 } 1241 } 1242 1243 1244 // In @Builder, add the specific dynamic component content. 1245 @Builder 1246 function TextInputBuilder2(params:Params) { 1247 TextInputComponent2({params: params}) 1248 .width(params.width) 1249 .height(params.height) 1250 .backgroundColor(Color.Gray) 1251 } 1252 1253 @Entry 1254 @Component 1255 struct Page{ 1256 browserTabController: webview.WebviewController = new webview.WebviewController(); 1257 private nodeControllerMap: Map<string, MyNodeController> = new Map(); 1258 @State componentIdArr: Array<string> = []; 1259 @State widthMap: Map<string, number> = new Map(); 1260 @State heightMap: Map<string, number> = new Map(); 1261 @State positionMap: Map<string, Edges> = new Map(); 1262 @State edges: Edges = {}; 1263 uiContext: UIContext = this.getUIContext(); 1264 1265 build() { 1266 Row() { 1267 Column() { 1268 Stack() { 1269 ForEach(this.componentIdArr, (componentId: string) => { 1270 NodeContainer(this.nodeControllerMap.get(componentId)) 1271 .position(this.positionMap.get(componentId)) 1272 .width(this.widthMap.get(componentId)) 1273 .height(this.heightMap.get(componentId)) 1274 }, (embedId: string) => embedId) 1275 // Load the local text.html page. 1276 Web({src: $rawfile("overlay.html"), controller: this.browserTabController}) 1277 // Enable same-layer rendering. 1278 .enableNativeEmbedMode(true) 1279 // Obtain the lifecycle change data of the embed tag. 1280 .onNativeEmbedLifecycleChange((embed) => { 1281 console.log("NativeEmbed surfaceId" + embed.surfaceId); 1282 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page. 1283 const componentId = embed.info?.id?.toString() as string 1284 if (embed.status == NativeEmbedStatus.CREATE) { 1285 console.log("NativeEmbed create" + JSON.stringify(embed.info)); 1286 // Create a NodeController instance, set parameters, and rebuild. 1287 let nodeController = new MyNodeController() 1288 // 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. 1289 nodeController.setRenderOption({surfaceId : embed.surfaceId as string, 1290 type : embed.info?.type as string, 1291 renderType : NodeRenderType.RENDER_TYPE_TEXTURE, 1292 embedId : embed.embedId as string, 1293 width : this.uiContext.px2vp(embed.info?.width), 1294 height : this.uiContext.px2vp(embed.info?.height)}) 1295 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 1296 nodeController.setDestroy(false); 1297 // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key. 1298 this.nodeControllerMap.set(componentId, nodeController); 1299 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 1300 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 1301 this.positionMap.set(componentId, this.edges); 1302 // 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. 1303 this.componentIdArr.push(componentId) 1304 } else if (embed.status == NativeEmbedStatus.UPDATE) { 1305 let nodeController = this.nodeControllerMap.get(componentId); 1306 console.log("NativeEmbed update" + JSON.stringify(embed)); 1307 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 1308 this.positionMap.set(componentId, this.edges); 1309 this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width)); 1310 this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height)); 1311 nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject) 1312 } else if (embed.status == NativeEmbedStatus.DESTROY) { 1313 console.log("NativeEmbed destroy" + JSON.stringify(embed)); 1314 let nodeController = this.nodeControllerMap.get(componentId); 1315 nodeController?.setDestroy(true) 1316 this.nodeControllerMap.clear(); 1317 this.positionMap.delete(componentId); 1318 this.widthMap.delete(componentId); 1319 this.heightMap.delete(componentId); 1320 this.componentIdArr.filter((value: string) => value != componentId) 1321 } else { 1322 console.log("NativeEmbed status" + embed.status); 1323 } 1324 })// Obtain the touch event information of components for same-layer rendering. 1325 .onNativeEmbedGestureEvent((touch) => { 1326 console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent)); 1327 this.componentIdArr.forEach((componentId: string) => { 1328 let nodeController = this.nodeControllerMap.get(componentId); 1329 // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 1330 if(nodeController?.getEmbedId() == touch.embedId) { 1331 let ret = nodeController?.postEvent(touch.touchEvent) 1332 if(ret) { 1333 console.log("onNativeEmbedGestureEvent success " + componentId); 1334 } else { 1335 console.log("onNativeEmbedGestureEvent fail " + componentId); 1336 } 1337 if(touch.result) { 1338 // Notify the Web component of the consumption result of the gesture event. 1339 touch.result.setGestureEventResult(ret); 1340 } 1341 } 1342 }) 1343 }) 1344 .border({width: 2, color: Color.Gray}) 1345 .height("50%") 1346 } 1347 } 1348 } 1349 } 1350 } 1351 ``` 1352 1353- Example of a frontend page: 1354 1355 The sample code uses the **embed** tag. If the **object** tag is used, register the **object** tag and **type** on the eTS side. 1356 ```html 1357 <!--HAP's src/main/resources/rawfile/overlay.html--> 1358 <!DOCTYPE html> 1359 <html> 1360 <head> 1361 <title>Same-Layer Rendering HTML</title> 1362 <meta name="viewport" content="initial-scale=1.0"> 1363 </head> 1364 <body> 1365 <div> 1366 <div id = "test" style = "position: absolute; z-index: 9999; text-align: center; background-color: rgb(61, 157, 180); top: 40px; left: 30px; width: 300px; height: 120px"> 1367 z-index: 9999 1368 </div> 1369 1370 <embed id = "input1" type = "native/view1" arkwebnativestyle = "display:overlay-infinity" style = "position: absolute; top: 60px; left: 50px; width: 300px; height: 100px"> 1371 1372 <embed id = "input2" type = "native/view2" arkwebnativestyle = "display:overlay" style = "position: absolute; top: 150px; left: 40px; width: 300px; height: 100px"> 1373 </div> 1374 </body> 1375 </html> 1376 ``` 1377 1378- Demo: 1379 1380 The **display** attribute of **arkwebnativestyle** is not set. 1381 1382  1383 1384 The **display** attribute of **arkwebnativestyle** is set. 1385 1386  1387 1388## FAQs 1389### What should I do if the same-layer rendered components are stretched? 1390 1391- The component height is too high. 1392 1393 Due to GPU limitations, the maximum height of the same-layer tag is 8000 px. If this limit is exceeded, the component will be stretched. In this case, you need to set the height of the same-layer tag to less than 8000 px. 1394 1395- The width and height of the custom component do not match those of the same-layer rendered tag. 1396 1397 The width and height of the custom same-layer rendered component must be the same as those of the same-layer tag. The following is an example: 1398 ```ts 1399 @Component 1400 struct TextInputComponent { 1401 @Prop params: Params 1402 @State bkColor: Color = Color.White 1403 1404 build() { 1405 Column() { 1406 TextInput({text: '', placeholder: 'please input your word...'}) 1407 .fontColor(Color.Black) 1408 } 1409 // The width and height of the outermost custom container component must be the same as those of the same-layer tag. 1410 .width(this.params.width) 1411 .height(this.params.height) 1412 } 1413 } 1414 ``` 1415 1416### How do I transparently transmit the events captured by the same-layer rendered component to the web front end? 1417For gesture events of the same-layer rendered tag, you can set the gesture event consumption result through [setGestureEventResult()](../reference/apis-arkweb/arkts-basic-components-web-EventResult.md#setgestureeventresult14). The gesture events can be consumed on the system component side or ArkWeb side. To consume gesture events on the system component side and ArkWeb side at the same time, set **stopPropagation** to **false** in [setGestureEventResult()](../reference/apis-arkweb/arkts-basic-components-web-EventResult.md#setgestureeventresult14). In this way, gesture events are propagated to ArkWeb when being consumed on the system component side. 1418 1419### What should I do if plug-ins cannot be displayed on the same-layer rendered page? 1420 1421- The same-layer rendering [enableNativeEmbedMode](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#enablenativeembedmode11) is not enabled. 1422 1423 Explicitly enable same-layer rendering. 1424 ```ts 1425 Web({ src: $rawfile("text.html"), controller: this.controller }) 1426 // Enable same-layer rendering. 1427 .enableNativeEmbedMode(true) 1428 ``` 1429 1430- The same-layer tag is incorrectly used. 1431 1432 To use the **\<embed>** tag, you need to explicitly write "embed" and use the **type** starting with "native/". To use the **\<object>** tag, you need to register it and **type**. 1433 1434### What should I do if the cursor and text box of the ArkUI component (such as TextInput) that involves UI interaction are misplaced? 1435You need to use a **Stack** to wrap the same-layer component container and **BuilderNode**. Then you need to bind **NodeContainer** to the position of the same-layer tag. The following is an example: 1436```ts 1437ForEach(this.componentIdArr, (componentId: string) => { 1438 NodeContainer(this.nodeControllerMap.get(componentId)) 1439 // The same-layer component container should be bound to the width, height, and position of the same-layer tag. 1440 .position(this.positionMap.get(componentId)) 1441 .width(this.widthMap.get(componentId)) 1442 .height(this.heightMap.get(componentId)) 1443}, (embedId: string) => embedId) 1444``` 1445