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