1# RenderNode 2 3## Overview 4 5For third-party frameworks that do not have their own rendering environment, although they have implemented frontend parsing and processing for layout and events, they rely on the system's basic rendering and animation capabilities. The universal attributes and events on [FrameNode](./arkts-user-defined-arktsNode-frameNode.md) are superfluous for such frameworks, leading to redundant operations, including handling logic for layout and events. 6 7This is where [RenderNode](../reference/apis-arkui/js-apis-arkui-renderNode.md) comes into the picture. **RenderNode** is a more lightweight rendering node that includes only rendering-related capabilities. It exposes the capability to set basic rendering attributes and provides capabilities for dynamically adding or removing nodes, as well as custom drawing. This can supply third-party frameworks with basic rendering and animation functionalities. 8 9## Creating and Removing Nodes 10 11You can create and remove nodes with **RenderNode**. You can create a custom instance of **RenderNode** using its constructor, and the instance thereby created corresponds to an entity node. You can use the **dispose** API in **RenderNode** to break the binding with the entity node. 12 13## Operating the Node Tree 14 15With **RenderNode**, you can add, delete, query, and modify nodes, thereby changing the subtree structure of nodes; you can also query the parent-child relationships to obtain the results. 16 17> **NOTE** 18> 19> - The subtree structure obtained through queries in **RenderNode** is constructed based on the parameters passed through the APIs of **RenderNode**. 20> 21> - To display a RenderNode in conjunction with built-in components, you need to mount the RenderNode obtained from a FrameNode onto the component tree. 22 23## Setting and Obtaining Rendering-related Properties 24 25In **RenderNode**, you can set rendering-related properties, such as **backgroundColor**, **clipToFrame**, and **opacity**. For details about the supported attributes, see [RenderNode](../reference/apis-arkui/js-apis-arkui-renderNode.md#rendernode). 26 27> **NOTE** 28> 29> - The properties obtained from a query in **RenderNode** are the values that have been explicitly set. 30> 31> - If no parameters are provided or if the provided parameters are invalid, the query will return the default values. 32> 33> - Avoid modifying RenderNodes in a BuilderNode. 34 35```ts 36import { FrameNode, NodeController, RenderNode } from '@kit.ArkUI'; 37 38const renderNode = new RenderNode(); 39renderNode.frame = { x: 0, y: 0, width: 200, height: 350 }; 40renderNode.backgroundColor = 0xffff0000; 41for (let i = 0; i < 5; i++) { 42 const node = new RenderNode(); 43 // Set the frame size of the node. 44 node.frame = { x: 10, y: 10 + 60 * i, width: 50, height: 50 }; 45 // Set the background color of the node. 46 node.backgroundColor = 0xff00ff00; 47 // Mount the new node to the RenderNode. 48 renderNode.appendChild(node); 49} 50 51class MyNodeController extends NodeController { 52 private rootNode: FrameNode | null = null; 53 54 makeNode(uiContext: UIContext): FrameNode | null { 55 this.rootNode = new FrameNode(uiContext); 56 57 const rootRenderNode = this.rootNode?.getRenderNode(); 58 if (rootRenderNode) { 59 rootRenderNode.appendChild(renderNode); 60 } 61 return this.rootNode; 62 } 63} 64 65@Entry 66@Component 67struct Index { 68 private myNodeController: MyNodeController = new MyNodeController(); 69 70 build() { 71 Row() { 72 NodeContainer(this.myNodeController) 73 .width(200) 74 .height(350) 75 Button('getNextSibling') 76 .onClick(() => { 77 const child = renderNode.getChild(1); 78 const nextSibling = child!.getNextSibling() 79 if (child === null || nextSibling === null) { 80 console.log('the child or nextChild is null'); 81 } else { 82 // Obtain the position of the child node. 83 console.log(`the position of child is x: ${child.position.x}, y: ${child.position.y}, ` + 84 `the position of nextSibling is x: ${nextSibling.position.x}, y: ${nextSibling.position.y}`); 85 } 86 }) 87 } 88 } 89} 90``` 91 92## Using Custom Drawing 93 94Override the [draw](../reference/apis-arkui/js-apis-arkui-renderNode.md#draw) API in **RenderNode** to customize the drawing content and use the [invalidate](../reference/apis-arkui/js-apis-arkui-renderNode.md#invalidate) API to manually trigger a redraw of the node. 95 96> **NOTE** 97> 98> - Triggering multiple **invalidate** calls at once will only result in a single redraw. 99> 100> - Custom drawing can be implemented by calling ArkTS APIs or Node-APIs. 101 102**ArkTS API sample code** 103 104```ts 105import { FrameNode, NodeController, RenderNode } from '@kit.ArkUI'; 106import { drawing } from '@kit.ArkGraphics2D'; 107 108class MyRenderNode extends RenderNode { 109 draw(context: DrawContext) { 110 // Obtain the canvas object. 111 const canvas = context.canvas; 112 // Create a brush. 113 const brush = new drawing.Brush(); 114 // Set the brush color. 115 brush.setColor({ alpha: 255, red: 255, green: 0, blue: 0 }); 116 canvas.attachBrush(brush); 117 // Draw a rectangle. 118 canvas.drawRect({ left: 0, right: 200, top: 0, bottom: 200 }); 119 canvas.detachBrush(); 120 } 121} 122 123const renderNode = new MyRenderNode(); 124renderNode.frame = { x: 0, y: 0, width: 300, height: 300 }; 125renderNode.backgroundColor = 0xff0000ff; 126renderNode.opacity = 0.5; 127 128class MyNodeController extends NodeController { 129 private rootNode: FrameNode | null = null; 130 131 makeNode(uiContext: UIContext): FrameNode | null { 132 this.rootNode = new FrameNode(uiContext); 133 134 const rootRenderNode = this.rootNode?.getRenderNode(); 135 if (rootRenderNode !== null) { 136 rootRenderNode.frame = { x: 0, y: 0, width: 500, height: 500 } 137 rootRenderNode.appendChild(renderNode); 138 } 139 140 return this.rootNode; 141 } 142} 143 144@Entry 145@Component 146struct Index { 147 private myNodeController: MyNodeController = new MyNodeController(); 148 149 build() { 150 Column() { 151 NodeContainer(this.myNodeController) 152 .width('100%') 153 Button('Invalidate') 154 .onClick(() => { 155 // Triggering multiple invalidate calls at once will only result in a single redraw. 156 renderNode.invalidate(); 157 renderNode.invalidate(); 158 }) 159 } 160 } 161} 162``` 163 164**Node-API sample code** 165 166The C++ side can obtain the canvas through the Node-API and perform subsequent custom drawing operations. 167 168```c++ 169// native_bridge.cpp 170#include "napi/native_api.h" 171#include <native_drawing/drawing_canvas.h> 172#include <native_drawing/drawing_color.h> 173#include <native_drawing/drawing_path.h> 174#include <native_drawing/drawing_pen.h> 175 176static napi_value OnDraw(napi_env env, napi_callback_info info) 177{ 178 size_t argc = 4; 179 napi_value args[4] = { nullptr }; 180 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 181 182 int32_t id; 183 napi_get_value_int32(env, args[0], &id); 184 185 // Obtain the pointer to the canvas. 186 void* temp = nullptr; 187 napi_unwrap(env, args[1], &temp); 188 OH_Drawing_Canvas *canvas = reinterpret_cast<OH_Drawing_Canvas*>(temp); 189 190 // Obtain the canvas width. 191 int32_t width; 192 napi_get_value_int32(env, args[2], &width); 193 194 // Obtain the canvas height. 195 int32_t height; 196 napi_get_value_int32(env, args[3], &height); 197 198 // Pass in information such as the canvas, height, and width to the drawing API for custom drawing. 199 auto path = OH_Drawing_PathCreate(); 200 OH_Drawing_PathMoveTo(path, width / 4, height / 4); 201 OH_Drawing_PathLineTo(path, width * 3 / 4, height / 4); 202 OH_Drawing_PathLineTo(path, width * 3 / 4, height * 3 / 4); 203 OH_Drawing_PathLineTo(path, width / 4, height * 3 / 4); 204 OH_Drawing_PathLineTo(path, width / 4, height / 4); 205 OH_Drawing_PathClose(path); 206 207 auto pen = OH_Drawing_PenCreate(); 208 OH_Drawing_PenSetWidth(pen, 10); 209 OH_Drawing_PenSetColor(pen, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0x00, 0x00)); 210 OH_Drawing_CanvasAttachPen(canvas, pen); 211 212 OH_Drawing_CanvasDrawPath(canvas, path); 213 214 return nullptr; 215} 216 217EXTERN_C_START 218static napi_value Init(napi_env env, napi_value exports) 219{ 220 napi_property_descriptor desc[] = { 221 { "nativeOnDraw", nullptr, OnDraw, nullptr, nullptr, nullptr, napi_default, nullptr } 222 }; 223 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 224 return exports; 225} 226EXTERN_C_END 227 228static napi_module demoModule = { 229 .nm_version =1, 230 .nm_flags = 0, 231 .nm_filename = nullptr, 232 .nm_register_func = Init, 233 .nm_modname = "entry", 234 .nm_priv = ((void*)0), 235 .reserved = { 0 }, 236}; 237 238extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 239{ 240 napi_module_register(&demoModule); 241} 242``` 243 244Add the following content to the **src/main/cpp/CMakeLists.txt** file of the project: 245```cmake 246# the minimum version of CMake. 247cmake_minimum_required(VERSION 3.4.1) 248project(NapiTest) 249 250set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 251 252include_directories(${NATIVERENDER_ROOT_PATH} 253 ${NATIVERENDER_ROOT_PATH}/include) 254 255add_library(entry SHARED native_bridge.cpp) 256target_link_libraries(entry PUBLIC libace_napi.z.so) 257target_link_libraries(entry PUBLIC libace_ndk.z.so) 258target_link_libraries(entry PUBLIC libnative_drawing.so) 259``` 260 261Add the definition of the custom drawing API on the ArkTS side to the **src/main/cpp/types/libentry/index.d.ts** file of the project. The following is an example: 262```ts 263import { DrawContext } from '@kit.ArkUI' 264 265export const nativeOnDraw: (id: number, context: DrawContext, width: number, height: number) => number; 266``` 267 268Code in ArkTS: 269 270```ts 271// Index.ets 272import bridge from "libentry.so" // This .so file is written and generated by Node-API. 273import { DrawContext, FrameNode, NodeController, RenderNode } from '@kit.ArkUI' 274 275class MyRenderNode extends RenderNode { 276 draw(context: DrawContext) { 277 // The width and height in the context need to be converted from vp to px. 278 bridge.nativeOnDraw(0, context, vp2px(context.size.height), vp2px(context.size.width)); 279 } 280} 281 282class MyNodeController extends NodeController { 283 private rootNode: FrameNode | null = null; 284 285 makeNode(uiContext: UIContext): FrameNode | null { 286 this.rootNode = new FrameNode(uiContext); 287 288 const rootRenderNode = this.rootNode.getRenderNode(); 289 if (rootRenderNode !== null) { 290 const renderNode = new MyRenderNode(); 291 renderNode.size = { width: 100, height: 100 } 292 rootRenderNode.appendChild(renderNode); 293 } 294 return this.rootNode; 295 } 296} 297 298@Entry 299@Component 300struct Index { 301 private myNodeController: MyNodeController = new MyNodeController(); 302 303 build() { 304 Row() { 305 NodeContainer(this.myNodeController) 306 } 307 } 308} 309``` 310 311## Setting the Label 312 313You can use the [label](../reference/apis-arkui/js-apis-arkui-renderNode.md#label12) API to set label information for **FrameNode**, which makes it easier to identify nodes in the node Inspector. 314 315```ts 316import { RenderNode, FrameNode, NodeController, UIContext } from '@kit.ArkUI'; 317 318class MyNodeController extends NodeController { 319 private rootNode: FrameNode | null = null; 320 321 makeNode(uiContext: UIContext): FrameNode | null { 322 this.rootNode = new FrameNode(uiContext); 323 const renderNode: RenderNode | null = this.rootNode.getRenderNode(); 324 if (renderNode !== null) { 325 const renderChildNode: RenderNode = new RenderNode(); 326 renderChildNode.frame = { x: 0, y: 0, width: 100, height: 100 }; 327 renderChildNode.backgroundColor = 0xffff0000; 328 renderChildNode.label = 'customRenderChildNode'; 329 console.log('label:', renderChildNode.label); 330 renderNode.appendChild(renderChildNode); 331 } 332 333 return this.rootNode; 334 } 335} 336 337@Entry 338@Component 339struct Index { 340 private myNodeController: MyNodeController = new MyNodeController(); 341 342 build() { 343 Column() { 344 NodeContainer(this.myNodeController) 345 .width(300) 346 .height(700) 347 .backgroundColor(Color.Gray) 348 } 349 } 350} 351``` 352