1# 使用Drawing实现图形绘制与显示
2
3## 场景介绍
4
5@ohos.graphics.drawing模块提供了基本的绘制能力,如绘制矩形、圆形、点、直线、自定义Path、字体等等。
6
7## 接口说明
8
9@ohos.graphics.drawing常用接口如下表所示,详细的接口说明请参考[@ohos.graphics.drawing](../reference/apis-arkgraphics2d/js-apis-graphics-drawing.md)。
10
11| 接口名 | 描述 |
12| -------- | -------- |
13| drawPath(path: Path) : void | 画一个自定义路径。 |
14| drawRect(rect: common2D.Rect): void | 用于绘制一个矩形,默认使用黑色填充。 |
15| drawTextBlob(blob: TextBlob, x: number, y: number): void | 用于绘制一段文字。 |
16| moveTo(x: number, y: number) : void | 设置自定义路径的起始点位置。 |
17| lineTo(x: number, y: number) : void | 添加一条到目标点的线段。 |
18| close(): void | 闭合路径,会添加一条到路径起点位置的线段。 |
19| setAntiAlias(aa: boolean) : void | 用于设置画笔是否开启反走样。开启后,可以使得图形的边缘在显示时更平滑。|
20| setColor(color: common2D.Color) : void | 用于设置画笔和画刷的颜色。|
21| setStrokeWidth(width: number) : void | 用于设置画笔的线宽。|
22| attachPen(pen: Pen): void | 绑定画笔给画布,画布将使用画笔的样式和颜色去绘制图形形状的轮廓。|
23| attachBrush(brush: Brush): void | 绑定画刷给画布,画布将使用画刷的样式和颜色去绘制图形形状,并在其内部进行填充。|
24
25## 开发步骤
26
27使用Drawing进行图形绘制与显示时,需要使用@ohos.graphics.drawing模块的画布画笔绘制基本的2D图形和文字,调用绘制和显示的逻辑,最终在应用上显示图形和文字。
28
29本文以实现2D图形和文字的绘制与显示为例,给出具体的开发指导。
30### 添加开发依赖
31
32**依赖文件**
33```js
34import { FrameNode, NodeController, RenderNode } from '@kit.ArkUI'
35import { common2D, drawing } from '@kit.ArkGraphics2D'
36```
37
38接下来介绍如何使用Drawing接口进行内容绘制。
39
40### 绘制2D图形
41
42以下步骤描述了如何使用@ohos.graphics.drawing模块的画布画笔绘制基本的2D图形和文字:
43
441. **创建RenderNode子类**。创建`RenderNode`子类`MyRenderNode`,并在其中定义绘图函数。`RenderNode`中包含树结构的操作,以及对绘制属性的操作,其中`draw`方法会在`RenderNode`进行绘制时被调用,更多细节请参考[RenderNode](../reference/apis-arkui/js-apis-arkui-renderNode.md)。
45
46    ```js
47    // 创建一个MyRenderNode类,并构建Path形状。
48    class MyRenderNode extends RenderNode {
49
50        async draw(context: DrawContext) {
51            // ...
52        }
53    }
54    ```
55
562. **构建Path形状**。使用path的`moveTo`,`lineTo`和`close`接口构建五角星形状的Path。
57
58    ```js
59    const canvas = context.canvas
60    let height_ = 1200
61    let width_ = 600
62    let len = height_ / 4
63    let aX = width_ / 2
64    let aY = height_ / 4
65    let dX = aX - len * Math.sin(18.0)
66    let dY = aY + len * Math.cos(18.0)
67    let cX = aX + len * Math.sin(18.0)
68    let cY = dY
69    let bX = aX + (len / 2.0)
70    let bY = aY + Math.sqrt((cX - dX) * (cX - dX) + (len / 2.0) * (len / 2.0))
71    let eX = aX - (len / 2.0)
72    let eY = bY;
73
74    // 创建一个path对象,然后使用接口连接成一个五角星形状
75    let path = new drawing.Path()
76
77    // 指定path的起始位置
78    path.moveTo(aX, aY)
79
80    // 用直线连接到目标点
81    path.lineTo(bX, bY)
82    path.lineTo(cX, cY)
83    path.lineTo(dX, dY)
84    path.lineTo(eX, eY)
85
86    // 闭合形状,path绘制完毕
87    path.close()
88    ```
89
903. **设置画笔和画刷样式**。使用`Pen`接口创建一个画笔实例pen,并设置抗锯齿、颜色、线宽等属性,画笔用于形状边框线的绘制。使用`Brush`接口创建一个画刷实例brush,并设置填充颜色,画刷用于形状内部的填充。使用canvas中的`attachPen`和`attachBrush`接口将画笔画刷的实例设置到画布实例中。
91
92    ```js
93    // 创建一个画笔Pen对象,Pen对象用于形状的边框线绘制
94    let pen = new drawing.Pen()
95    pen.setAntiAlias(true)
96    let pen_color : common2D.Color = { alpha: 0xFF, red: 0xFF, green: 0x00, blue: 0x00 }
97    pen.setColor(pen_color)
98    pen.setStrokeWidth(10.0)
99
100    // 将Pen画笔设置到canvas中
101    canvas.attachPen(pen)
102
103    // 创建一个画刷Brush对象,Brush对象用于形状的填充
104    let brush = new drawing.Brush()
105    let brush_color : common2D.Color = { alpha: 0xFF, red: 0x00, green: 0xFF, blue: 0x00 }
106    brush.setColor(brush_color)
107
108    // 将Brush画刷设置到canvas中
109    canvas.attachBrush(brush)
110    ```
111
1124. **绘制Path形状**。使用canvas中的`drawPath`接口将五角星绘制到画布上。
113
114    ```js
115    // 绘制path
116    canvas.drawPath(path)
117    ```
118
1195. **创建MyRenderNode对象**。以上1到4步构建出了MyRenderNode类并在其中定义了绘图的主要函数,接下来创建一个MyRenderNode对象,并设置它的像素格式。
120
121    ```js
122    // 创建一个MyRenderNode对象
123    const newNode = new MyRenderNode()
124    // 定义newNode的像素格式
125    newNode.frame = { x: 100, y: 100, width: 200, height: 800 }
126    newNode.pivot = { x: 0.2, y: 0.8 }
127    newNode.scale = { x: 1, y: 1 }
128    ```
129
1306. **绘制矩形**。使用canvas中的`drawRect`接口绘制矩形。
131
132    ```js
133    class RectRenderNode extends RenderNode {
134        async draw(context: DrawContext) {
135            const canvas = context.canvas
136            const pen = new drawing.Pen()
137            pen.setStrokeWidth(5)
138            pen.setColor({alpha: 255, red: 255, green: 0, blue: 0})
139            canvas.attachPen(pen)
140            canvas.drawRect({ left : 200, right : 500, top : 300, bottom : 900})
141        }
142    }
143    // 创建一个RectRenderNode对象
144    const rectNode = new RectRenderNode()
145    // 定义rectNode的像素格式
146    rectNode.frame = { x: 90, y: 100, width: 200, height: 800 }
147    rectNode.pivot = { x: 0.2, y: 0.8 }
148    rectNode.scale = { x: 1, y: 1 }
149    ```
150
1517. **绘制文字**。使用canvas中的`drawTextBlob`接口绘制文字。
152
153    ```js
154    class TextRenderNode extends RenderNode {
155        async draw(context: DrawContext) {
156            const canvas = context.canvas
157            const brush = new drawing.Brush()
158            brush.setColor({alpha: 255, red: 255, green: 0, blue: 0})
159            const font = new drawing.Font()
160            font.setSize(100)
161            const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8)
162            canvas.attachBrush(brush)
163            canvas.drawTextBlob(textBlob, 90, 500)
164        }
165    }
166    // 创建一个TextRenderNode对象
167    const textNode = new TextRenderNode()
168    // 定义textNode的像素格式
169    textNode.frame = { x: 90, y: 100, width: 200, height: 800 }
170    textNode.pivot = { x: 0.2, y: 0.8 }
171    textNode.scale = { x: 1, y: 1 }
172    ```
173
1748. **创建NodeController子类**。创建`NodeController`的子类`MyNodeController`,并在其中定义创建`FrameNode`的函数。`NodeController`定义了节点容器的控制器,控制着容器里在生命周期中的节点。`FrameNode`定义了节点的基本类型,并包含一个`RenderNode`。
175
176    ```js
177    class MyNodeController extends NodeController {
178        private rootNode: FrameNode | null = null;
179
180        makeNode(uiContext: UIContext): FrameNode {
181            this.rootNode = new FrameNode(uiContext)
182            if (this.rootNode == null) {
183                return this.rootNode
184            }
185            const renderNode = this.rootNode.getRenderNode()
186            if (renderNode != null) {
187                renderNode.frame = { x: 0, y: 0, width: 10, height: 500 }
188                renderNode.pivot = { x: 50, y: 50 }
189            }
190            return this.rootNode
191        }
192    }
193    ```
194
1959. **创建添加节点的接口**。在第8步中创建的`MyNodeController`类中创建添加`RenderNode`的接口。
196
197    ```js
198    addNode(node: RenderNode): void {
199        if (this.rootNode == null) {
200            return
201        }
202        const renderNode = this.rootNode.getRenderNode()
203        if (renderNode != null) {
204            renderNode.appendChild(node)
205        }
206    }
207    ```
208
20910. **创建删除节点的接口**。在第8步中创建的`MyNodeController`类中创建删除`RenderNode`的接口。
210
211    ```js
212    clearNodes(): void {
213        if (this.rootNode == null) {
214            return
215        }
216        const renderNode = this.rootNode.getRenderNode()
217        if (renderNode != null) {
218            renderNode.clearChildren()
219        }
220    }
221    ```
222
22311. **绘制图形和文字**。创建`MyNodeController`实例并将其存入`NodeContainer`,添加button控件供用户点击,并调用已定义的接口。
224
225    ```js
226    @Entry
227    @Component
228    struct RenderTest {
229        private myNodeController: MyNodeController = new MyNodeController()
230        build() {
231            Column() {
232                Row() {
233                    NodeContainer(this.myNodeController)
234                        .height('100%')
235                    Button("Draw Path")
236                        .margin({ bottom: 200, right: 12 })
237                        .onClick(() => {
238                            this.myNodeController.clearNodes()
239                            this.myNodeController.addNode(newNode)
240                        })
241                    Button("Draw Rect")
242                        .margin({ bottom: 200, right: 12 })
243                        .onClick(() => {
244                            this.myNodeController.clearNodes()
245                            this.myNodeController.addNode(rectNode)
246                        })
247                    Button("Draw Text")
248                        .margin({ bottom: 200, right: 12 })
249                        .onClick(() => {
250                            this.myNodeController.clearNodes()
251                            this.myNodeController.addNode(textNode)
252                        })
253                }
254                .width('100%')
255                .justifyContent(FlexAlign.Center)
256                .shadow(ShadowStyle.OUTER_DEFAULT_SM)
257                .alignItems(VerticalAlign.Bottom)
258                .layoutWeight(1)
259            }
260        }
261    }
262    ```
263
26412. 绘制与显示的效果图如下:
265
266    | 主页                                 | 绘制五角星                                            | 绘制矩形                                            | 绘制文字                                            |
267    | ------------------------------------ | ----------------------------------------------- | ------------------------------------ | ------------------------------------ |
268    | ![main](./figures/JSMainPage.jpg) | ![Draw Path](figures/JSdrawPath.jpg) | ![Draw Path](figures/JSdrawRect.jpg) | ![Draw Path](figures/JSdrawText.jpg) |
269
270## 相关实例
271
272针对Drawing图形绘制和显示的更多开发,可参考以下相关示例:
273
274- [字体文本展开(ArkTS)(API12)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-5.0.2-Release/code/BasicFeature/Graphics/Graphics2d/ExpandText)
275
276- [字体绘制(ArkTS)(API12)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-5.0.2-Release/code/BasicFeature/Graphics/Graphics2d/PaintVerbatim)
277
278<!--RP1--><!--RP1End-->
279