Path
A Path is geometry — a sequence of moves, line segments, quadratic and cubic Bezier curves, and closures. It carries no color, no fill style, no stroke; you hand a Path plus a Paint to canvas.drawPath, and the Paint decides what to do with it.
Paths are the cheapest way to describe complex 2D shapes Skia can render: stars, glyphs, vector-network output, the result of boolean operations between two other paths. A circle drawn with path.addCircle is internally four cubic Bezier segments; the same Path can later be filled, stroked, dashed, hit-tested, transformed, or boolean'd against another path.
Path is a WASM object — call path.delete() when you're done with it.
Building a path manually
moveTo plants a starting point; lineTo, quadTo, and cubicTo extend the current subpath. close connects the current point back to the most recent moveTo. Each method returns the path so you can chain.
const p = new CK.Path();
p.moveTo(80, 200);
p.lineTo(340, 200);
p.lineTo(210, 60);
p.close();
const fill = new CK.Paint();
fill.setColor(CK.Color(70, 120, 200, 1));
fill.setAntiAlias(true);
canvas.drawPath(p, fill);
surface.flush();
p.delete();
fill.delete();
Curves and the cubic-bezier cubicTo
Cubic Beziers take two control points plus an end. The control points decide how aggressively the curve pulls toward them. Move the cursor — the second control point follows it.
const p = new CK.Path();
const stroke = new CK.Paint();
stroke.setStyle(CK.PaintStyle.Stroke);
stroke.setStrokeWidth(3);
stroke.setColor(CK.Color(70, 120, 200, 1));
stroke.setAntiAlias(true);
loop(() => {
p.reset();
p.moveTo(60, 130);
p.cubicTo(150, 30, mouse.x || 280, mouse.y || 230, 360, 130);
canvas.clear(CK.WHITE);
canvas.drawPath(p, stroke);
canvas.drawAnchors(p);
canvas.drawTangents(p);
surface.flush();
});
Convenience constructors
addCircle, addOval, addRect, addRRect, addPoly, addArc build common shapes in one call. addCircle is internally four cubics — Skia's standard quarter-circle approximation.
const p = new CK.Path();
p.addRect(CK.LTRBRect(40, 40, 160, 160));
p.addOval(CK.LTRBRect(180, 40, 300, 160));
p.addCircle(370, 100, 50);
const stroke = new CK.Paint();
stroke.setStyle(CK.PaintStyle.Stroke);
stroke.setStrokeWidth(2);
stroke.setAntiAlias(true);
stroke.setColor(CK.Color(70, 120, 200, 1));
canvas.clear(CK.WHITE);
canvas.drawPath(p, stroke);
surface.flush();
p.delete();
stroke.delete();
A single Path can hold multiple disjoint subpaths. They render as one shape but each add* call starts a new subpath.
Parsing SVG path data
CK.Path.MakeFromSVGString accepts the same d="" string SVG <path> elements use. It returns a fresh Path you must .delete().
const p = CK.Path.MakeFromSVGString(
"M 100 60 L 220 60 L 220 200 L 100 200 Z " +
"M 250 130 m -50 0 a 50 50 0 1 0 100 0 a 50 50 0 1 0 -100 0 Z"
);
const fill = new CK.Paint();
fill.setColor(CK.Color(220, 60, 60, 0.4));
fill.setAntiAlias(true);
const edge = new CK.Paint();
edge.setStyle(CK.PaintStyle.Stroke);
edge.setStrokeWidth(2);
edge.setColor(CK.Color(180, 30, 30, 1));
edge.setAntiAlias(true);
canvas.clear(CK.WHITE);
canvas.drawPath(p, fill);
canvas.drawPath(p, edge);
surface.flush();
p.delete();
fill.delete();
edge.delete();
MakeFromSVGString covers everything in the SVG path mini-language: M m L l H h V v C c S s Q q T t A a Z z. Round-trip via path.toSVGString() gives back a deterministic string that may not match the input character-for-character but draws the same shape.
Transforming a path
offset(dx, dy) translates in place. transform(matrix) accepts a 6- or 9-element affine matrix and bakes the transform into the path's points — the Path itself moves; subsequent draws don't re-transform.
const star = new CK.Path();
for (let i = 0; i < 10; i++) {
const a = -PI / 2 + i * PI / 5;
const r = i % 2 === 0 ? 70 : 30;
if (i === 0) star.moveTo(cos(a) * r, sin(a) * r);
else star.lineTo(cos(a) * r, sin(a) * r);
}
star.close();
const drawn = new CK.Path();
const stroke = new CK.Paint();
stroke.setStyle(CK.PaintStyle.Stroke);
stroke.setStrokeWidth(2);
stroke.setAntiAlias(true);
stroke.setColor(CK.Color(60, 160, 90, 1));
loop(() => {
drawn.reset();
drawn.addPath(star);
const angle = (mouse.x || 200) / 200;
const k = cos(angle), s = sin(angle);
drawn.transform([k, -s, mouse.x || 200, s, k, mouse.y || 130]);
canvas.clear(CK.WHITE);
canvas.drawPath(drawn, stroke);
surface.flush();
});
Inspecting a path
countPoints, getPoint(i), countVerbs, getVerb(i) walk the path point-by-point. toCmds() returns a flat Float32Array of [verb, …coords, verb, …coords] (verbs: 0=move, 1=line, 2=quad, 3=conic, 4=cubic, 5=close). The runtime helpers canvas.drawAnchors(path) and canvas.drawTangents(path) use exactly this for the dot/handle visualizations.
Common methods
Hover any non-primitive type to see its description. Most builders return this so calls chain.
| Member | Args | Returns | Notes |
|---|---|---|---|
addCircle | cx, cy, r: number | this | Append a circle as four cubic Beziers. |
addOval | rect: Rect | this | Append an oval inscribed in rect. |
addPath | other: Path, matrix?: Matrix | this | Copy other's geometry into this path, optionally transformed. |
addPoly | points: Float32Array | number[], close: boolean | this | Append a polyline from a flat [x0, y0, x1, y1, …] array. |
addRect | rect: Rect | this | Append an axis-aligned rectangle. |
addRRect | rrect: RRect | this | Append a rounded rectangle. Per-corner radii via RRect. |
arcToTangent | x1, y1, x2, y2, r: number | this | Arc that tangents the lines from current → (x1, y1) → (x2, y2). |
close | — | this | Connect back to the latest moveTo. |
computeTightBounds | — | Rect | Exact bounds (slower, samples the curves). |
contains | x, y: number | boolean | Hit-test using the current fill rule. |
copy | — | Path | Independent copy. The copy must be .delete()'d separately. |
countPoints | — | number | Number of anchor + control points. |
countVerbs | — | number | Number of verbs (move / line / quad / cubic / close). |
cubicTo | c1x, c1y, c2x, c2y, x, y: number | this | Append a cubic Bezier with two controls and end. |
delete | — | void | Free the WASM memory. Required. |
getBounds | — | Rect | Loose bounds (cheap, ignores curve control points). |
getPoint | i: number | Point | Read a single point by index. |
getVerb | i: number | number | Read a single verb by index. |
lineTo | x: number, y: number | this | Append a line segment to (x, y). |
moveTo | x: number, y: number | this | Start a new subpath at (x, y). |
offset | dx, dy: number | this | Translate the path in place. |
quadTo | cx, cy, x, y: number | this | Append a quadratic Bezier with control (cx, cy), end (x, y). |
reset | — | this | Empty the path so it can be reused. Cheaper than new CK.Path(). |
toCmds | — | Float32Array | Flat array of all verbs + coords. |
toSVGString | — | string | Serialize as SVG d attribute. |
transform | matrix: Matrix | this | Apply a 6- or 9-element affine matrix in place. |
Static factories
| Factory | Args | Returns | Notes |
|---|---|---|---|
CK.Path.MakeFromCmds | cmds: Float32Array | Path | Round-trip from toCmds() output. |
CK.Path.MakeFromOp | a: Path, b: Path, op: PathOp | Path | null | Boolean op between two paths. |
CK.Path.MakeFromPathInterpolation | start: Path, end: Path, t: number | Path | Morph between two compatible paths. |
CK.Path.MakeFromSVGString | d: string | Path | null | Parse an SVG d attribute. |
CK.Path.MakeFromVerbsPointsWeights | verbs: Uint8Array, points: Float32Array, weights?: Float32Array | Path | Direct construction from typed arrays. |
See also
Canvas.drawPath— render a path with a paint.Paint— fill style, stroke width, blend mode, dash effect.PathEffect— dashes, corner rounding, 1D effects.PathOp— boolean op codes.- Memory management — when to delete, who owns what.