Line and Area Marks
Line and area marks materialize ordered backend-neutral paths. Lines connect values; areas close two edges or one density edge against a baseline.
Line marks
createLineMark({ id?, data?, stroke?, strokeWidth?, opacity?, curve?, closed? } = {})
const program = chart()
.createData({ values: cars })
.createLineMark({ stroke: "#7c3aed", opacity: 0.55 })
.encodeX({ field: "Year", fieldType: "temporal" })
.encodeY({ field: "Acceleration", aggregate: "mean" });
The first ID is "line", data defaults to current data, stroke width defaults
to 2, opacity defaults to 1, and curve defaults to "linear". A line begins
as an empty path collection because later grouping determines series
cardinality. Complete encodings materialize sorted commands; color and stroke
dash may regroup them.
For a direct quantitative line, encodeX and encodeY may be called in either
order. The first action stores valid incomplete semantic and scale state while
the path remains empty; the second completes the same final layer, resolved
scales, and graphics in both orders. Aggregate y lines are different: their
grain requires a compatible temporal x encoding and rejects a quantitative
partial that would change the meaning of the line.
When a line is layered immediately after a compatible encoded mark, omitted
data and positions are inferred. Compatible aggregate grain is inferred too,
so an aggregate trend over aggregate bars needs no repeated encodeX or
encodeY call:
const layered = chart()
.createData({ values: cars })
.createBarMark({ id: "bars" })
.encodeX({ field: "Year", fieldType: "temporal" })
.encodeY({ field: "Acceleration", aggregate: "mean" })
.createLineMark({ id: "trend", strokeWidth: 3 });
Both layers reference the same x/y scales. The bar owns its bandwidth while
line vertices use the shared bar centers. Incompatible bin, stack, or offset
policies are not transferred. Pass data explicitly to assemble an independent
line with explicit encodings and scale IDs.
editLineMark({ target?, stroke?, strokeWidth?, opacity?, curve?, closed? })
program.editLineMark({
curve: "monotone",
stroke: "#7c3aed",
strokeWidth: 4,
opacity: 0.55
});
Supported curves are linear, step, step-before, step-after, basis,
cardinal, monotone, and natural. Smooth curves use cubic commands and
two-point series fall back to linear. Monotone paths require strictly increasing
materialized x values.
A constant stroke conflicts with field-driven encodeColor. Appearance is
stored and reapplied whenever scale, Canvas, or grouping changes rebuild paths.
Polar lines and radar paths
Line marks also accept theta/radius positions. The two encoding actions may be called in either order; one channel remains valid semantic state but does not produce a path until both are present.
const radar = chart()
.createData({ values: rows })
.createLineMark({ closed: true, strokeWidth: 2.5 })
.encodeTheta({ field: "category", fieldType: "nominal" })
.encodeR({ field: "score", scale: { domain: [0, 1] } })
.encodeGroup({ field: "series" });
closed defaults to false. When true, every series ends with one closing
Z command; the first row is not duplicated. editLineMark({ closed })
switches an existing Polar line between open and closed. Polar lines currently
accept only curve: "linear"; other interpolation modes remain available to
Cartesian lines. Color, stroke dash, grouping, legends, scale edits, Canvas
resizing, filtering, selection, and highlighting all rebuild the same path
through the shared line materialization lifecycle.
Area marks
createAreaMark({ id?, data?, fill?, opacity?, stroke?, strokeWidth?, curve? } = {})
const area = chart()
.createData({ values: intervalRows })
.createAreaMark()
.encodeX({ field: "year", fieldType: "temporal" })
.encodeYRange({ lower: "lower", upper: "upper" });
Area fill defaults to "#4c78a8", opacity to 0.2, and curve to "linear".
An area becomes renderable with exactly one ranged orientation, or with a
complete density value/density pair. encodeGroup creates one closed path per
nominal group without creating a scale or legend.
editAreaMark({ target?, fill?, opacity?, stroke?, strokeWidth?, curve? })
program.editAreaMark({
opacity: 0.35,
stroke: "#334155",
strokeWidth: 1.5,
curve: "cardinal"
});
stroke: false removes both outline and stored width. A width-only edit
requires an active outline. Constant fill cannot replace a field-driven color
encoding. Complete paths rematerialize immediately; incomplete paths retain the
configuration until their encodings become renderable.
Arc marks
createArcMark({ id?, data?, innerRadius?, padAngle?, fill?, opacity?, stroke?, strokeWidth? } = {})
const donut = chart()
.createCanvas({ width: 640, height: 500, margin: 55 })
.createData({ values: cars })
.createArcMark({ innerRadius: 0.56, padAngle: 1.5 })
.encodeTheta({ field: "Origin", aggregate: "count" })
.encodeColor({ field: "Origin", palette: "tableau10" });
innerRadius is a ratio from 0 inclusive to 1 exclusive. padAngle uses
degrees. Count theta creates proportional pie or donut sectors. Categorical
theta plus quantitative encodeR creates equal-angle radial sectors; repeated
rows in one angle band are drawn larger first so smaller overlays remain
visible. Arc graphics are ordinary closed path commands, so renderers do not
interpret Polar semantics.
editArcMark({ target?, innerRadius?, padAngle?, fill?, opacity?, stroke?, strokeWidth? })
Geometry and appearance edits rebuild complete sectors. An incomplete arc
retains the edited settings until both required encodings exist. Constant
fill cannot replace a field-driven color encoding.
Related
Position encodings · Polar line tutorial · Series encodings · Density · Error bands