鸿蒙新特性实战:Shape 矢量图形 — Circle/Ellipse/Rect/Polygon/Polyline/Path 2D 绘图
引言
在移动应用界面设计中,并非所有视觉效果都需要依赖图片资源。图标、装饰性元素、数据可视化图表,以及各种形状边框,都可以通过矢量图形直接在 UI 中绘制——它们不占用额外的安装包空间、不会因屏幕密度变化而模糊,并且可以通过代码灵活调整每一个视觉参数。
HarmonyOS NEXT 在 ArkUI 中内置了一套完整的声明式 2D 矢量绘图组件:
- Shape — 绘图容器,定义坐标系统和全局样式
- Circle — 圆形,通过半径控制
- Ellipse — 椭圆,由宽高独立控制
- Rect — 矩形,支持圆角
- Polygon — 多边形,自动闭合以形成填充区域
- Polyline — 折线,不闭合,适合走势图
- Path — SVG 路径,支持 M/L/C/Q/A/Z 全部标准指令
与 Canvas 的命令式绘图不同(调用 API 绘制,状态不可逆),Shape 系列是声明式组件——每一种形状都是一个 UI 组件,可以直接嵌入 build() 方法,使用属性的方式配置样式,并且天然支持状态驱动的动态更新。
本文构建一个矢量图形实验室 Demo,在 7 种 Shape 组件之间切换,通过滑块和文本输入实时调节图形参数,配合填充色/描边色/描边宽度/虚线/抗锯齿等样式控件,直观地展示每种 Shape 的行为和效果。
读完本文,你将掌握:
- Shape 容器:
viewPort坐标系统、全局fill/stroke/strokeWidth/antiAlias/strokeDashArray - Circle 与 Ellipse:基于
width/height的圆形和椭圆绘制 - Rect:矩形 +
radiusWidth/radiusHeight实现圆角 - Polygon 与 Polyline:
points数组驱动的多顶点连线,闭合与不闭合的差异 - Path:SVG 风格指令字符串——M(移动)、L(直线)、C(三次贝塞尔)、Q(二次贝塞尔)、A(弧线)、Z(闭合)
- 声明式 vs 命令式绘图:Shape 与 Canvas 的本质区别,各自适用的场景
一、Shape 组件体系总览
1.1 Shape — 绘图容器
Shape 是所有矢量形状的父容器。它有两个核心职责:
- 定义坐标系统(
viewPort):设置可视区域的宽高和原点偏移 - 设定默认样式:
fill、stroke、strokeWidth、strokeDashArray、antiAlias会传递给所有子 Shape
Shape() {
Circle({ width: 80, height: 80 })
}
.width('100%')
.height(120)
.viewPort({ x: 0, y: 0, width: 200, height: 200 })
.fill('#3B82F6')
.stroke('#1E40AF')
.strokeWidth(2)
.antiAlias(true)
.strokeDashArray([4, 4])
viewPort 的四个参数含义:
x/y:可视区域左上角在坐标系统中的位置(用于平移视口)width/height:可视区域的尺寸
1.2 样式属性 — 六种通用绘制控制
所有 Shape 子组件都可以通过以下属性控制外观:
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
.fill(color) |
ResourceColor |
形状内部填充颜色 | Color.Black |
.fillOpacity(value) |
number |
填充透明度 (0.0~1.0) | 1.0 |
.stroke(color) |
ResourceColor |
描边颜色 | Color.Black |
.strokeWidth(value) |
number |
描边宽度 (vp) | 1.0 |
.strokeDashArray([dash, gap]) |
Array<number> |
虚线样式,交替的实线段和空白段 | 无 |
.antiAlias(boolean) |
boolean |
是否启用抗锯齿 | true |
注意:strokeDashArray 的值是循环的——[4, 4] 意味着 4px 实线、4px 空白交替;[8, 4, 2, 4] 则形成一个"长短交替"的虚线图案。
1.3 与 Canvas 的本质区别
| 维度 | Shape 声明式绘图 | Canvas 命令式绘图 |
|---|---|---|
| 编程范式 | 声明式:描述"是什么" | 命令式:描述"怎么做" |
| 状态管理 | @State 驱动自动重绘 |
手动 clearRect + 重绘 |
| 组件树 | 每个形状是 UI 组件 | 所有形状在同一个画布上 |
| 交互能力 | 天然支持 .onClick 等组件事件 |
需要手动计算坐标命中检测 |
| 动画 | 配合 animateTo 实现属性动画 |
通过 requestAnimationFrame 逐帧绘制 |
| 性能 | 图形数量少时高效 | 图形数量多时高效 |
| 适合场景 | UI 图标、装饰元素、简单图表 | 游戏、复杂数据可视化、粒子效果 |
选择建议:UI 装饰和图标用 Shape,游戏和复杂图表用 Canvas。
二、Circle — 圆形
Circle 是最简单的 Shape 子组件。通过设置 width 和 height 为相同值绘制正圆,不同值则产生椭圆形。
Shape() {
Circle({ width: 120, height: 120 })
}
.viewPort({ x: 0, y: 0, width: 120, height: 120 })
.fill('#3B82F6')
Circle 没有独立的 radius 属性——半径由 width 和 height 决定。如果需要控制半径,只需保证 width = height = 2 * radius。

三、Ellipse — 椭圆
Ellipse 与 Circle 使用相同的构造函数({ width, height }),当 width ≠ height 时自然形成椭圆。
Shape() {
Ellipse({ width: 100, height: 60 })
}
.viewPort({ x: 0, y: 0, width: 100, height: 60 })
.fill('#10B981')
椭圆没有独立的 X 轴半径和 Y 轴半径参数——它们由 width/2 和 height/2 隐式决定。
四、Rect — 矩形与圆角
Rect 支持三个属性:
Rect()
.width(120) // 矩形宽度 (vp)
.height(80) // 矩形高度 (vp)
.radiusWidth(12) // X 方向圆角半径 (vp)
.radiusHeight(12) // Y 方向圆角半径 (vp)
radiusWidth 和 radiusHeight 可以独立设置,产生不对称的圆角效果。当两者都设为 0 时,就是标准直角矩形;设为一个很大的值(如 999)则接近胶囊形。
五、Polygon — 多边形
Polygon 通过 points 数组定义顶点:
Polygon().points([[x1, y1], [x2, y2], [x3, y3], ...])
Polygon 会自动闭合——最后一个顶点会自动连接到第一个顶点,形成封闭的填充区域。
典型三角形示例:
Shape() {
Polygon().points([[60, 10], [110, 90], [10, 90]])
}
.viewPort({ x: 0, y: 0, width: 120, height: 100 })
.fill('#8B5CF6')
Polygon 是绘制填充型几何图形的首选:三角形、五角星、六边形、箭头等。它天然适合做 UI 中的状态图标和装饰形状。
六、Polyline — 折线
Polyline 的 API 与 Polygon 完全相同(points 数组),但行为不同——Polyline 不闭合,顶点按顺序直线连接,最后一个顶点和第一个顶点之间不会自动连线。
Shape() {
Polyline().points([[10, 80], [40, 20], [70, 60], [100, 10]])
}
.viewPort({ x: 0, y: 0, width: 110, height: 90 })
.stroke('#EC4899')
.strokeWidth(2)
Polyline 适合绘制折线图、心电图、价格走势等不需要闭合的线条类图形。由于没有填充,通常配合 .fill(Color.Transparent) 或直接省略填充。
Polygon vs Polyline 选择口诀:需要填充颜色 → Polygon;只需要线条 → Polyline。
七、Path — SVG 路径
Path 是 Shape 子组件中最强大也最复杂的一个。它通过 commands 属性接受一个SVG 风格的指令字符串:
Path().commands('M10 50 L50 10 L90 50 L50 90 Z')
7.1 指令速查
| 指令 | 含义 | 参数 | 示例 |
|---|---|---|---|
M x y |
移动到(不画线) | x, y | M10 50 |
L x y |
画直线到 | x, y | L50 10 |
C cx1 cy1 cx2 cy2 x y |
三次贝塞尔曲线 | 控制点1, 控制点2, 终点 | C20 10 80 10 90 50 |
Q cx cy x y |
二次贝塞尔曲线 | 控制点, 终点 | Q50 0 100 50 |
A rx ry angle large sweep x y |
弧线 | 半径x, 半径y, 旋转角, 大弧标志, 扫掠方向, 终点 | A45 45 0 0 1 95 50 |
Z |
闭合路径 | 无 | Z |
7.2 实战示例
菱形(Diamond):
M10 50 L50 10 L90 50 L50 90 Z
五角星(Star):
M50 5 L63 38 L98 38 L70 58 L80 93 L50 73 L20 93 L30 58 L2 38 L38 38 Z
扇形(Pie Slice):
M50 5 A45 45 0 0 1 95 50 L50 50 Z
波浪(Wave/Cubic Bezier):
M5 90 C5 30 95 30 95 90
7.3 三次贝塞尔(C)vs 二次贝塞尔(Q)
- C 有 2 个控制点,可以产生 S 形曲线
- Q 有 1 个控制点,只能产生单峰曲线
对于大多数 UI 场景的曲线需求(圆角、波浪边缘、有机形态),二次贝塞尔已经足够——它少一个参数,更容易调出想要的效果。
7.4 弧线(A)参数详解
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
rx, ry:椭圆弧的 X/Y 半径x-axis-rotation:弧线旋转角度(通常为 0)large-arc-flag:0 = 小弧(≤180°),1 = 大弧(>180°)sweep-flag:0 = 逆时针,1 = 顺时针x, y:终点坐标
弧线是 Path 中最复杂的指令,但掌握了 large-arc-flag 和 sweep-flag 的组合就掌握了核心:
| large-arc | sweep | 效果 |
|---|---|---|
| 0 | 0 | 小弧 + 逆时针 |
| 0 | 1 | 小弧 + 顺时针 |
| 1 | 0 | 大弧 + 逆时针 |
| 1 | 1 | 大弧 + 顺时针 |
八、实战 Demo:矢量图形实验室
页面结构
矢量图形实验室
├── 标题栏 — "矢量图形实验室" + Shape 标签
├── 图形类型选择器 — Circle / Ellipse / Rect / Polygon / Polyline / Path
├── 实时渲染预览区
│ ├── 虚线边框画布
│ └── 实时绘制当前选中图形(应用所有样式属性)
├── 属性控制区(按类型动态变化)
│ ├── Circle: 半径滑块
│ ├── Ellipse: 宽度/高度滑块
│ ├── Rect: 宽度/高度/圆角滑块
│ ├── Polygon: 顶点坐标编辑
│ ├── Polyline: 折线坐标编辑
│ └── Path: 路径命令输入 + 6 个预设
├── 样式控制区
│ ├── 填充色(8 种颜色圆点)
│ ├── 描边色(8 种颜色圆点)
│ ├── 描边宽度(0-10 vp 滑块)
│ ├── 虚线开关(Toggle)
│ └── 抗锯齿开关(Toggle)
├── 七种 Shape 组件参考卡片
└── 核心 API 参考 — 15 个关键 API/属性/指令
4 个交互点
- 6 种形状切换 — 点击图形类型标签实时切换,每种形状在预览区以独立渲染方式展示
- 属性滑块调节 — 每种形状有专属参数(半径/宽高/圆角/坐标/路径),通过滑块实时调节预览
- Path 预设切换 — 6 个预设路径(菱形/五角星/扇形/波浪/贝塞尔曲线),点击立即切换
- 样式自由搭配 — 8 色填充 + 8 色描边 + 描边宽度 + 虚线 + 抗锯齿,实时调节实时生效
核心代码实现
图形类型切换和渲染
if (this.shapeType === 0) { // Circle
Shape() {
Circle({ width: this.circleRadius * 2, height: this.circleRadius * 2 })
}
.viewPort({ x: this.strokeW, y: this.strokeW, width: this.viewW, height: this.viewH })
}
if (this.shapeType === 5) { // Path
Shape() {
Path().commands(this.pathCmd)
}
.viewPort({ x: this.strokeW, y: this.strokeW, width: this.viewW, height: this.viewH })
}
每种形状的样式属性(fill/stroke/strokeWidth 等)通过 Shape 容器统一设置,所以预览区的每个条件分支中,Shape 组件的样式链都是相同的。
Polygon/Polyline 的坐标解析
parsePoints(pts: string): number[][] {
let result: number[][] = [];
let parts = pts.split(/[\s,]+/); // 按空白和逗号分割
for (let i = 0; i + 1 < parts.length; i = i + 2) {
let x = parseFloat(parts[i]);
let y = parseFloat(parts[i + 1]);
if (!isNaN(x) && !isNaN(y)) result.push([x, y]);
}
return result;
}
这个辅助函数将字符串 "60,10 110,90 10,90" 解析为 Polygon/Polyline 所需的 number[][] 格式。
Path 预设系统
pathPresets: string[] = [
'M10 50 L50 10 L90 50 L50 90 Z', // 菱形
'M50 5 L63 38 L98 38 L70 58 L80 93 L50 73 L20 93 L30 58 L2 38 L38 38 Z', // 五角星
'M50 5 A45 45 0 0 1 95 50 L50 50 Z', // 扇形
'M5 50 Q50 5 95 50 Q50 95 5 50', // 双二次贝塞尔
'M5 90 C5 30 95 30 95 90', // S 形贝塞尔
'M10 10 L90 10 L90 90 L10 90 Z' // 正方形
];
6 个预设从简单到复杂,覆盖了 Path 的全部指令类型:M(移动)、L(直线)、C(三次贝塞尔)、Q(二次贝塞尔)、A(弧线)、Z(闭合)。
预览效果预期
- Circle 模式:蓝色正圆,半径 60vp,可通过滑块从 10 调到 90
- Rect 模式:圆角矩形,宽 120、高 80、圆角 12,三个独立滑块
- Polygon 模式:三角形(三点坐标),可修改坐标值变为任意多边形
- Path 模式:默认显示菱形(
M10 50 L50 10...Z),可输入任意 SVG 命令 - 样式调整:填充色圆点实时切换,描边宽度从 0 到 10,虚线开关切换实线/虚线
- 抗锯齿效果:在 Path 曲线边缘尤其明显,关闭后边缘会出现锯齿
九、与 CSS SVG / Android VectorDrawable / iOS UIBezierPath 的对比
| 维度 | CSS/HTML SVG | Android VectorDrawable | iOS UIBezierPath | HarmonyOS Shape |
|---|---|---|---|---|
| 绘画范式 | 声明式(XML/HTML) | 声明式(XML) | 命令式(Swift API) | 声明式(ArkUI 组件) |
| 路径指令 | SVG 标准 d 属性 |
SVG 标准 pathData |
方法调用 addLine/to/curve |
SVG 标准 commands 字符串 |
| 圆角矩形 | rect rx="..." |
同 SVG | UIBezierPath(roundedRect:) |
Rect().radiusWidth() |
| 多边形 | <polygon points="..." /> |
同 SVG | 需手动 move + addLine |
Polygon().points([[x,y],...]) |
| 动态更新 | 通过 JS 修改 DOM/React state | 需代码重建 | 修改 path 后 setNeedsDisplay |
@State 驱动自动重绘 |
| 集成方式 | 嵌入 HTML | asSrc() 设置给 ImageView |
CAShapeLayer.path |
直接放在 ArkUI build() 中 |
HarmonyOS 的 Shape 系统继承了 SVG 路径指令的成熟标准,同时将每种形状封装为独立的 ArkUI 组件——这种设计让 Shape 同时享受了 SVG 路径的表达能力和 ArkUI 声明式状态管理的自动更新能力。
十、总结
Shape 组件体系是 HarmonyOS ArkUI 中轻量级矢量绘图的核心工具:
- 7 种组件覆盖了从简单几何(Circle/Rect)到复杂自由路径(Path)的全部需求
- 声明式建模让每种形状都是一个 UI 组件,可以参与
@State驱动的自动刷新 - SVG 路径兼容:Path 的
commands字符串与 Web/CSS 的 SVGd属性完全同构,学习一次,三端通用 - 样式属性丰富:fill/fillOpacity/stroke/strokeWidth/strokeDashArray/antiAlias 六种控制,每种都可以动态切换
- 与 Canvas 互补:Canvas 适合游戏和复杂数据可视化,Shape 适合 UI 装饰、图标和简单图表
结合 animateTo 属性动画,Shape 组件可以实现从形状变换到颜色渐变的流畅动画效果;结合 TransitionEffect,可以实现图形的进场退场动画。HarmonyOS 开发者手中的声明式矢量绘图工具箱,已经达到了生产级的水准。
更多推荐




所有评论(0)