仓颉 ArkUI Path2D 路径对象完全指南:从 SVG 路径字符串到 Canvas 绘制实战
仓颉 ArkUI Path2D 路径对象完全指南:从 SVG 路径字符串到 Canvas 绘制实战
Path2D 是仓颉语言 ArkUI 框架(kit.ArkUI)中用于描述自定义绘制路径的路径对象,它既支持通过 moveTo、lineTo、bezierCurveTo 等接口逐段构建路径,也支持直接用符合 SVG 路径描述规范的字符串一次性构造复杂路径,最终交由 CanvasRenderingContext2D 的 stroke()、fill()、clip() 等接口完成描边、填充与裁剪。本文以官方 API 文档为骨架,结合本仓库中的画布定制指南与 SVG 路径规范,完整讲解 Path2D 的构造方式、12 个核心方法、与 Canvas 组件的配合流程,并提供可直接运行的仓颉示例,帮助你掌握在 OpenHarmony 应用中绘制自定义图形、组合路径与路径变换的完整能力。
一、Path2D 是什么
在 ArkUI 的 Canvas 体系中,绘制自定义图形有两条路径:
- 直接绘制:在
CanvasRenderingContext2D对象上调用moveTo/lineTo/arc等方法描述路径,再调用stroke()/fill()进行绘制; - 先构造 Path2D 对象,再绘制:先单独定义
Path2D对象构造理想的路径,再通过调用CanvasRenderingContext2D对象(以及OffscreenCanvasRenderingContext2D对象)的stroke()接口或者fill()接口进行绘制。
Path2D 就是第二种方式中的"路径对象",它支持通过对象自身的接口完成路径描述,并可被多个绘制上下文复用。该类的系统能力为 SystemCapability.ArkUI.ArkUI.Full,起始版本为 22,使用前需先导入模块:
import kit.ArkUI.*
其类声明如下(cj-canvas-drawing-path2d.md):
public class Path2D {
public init()
public init(path: String)
}
两种构造方式
| 构造器 | 功能 | 说明 |
|---|---|---|
init() |
构造一个空的 Path2D 对象 | 之后通过 moveTo、lineTo、arc、bezierCurveTo 等接口逐段描述路径 |
init(d: ?String) |
使用符合 SVG 路径描述规范的路径字符串构造 Path2D 对象 | d 为必填参数,类型为 ?String,格式参考 Path 组件中 SVG 路径描述规范说明 |
第二种构造方式特别适合复用来自设计稿(如 Figma/SVG 导出的 path 数据)的路径,无需逐行调用接口即可生成完整的复杂路径。
二、Path2D 的 12 个核心方法
Path2D 提供的方法覆盖了路径描述所需的全部基础能力:移动/连线、弧线/圆弧、贝塞尔曲线、椭圆、矩形、闭合路径、路径组合与变换。
2.1 路径基础操作:moveTo / lineTo / closePath
public func moveTo(x: Float64, y: Float64): Unit
public func lineTo(x: Float64, y: Float64): Unit
public func closePath(): Unit
| 方法 | 功能 | 参数说明 |
|---|---|---|
moveTo(x, y) |
将路径的当前坐标点移动到目标点,移动过程中不绘制线条 | x:目标点 X 轴坐标;y:目标点 Y 轴坐标,默认单位 vp |
lineTo(x, y) |
从当前点绘制一条直线到目标点 | 同上,坐标为 vp |
closePath() |
将路径的当前点移回到路径的起点,当前点与起点间画一条直线 | 若形状已闭合或只有一个点,则此方法不执行任何操作 |
这组方法对应 SVG 命令中的 M(moveto)、L(lineto)与 Z(closepath)。使用这三个方法即可勾勒任意多边形轮廓。
2.2 弧线与椭圆:arc / arcTo / ellipse
public func arc(
x: Float64, y: Float64, radius: Float64,
startAngle: Float64, endAngle: Float64,
counterclockwise!: ?Bool = None
): Unit
public func arcTo(
x1: Float64, y1: Float64, x2: Float64, y2: Float64, radius: Float64
): Unit
public func ellipse(
x: Float64, y: Float64, radiusX: Float64, radiusY: Float64,
rotation: Float64, startAngle: Float64, endAngle: Float64,
counterclockwise!: ?Bool = None
): Unit
arc() 绘制弧形路径,参数如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
x / y |
Float64 | 是 | 弧线圆心的 x、y 坐标值,默认单位 vp |
radius |
Float64 | 是 | 弧线的圆半径,默认单位 vp |
startAngle |
Float64 | 是 | 弧线的起始弧度,单位弧度(非角度) |
endAngle |
Float64 | 是 | 弧线的终止弧度,单位弧度 |
counterclockwise |
?Bool | 否 | 命名参数,是否逆时针绘制圆弧。true 逆时针,false 顺时针,默认 None |
arcTo() 依据圆弧经过的两个点与圆弧半径创建圆弧路径:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
x1 / y1 |
Float64 | 是 | 圆弧经过的第一个点的 x、y 坐标,默认单位 vp |
x2 / y2 |
Float64 | 是 | 圆弧经过的第二个点的 x、y 坐标,默认单位 vp |
radius |
Float64 | 是 | 圆弧的圆半径值,默认单位 vp |
ellipse() 在规定的矩形区域绘制一个椭圆:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
x / y |
Float64 | 是 | 椭圆圆心的 x、y 轴坐标,默认单位 vp |
radiusX / radiusY |
Float64 | 是 | 椭圆 x 轴、y 轴的半径长度,默认单位 vp |
rotation |
Float64 | 是 | 椭圆的旋转角度,单位弧度 |
startAngle |
Float64 | 是 | 椭圆绘制的起始点角度,单位弧度 |
endAngle |
Float64 | 是 | 椭圆绘制的结束点角度,单位弧度 |
counterclockwise |
?Bool | 否 | 命名参数,是否逆时针绘制椭圆,默认 None |
注意:
arc/ellipse的角度参数一律使用弧度。绘制整圆/整椭圆时,常用0.0到2π(约 6.28)作为起止角度,例如下方示例中arc(100.0, 75.0, 50.0, 0.0, 6.28)即绘制一个完整圆。
2.3 贝塞尔曲线:quadraticCurveTo / bezierCurveTo
public func quadraticCurveTo(cpx: Float64, cpy: Float64, x: Float64, y: Float64): Unit
public func bezierCurveTo(
cp1x: Float64, cp1y: Float64, cp2x: Float64, cp2y: Float64,
x: Float64, y: Float64
): Unit
| 方法 | 功能 | 关键参数 |
|---|---|---|
quadraticCurveTo |
创建二次贝塞尔曲线的路径 | cpx/cpy:贝塞尔控制点的 x、y 坐标;x/y:路径结束时的坐标 |
bezierCurveTo |
创建三次贝塞尔曲线的路径 | cp1x/cp1y:第一个贝塞尔控制点;cp2x/cp2y:第二个贝塞尔控制点;x/y:路径结束坐标 |
所有坐标默认单位均为 vp。贝塞尔曲线是绘制圆角、波浪线、平滑曲线等不规则形状的核心手段,对应 SVG 命令中的 Q(二次贝塞尔)与 C(三次贝塞尔)。
2.4 矩形:rect
public func rect(x: Float64, y: Float64, width: Float64, height: Float64): Unit
创建矩形路径,参数为:左上角 x、y 坐标(vp),矩形的宽度与高度(vp)。与 fillRect/strokeRect 不同,rect() 只创建路径而不立即绘制,它把矩形"登记"进当前 Path2D 对象,需要后续配合 stroke()/fill() 才可见——这使矩形可以作为复杂路径的一部分参与组合。
2.5 路径组合与变换:addPath / setTransform
public func addPath(path2D: ?Path2D): Unit
public func setTransform(
scaleX: ?Float64, skewX: ?Float64, skewY: ?Float64, scaleY: ?Float64,
translateX: ?Float64, translateY: ?Float64
): Unit
addPath(path2D) 将另一个路径对象添加到当前的路径对象中,参数 path2D 类型为 ?Path2D,路径单位 px。借助该方法可以将基础形状组合成复杂图形,并让同一个子路径在多处复用。
setTransform(...) 对路径对象进行缩放、倾斜和平移的复合操作,六个参数均为必填:
| 参数 | 类型 | 说明 |
|---|---|---|
scaleX |
?Float64 | x 轴方向上的缩放值 |
skewX |
?Float64 | x 轴方向上的倾斜值 |
skewY |
?Float64 | y 轴方向上的倾斜值 |
scaleY |
?Float64 | y 轴方向上的缩放值 |
translateX |
?Float64 | x 轴方向上的平移值 |
translateY |
?Float64 | y 轴方向上的平移值 |
注意参数顺序与常规"2D 变换矩阵"顺序一致(scaleX → skewX → skewY → scaleY → translateX → translateY),设置后对该路径对象的整体几何形态生效。若只需旋转、平移等单一项变换,也可以参考 CanvasRenderingContext2D 的 rotate()/translate()/scale() 思路,通过 setTransform 组合实现。
三、SVG 路径描述规范:用字符串构造 Path2D
Path2D("...") 构造器接受的 d 参数遵循 SVG 路径描述规范。该规范的完整命令定义在 Path 组件文档(cj-graphic-drawing-path.md) 中,下表是全部支持的命令:
| 命令 | 名称 | 参数 | 说明 |
|---|---|---|---|
M |
moveto | (x y) |
在给定 (x,y) 坐标处开始一个新的子路径。例如 M 0 0 将 (0,0) 点作为新子路径的起始点 |
L |
lineto | (x y) |
从当前点到给定 (x,y) 坐标画一条线,该坐标成为新的当前点 |
H |
horizontallineto | x |
从当前点绘制一条水平线,等效于将 y 坐标指定为 0 的 L 命令 |
V |
verticallineto | y |
从当前点绘制一条垂直线,等效于将 x 坐标指定为 0 的 L 命令 |
C |
curveto | (x1 y1 x2 y2 x y) |
以 (x1,y1) 为曲线起点控制点、(x2,y2) 为终点控制点,从当前点到 (x,y) 绘制三次贝塞尔曲线 |
S |
smoothcurveto | (x2 y2 x y) |
以 (x2,y2) 为终点控制点绘制三次贝塞尔曲线;若前一个命令是 C 或 S,起点控制点是上一个命令终点控制点相对起点的映射,否则起点控制点与当前点重合 |
Q |
quadraticBeziercurve | (x1 y1 x y) |
以 (x1,y1) 为控制点,从当前点到 (x,y) 绘制二次贝塞尔曲线 |
T |
smoothquadraticBeziercurveto | (x y) |
绘制二次贝塞尔曲线;若前一个命令是 Q 或 T,控制点为上一个命令终点控制点相对起点的映射,否则控制点与当前点重合 |
A |
ellipticalArc | (rx ry x-axis-rotation large-arc-flag sweep-flag x y) |
从当前点到 (x,y) 绘制椭圆弧。椭圆大小与方向由两个半径 (rx,ry) 与 x-axis-rotation(度)定义,large-arc-flag 与 sweep-flag 决定弧的绘制方式 |
Z |
closepath | none | 通过将当前路径连接回当前子路径的初始点来关闭当前子路径 |
典型用例:commands("M0 20 L50 50 L50 100 Z") 定义了一个三角形——起始于 (0,20),绘制到 (50,50) 的直线,再绘制到 (50,100) 的直线,最后以 Z 绘制 (50,100) 回到 (0,20) 的直线并闭合路径,形成封闭三角形。将这个字符串传给 Path2D("M0 20 L50 50 L50 100 Z"),即可得到等价的 Path2D 对象。
关联参考:Path 绘制组件(
Path()组件本身)的commands()属性同样遵循该规范,单位为 px,像素单位转换方法参考像素单位转换说明。Path2D 构造器与其共用一套 SVG 路径语义,理解其一即可触类旁通。
四、把 Path2D 画出来:与 CanvasRenderingContext2D 配合
Path2D 本身只负责"描述",真正上屏需要 Canvas 组件与其渲染上下文。
4.1 搭建画布
在仓颉 ArkUI 中创建 Canvas 画布(详见 Canvas 组件文档):
var settings: RenderingContextSettings = RenderingContextSettings(antialias: true)
var context: CanvasRenderingContext2D = CanvasRenderingContext2D(this.settings)
RenderingContextSettings(antialias: true)创建渲染上下文设置对象,antialias表示是否启用抗锯齿,默认值为false;CanvasRenderingContext2D(settings)创建 2D 渲染上下文;- 在
build()中通过Canvas(this.context)承载该上下文,并在onReady回调中开始绘制——Canvas 组件构造完成后才会触发onReady,此时才可安全绘制。
4.2 三种使用方式:stroke / fill / clip
CanvasRenderingContext2D 文档 中与 Path2D 直接相关的接口如下:
// 描边:进行边框绘制操作
public func stroke(): Unit
public func stroke(path: Path2D): Unit
// 填充:根据当前填充样式填充指定路径
public func fill(fillRule!: ?CanvasFillRule = None): Unit
public func fill(path: ?Path2D, fillRule!: ?CanvasFillRule = None): Unit
// 裁剪:根据指定路径进行裁剪
public func clip(fillRule!: ?CanvasFillRule = None): Unit
public func clip(path: ?Path2D, fillRule!: ?CanvasFillRule = None): Unit
三种典型用法:
- 描边(stroke):
context.stroke(path)按当前描边样式(strokeStyle、lineWidth等)勾勒路径轮廓; - 填充(fill):
context.fill(path, fillRule)按当前填充样式(fillStyle)填充路径内部;fillRule为可选的填充规则,取值见CanvasFillRule枚举:EvenOdd(奇偶规则)与NonZero(非零环绕规则),详见 cj-common-types.md,在路径自交或存在孔洞时,两种规则会得到不同的填充结果; - 裁剪(clip):
context.clip(path, fillRule)将 Path2D 路径设为裁剪区域,此后的绘制只在该区域内可见,是制作异形卡片、蒙版效果的关键手段。
4.3 完整示例一:用 Path2D 接口构造五边形并填充
以下代码来自画布自定义绘制指南,展示了"构造 Path2D → 设置填充样式 → 填充绘制"的完整链路:
package ohos_app_cangjie_entry
import kit.ArkUI.*
import ohos.arkui.state_macro_manage.*
@Entry
@Component
class EntryView {
private let settings: RenderingContextSettings = RenderingContextSettings(antialias: true)
private let context: CanvasRenderingContext2D = CanvasRenderingContext2D(this.settings)
// 使用Path2D的接口构造一个五边形
var path: Path2D = Path2D()
func build() {
Flex(direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center) {
Canvas(this.context)
.width(100.percent)
.height(100.percent)
.backgroundColor(0XF5DC62)
.onReady(
{
=>
path.moveTo(150.0, 50.0)
path.lineTo(50.0, 150.0)
path.lineTo(100.0, 250.0)
path.lineTo(200.0, 250.0)
path.lineTo(250.0, 150.0)
path.closePath()
// 设定填充色为蓝色
this.context.fillStyle = 0X0097D4
// 使用填充的方式,将Path2D描述的五边形绘制在canvas组件内部
this.context.fill(path)
}
)
}.width(100.percent).height(100.percent)
}
}
要点拆解:
Path2D()空构造后,用moveTo落到起点(150,50),再用 4 次lineTo依次连线,最后closePath()闭合五边形;fillStyle = 0X0097D4设置填充色为蓝色(ArkUI 中ResourceColor支持十六进制整型字面量);context.fill(path)一次性填充整个 Path2D 路径。
4.4 完整示例二:用 arc 绘制圆弧后描边
同样来自该指南,演示 Path2D + stroke 的组合:
package ohos_app_cangjie_entry
import kit.ArkUI.*
import ohos.arkui.state_macro_manage.*
@Entry
@Component
class EntryView {
private let settings: RenderingContextSettings = RenderingContextSettings(antialias: true)
private let context: CanvasRenderingContext2D = CanvasRenderingContext2D(this.settings)
var region: Path2D = Path2D()
func build() {
Flex(direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center) {
Canvas(this.context)
.width(100.percent)
.height(100.percent)
.backgroundColor(0XF5DC62)
.onReady(
{
=>
this.region.arc(100.0, 75.0, 50.0, 0.0, 6.28)
this.context.stroke(this.region)
}
)
}.width(100.percent).height(100.percent)
}
}
arc(100.0, 75.0, 50.0, 0.0, 6.28) 以 (100,75) 为圆心、半径 50、从 0 弧度到约 6.28(2π)弧度绘制整圆路径,随后 context.stroke(region) 以描边方式输出。
五、实用建议与注意事项
- 单位区分:Path2D 各方法(
moveTo、lineTo、arc等)的坐标/半径默认单位为 vp;而addPath的参数说明中路径单位为 px。若设备像素比不同,注意换算(参考像素单位转换)。 - 角度一律用弧度:
arc、ellipse的起始/结束角度与rotation均为弧度制;整圆常用0.0 ~ 6.28(即2π)或借助std.math.MathExtension的Float64.GetPI()计算。 - 路径复用:同一 Path2D 对象可以同时用于
stroke(path)、fill(path)、clip(path),也可以作为addPath的参数组合进其他路径,适合反复绘制同一图形。 - 绘制时机:所有绘制必须在 Canvas 的
onReady回调中进行,此时组件已构造完成,上下文才可用。 - 填充规则:路径自交或含孔洞时,通过
fill(path, fillRule)显式指定EvenOdd或NonZero可精确控制镂空区域。