• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 绘制图形
2
3绘制能力主要是通过框架提供的绘制组件来支撑,支持svg标准绘制命令。
4
5本节主要学习如何使用绘制组件,绘制详情页食物成分标签(基本几何图形)和应用Logo(自定义图形)。
6
7## 绘制基本几何图形
8
9绘制组件封装了一些常见的基本几何图形,比如矩形Rect、圆形Circle、椭圆形Ellipse等,为开发者省去了路线计算的过程。
10
11FoodDetail页面的食物成分表里,给每一项成分名称前都加上一个圆形的图标作为成分标签。
12
131. 创建Circle组件,在每一项含量成分前增加一个圆形图标作为标签。设置Circle的直径为 6vp。修改FoodDetail页面的ContentTable组件里的IngredientItem方法,在成分名称前添加Circle。
14
15   ```ts
16   // FoodDetail.ets
17   @Component
18   struct ContentTable {
19     private foodItem: FoodData
20
21     @Builder IngredientItem(title:string, colorValue: string, name: string, value: string) {
22       Flex() {
23         Text(title)
24           .fontSize(17.4)
25           .fontWeight(FontWeight.Bold)
26           .layoutWeight(1)
27         Flex({ alignItems: ItemAlign.Center }) {
28           Circle({width: 6, height: 6})
29             .margin({right: 12})
30             .fill(colorValue)
31           Text(name)
32             .fontSize(17.4)
33             .flexGrow(1)
34           Text(value)
35             .fontSize(17.4)
36         }
37         .layoutWeight(2)
38       }
39     }
40
41     build() {
42       ......
43     }
44   }
45   ```
46
472. 每个成分的标签颜色不一样,所以我们在build方法中,调用IngredientItem,给每个Circle填充不一样的颜色。
48
49   ```ts
50   // FoodDetail.ets
51   @Component
52   struct ContentTable {
53     private foodItem: FoodData
54
55     @Builder IngredientItem(title:string, colorValue: string, name: string, value: string) {
56       Flex() {
57         Text(title)
58           .fontSize(17.4)
59           .fontWeight(FontWeight.Bold)
60           .layoutWeight(1)
61         Flex({ alignItems: ItemAlign.Center }) {
62           Circle({width: 6, height: 6})
63             .margin({right: 12})
64             .fill(colorValue)
65           Text(name)
66             .fontSize(17.4)
67             .flexGrow(1)
68           Text(value)
69             .fontSize(17.4)
70         }
71         .layoutWeight(2)
72       }
73     }
74
75     build() {
76       Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
77         this.IngredientItem('Calories', '#FFf54040', 'Calories', this.foodItem.calories + 'kcal')
78         this.IngredientItem('Nutrition', '#FFcccccc', 'Protein', this.foodItem.protein + 'g')
79         this.IngredientItem(' ', '#FFf5d640', 'Fat', this.foodItem.fat + 'g')
80         this.IngredientItem(' ', '#FF9e9eff', 'Carbohydrates', this.foodItem.carbohydrates + 'g')
81         this.IngredientItem(' ', '#FF53f540', 'VitaminC', this.foodItem.vitaminC + 'mg')
82       }
83       .height(280)
84       .padding({ top: 30, right: 30, left: 30 })
85     }
86   }
87   ```
88
89   ![drawing-feature](figures/drawing-feature.png)
90
91## 绘制自定义几何图形
92
93除绘制基础几何图形,开发者还可以使用Path组件来绘制自定义的路线,下面进行绘制应用的Logo图案。
94
951. 在pages文件夹下创建新的页面Logo.ets96
97   ![drawing-feature1](figures/drawing-feature1.png)
98
992. Logo.ets中删掉模板代码,创建Logo Component。
100
101   ```ts
102   @Entry
103   @Component
104   struct Logo {
105     build() {
106     }
107   }
108   ```
109
1103. 创建Flex组件为根节点,宽高设置为100%,设置其在主轴方向和交叉轴方向的对齐方式都为Center,创建Shape组件为Flex子组件。
111
112   Shape组件是所有绘制组件的父组件。如果需要组合多个绘制组件成为一个整体,需要创建Shape作为其父组件。
113
114   我们要绘制的Logo的大小630px * 630px。声明式UI范式支持多种长度单位的设置,在前面的章节中,我们直接使用number作为参数,即采用了默认长度单位vp,虚拟像素单位。vp和设备分辨率以及屏幕密度有关。比如设备分辨率为1176 * 2400,屏幕基准密度(resolution)为3,vp = px / resolution,则该设备屏幕宽度是392vp。
115
116   但是绘制组件采用svg标准,默认采取px为单位的,为方便统一,在这绘制Logo这一部分,统一采取px为单位。声明式UI框架同样也支持px单位,入参类型为string,设置宽度为630px,即210vp,设置方式为width('630px')或者width(210)。
117
118   ```ts
119   @Entry
120   @Component
121   struct Logo {
122     build() {
123       Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
124         Shape() {
125
126         }
127         .height('630px')
128         .width('630px')
129       }
130       .width('100%')
131       .height('100%')
132     }
133   }
134   ```
135
1364. 给页面填充渐变色。设置为线性渐变,偏移角度为180deg,三段渐变 #BDE895 -->95DE7F --> #7AB967,其区间分别为[0, 0.1], (0.1, 0.6], (0.6, 1]。
137
138   ```ts
139   .linearGradient(
140     {
141       angle: 180,
142       colors: [['#BDE895', 0.1], ["#95DE7F", 0.6],  ["#7AB967", 1]]
143   })
144   ```
145
146   ![drawing-feature2](figures/drawing-feature2.png)
147
148   ```ts
149   @Entry
150   @Component
151   struct Logo {
152     build() {
153       Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
154         Shape() {
155
156         }
157         .height('630px')
158         .width('630px')
159       }
160       .width('100%')
161       .height('100%')
162       .linearGradient(
163       {
164         angle: 180,
165         colors: [['#BDE895', 0.1], ["#95DE7F", 0.6],  ["#7AB967", 1]]
166       })
167     }
168   }
169   ```
170
171   ![drawing-feature3](figures/drawing-feature3.png)
172
1735. 绘制第一条路线Path,设置其绘制命令。
174
175   ```ts
176   Path()
177     .commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
178   ```
179
180   Path的绘制命令采用svg标准,上述命令可分解为:
181
182   ```ts
183   M162 128.7
184   ```
185
186   将笔触移动到(Moveto)坐标点(162, 128.7)。
187
188   ```ts
189   a222 222 0 0 1 100.8 374.4
190   ```
191
192   画圆弧线(elliptical arc)半径rx,ry为222,x轴旋转角度x-axis-rotation为0,角度大小large-arc-flag为0,即小弧度角,弧线方向(sweep-flag)为1,即逆时针画弧线,小写a为相对位置,即终点坐标为(162 + 100.8 = 262.8, 128.7 + 374.4 = 503.1)。
193
194   ```ts
195   H198
196   ```
197
198   画水平线(horizontal lineto)到198,即画(262.8, 503.1)到(198, 503.1)的水平线。
199
200   ```ts
201   a36 36 0 0 3 -36 -36
202   ```
203
204   画圆弧线(elliptical arc),含义同上,结束点为(198 - 36 = 162, 503.1 - 36 = 467.1)。
205
206   ```ts
207   V128.7
208   ```
209
210   画垂直线(vertical lineto)到128.7,即画(162, 467.1)到(162, 128.7)的垂直线。
211
212   ```ts
213   z
214   ```
215
216   关闭路径(closepath)。
217
218   ![drawing-feature4](figures/drawing-feature4.png)
219
220   填充颜色为白色,线条颜色为透明。
221
222   ```ts
223   .fill(Color.White)
224   .stroke(Color.Transparent)
225   ```
226
227   ```ts
228   @Entry
229   @Component
230   struct Logo {
231     build() {
232       Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
233         Shape() {
234           Path()
235             .commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
236             .fill(Color.White)
237             .stroke(Color.Transparent)
238         }
239         .height('630px')
240         .width('630px')
241       }
242       .width('100%')
243       .height('100%')
244       .linearGradient(
245         {
246           angle: 180,
247           colors: [['#BDE895', 0.1], ["#95DE7F", 0.6],  ["#7AB967", 1]]
248         })
249     }
250   }
251   ```
252
253   ![drawing-feature5](figures/drawing-feature5.png)
254
2556. 在Shape组件内绘制第二个Path。第二条Path的背景色为渐变色,但是渐变色的填充是其整体的box,所以需要clip将其裁剪,入参为Shape,即按照Shape的形状进行裁剪。
256
257   ```ts
258   Path()
259     .commands('M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z')
260     .fill('none')
261     .stroke(Corlor.Transparent)
262     .linearGradient(
263     {
264       angle: 30,
265       colors: [["#C4FFA0", 0],  ["#ffffff", 1]]
266     })
267     .clip(new Path().commands('M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'))
268   ```
269
270   Path的绘制命令比较长,可以将其作为组件的成员变量,通过this调用。
271
272   ```ts
273   @Entry
274   @Component
275   struct Logo {
276     private pathCommands1:string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
277     build() {
278       ......
279           Path()
280             .commands(this.pathCommands1)
281             .fill('none')
282             .stroke(Color.Transparent)
283             .linearGradient(
284             {
285               angle: 30,
286               colors: [["#C4FFA0", 0],  ["#ffffff", 1]]
287             })
288             .clip(new Path().commands(this.pathCommands1))
289        ......
290     }
291   }
292   ```
293
294   ![drawing-feature6](figures/drawing-feature6.png)
295
2967. 在Shape组件内绘制第二个Path。
297
298   ```ts
299   @Entry
300   @Component
301   struct Logo {
302     private pathCommands1:string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
303     private pathCommands2:string = 'M270.6 128.1 h48.6 c51.6 0 98.4 21 132.3 54.6 a411 411 0 0 3 -45.6 123 c-25.2 45.6 -56.4 84 -87.6 110.4 a206.1 206.1 0 0 0 -47.7 -288 z'
304     build() {
305       Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
306         Shape() {
307           Path()
308             .commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
309             .fill(Color.White)
310             .stroke(Color.Transparent)
311
312           Path()
313             .commands(this.pathCommands1)
314             .fill('none')
315             .stroke(Color.Transparent)
316             .linearGradient(
317             {
318               angle: 30,
319               colors: [["#C4FFA0", 0],  ["#ffffff", 1]]
320             })
321             .clip(new Path().commands(this.pathCommands1))
322
323           Path()
324             .commands(this.pathCommands2)
325             .fill('none')
326             .stroke(Color.Transparent)
327             .linearGradient(
328             {
329               angle: 50,
330               colors: [['#8CC36A', 0.1], ["#B3EB90", 0.4],  ["#ffffff", 0.7]]
331             })
332             .clip(new Path().commands(this.pathCommands2))
333         }
334         .height('630px')
335         .width('630px')
336       }
337       .width('100%')
338       .height('100%')
339       .linearGradient(
340         {
341           angle: 180,
342           colors: [['#BDE895', 0.1], ["#95DE7F", 0.6],  ["#7AB967", 1]]
343         })
344     }
345   }
346   ```
347
348   ![drawing-feature7](figures/drawing-feature7.png)
349
350   完成应用Logo的绘制。Shape组合了三个Path组件,通过svg命令绘制出一个艺术的叶子,寓意绿色健康饮食方式。
351
3528. 添加应用的标题和slogan。
353
354   ```ts
355   @Entry
356   @Component
357   struct Logo {
358     private pathCommands1: string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
359     private pathCommands2: string = 'M270.6 128.1 h48.6 c51.6 0 98.4 21 132.3 54.6 a411 411 0 0 3 -45.6 123 c-25.2 45.6 -56.4 84 -87.6 110.4 a206.1 206.1 0 0 0 -47.7 -288 z'
360
361     build() {
362       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
363         Shape() {
364           Path()
365             .commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
366             .fill(Color.White)
367             .stroke(Color.Transparent)
368
369           Path()
370             .commands(this.pathCommands1)
371             .fill('none')
372             .stroke(Color.Transparent)
373             .linearGradient(
374               {
375                 angle: 30,
376                 colors: [["#C4FFA0", 0], ["#ffffff", 1]]
377               })
378             .clip(new Path().commands(this.pathCommands1))
379
380           Path()
381             .commands(this.pathCommands2)
382             .fill('none')
383             .stroke(Color.Transparent)
384             .linearGradient(
385               {
386                 angle: 50,
387                 colors: [['#8CC36A', 0.1], ["#B3EB90", 0.4], ["#ffffff", 0.7]]
388               })
389             .clip(new Path().commands(this.pathCommands2))
390         }
391         .height('630px')
392         .width('630px')
393
394         Text('Healthy Diet')
395           .fontSize(26)
396           .fontColor(Color.White)
397           .margin({ top: 300 })
398
399         Text('Healthy life comes from a balanced diet')
400           .fontSize(17)
401           .fontColor(Color.White)
402           .margin({ top: 4 })
403       }
404       .width('100%')
405       .height('100%')
406       .linearGradient(
407         {
408           angle: 180,
409           colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
410        })
411     }
412   }
413   ```
414
415   ![drawing-feature8](figures/drawing-feature8.png)