add tilt field to chart

This commit is contained in:
2026-07-17 16:14:10 -04:00
parent 93acf143f2
commit 35f0ea6db3
3 changed files with 42 additions and 13 deletions

View File

@@ -48,10 +48,14 @@ export interface ChartProps {
// donut only: inner-radius fraction of the outer radius (0.6 by default).
donutRatio?: number;
// Controls border/gap between pie/doughnut segments (acts as a per-slice & full pie "stroke")
segmentGap?: number; // value in px (default 2)
// Give the chart depth: bars extrude, pie/donut tilt and gain a rim (line/area ignore
// it). An embellishment — flat reads more precisely — but sometimes wanted.
threeD?: boolean;
depth?: number; // 3D extrusion depth in px (default 16).
tilt?: number; // 3D tilt scalar (0 -> full tilt, 1 -> fully flattened)
height?: number; // px of the plot area (default 300). The legend adds its own height.
width?: number; // fix the width instead of measuring the container.
@@ -83,7 +87,6 @@ const DEFAULT_HEIGHT = 300;
const DEFAULT_WIDTH = 640; // used only until the container is measured (and on the server)
const BAR_MAX_W = 24; // cap a bar's thickness; the band's leftover is deliberate air
const BAR_RADIUS = 4; // rounded data-end
const SEG_GAP = 2; // the surface gap between touching marks (stacked segments)
const MARK_R = 4; // hover marker radius (8px mark)
const DEFAULT_DEPTH = 16; // 3D extrusion depth
@@ -410,7 +413,7 @@ function CartesianChart(p: SubProps): JSXElement {
const to = from + v;
if (v >= 0) accPos = to; else accNeg = to;
const isEnd = (v > 0 && s === lastPos) || (v < 0 && s === lastNeg);
const inset = isEnd ? 0 : SEG_GAP; // 2px surface gap between segments
const inset = isEnd ? 0 : (p.props.segmentGap ?? 2);
const vFrom = valuePos(from);
// pull the value end toward the baseline by the gap (except the outer end)
const vTo = valuePos(to) + (h ? (v >= 0 ? -inset : inset) : (v >= 0 ? inset : -inset));
@@ -424,8 +427,8 @@ function CartesianChart(p: SubProps): JSXElement {
// original series index.
const vis = series.map((_, s) => s).filter(shown);
const nS = Math.max(1, vis.length);
const groupSize = Math.min(bf * 0.72, (BAR_MAX_W + SEG_GAP) * nS);
const each = Math.max(1, Math.min(BAR_MAX_W, groupSize / nS - SEG_GAP));
const groupSize = Math.min(bf * 0.72, (BAR_MAX_W + (p.props.segmentGap ?? 2)) * nS);
const each = Math.max(1, Math.min(BAR_MAX_W, groupSize / nS - (p.props.segmentGap ?? 2)));
for (let i = 0; i < count; i++) {
const g = catStart() + bf * i + (bf - groupSize) / 2;
vis.forEach((s, j) => {
@@ -610,7 +613,7 @@ function RadialChart(p: SubProps): JSXElement {
const threeD = () => !!p.props.threeD;
const depth = () => p.props.depth ?? DEFAULT_DEPTH;
const tilt = () => (threeD() ? 0.62 : 1); // vertical squash of the disc when tilted
const tilt = () => (threeD() ? clamp((p.props.tilt ?? 0.85), 0, 1) : 1); // vertical squash of the disc when tilted
const values = () => p.props.series[0]?.data ?? [];
const labels = () => p.props.labels ?? values().map((_, i) => String(i + 1));
@@ -679,7 +682,7 @@ function RadialChart(p: SubProps): JSXElement {
for (const s of slices()) {
if (s.a1 <= s.a0) continue;
const op = hv === null || hv === s.idx ? 1 : 0.55;
out.push(`<path d="${slicePathD(g.cx, g.cy, g.rOut, g.rIn, s.a0, s.a1, g.k)}" fill="${esc(p.colorOf(s.idx))}" fill-opacity="${op}" stroke="var(--color-surface)" stroke-width="${SEG_GAP}"/>`);
out.push(`<path d="${slicePathD(g.cx, g.cy, g.rOut, g.rIn, s.a0, s.a1, g.k)}" fill="${esc(p.colorOf(s.idx))}" fill-opacity="${op}" stroke="var(--color-surface)" stroke-width="${(p.props.segmentGap ?? 2)}"/>`);
}
}
return out.join("");