sparkline: smooth quadratic curves between data points

This commit is contained in:
Damocles 2026-04-25 10:49:08 +02:00
parent 2ed0b3da3a
commit f6c93ad9d9

View file

@ -103,30 +103,43 @@ Canvas {
ctx.stroke();
}
// Build polygon path
// Smooth curve through sample midpoints
function midX(i) {
const [bx, bw] = xOf(i);
return bx + bw / 2;
}
function traceCurve() {
ctx.moveTo(0, yOf(d[0]));
if (n === 1) {
ctx.lineTo(width, yOf(d[0]));
} else {
ctx.lineTo(midX(0), yOf(d[0]));
for (let i = 0; i < n - 1; i++) {
const mx = (midX(i) + midX(i + 1)) / 2;
ctx.quadraticCurveTo(midX(i), yOf(d[i]), mx, (yOf(d[i]) + yOf(d[i + 1])) / 2);
}
ctx.lineTo(midX(n - 1), yOf(d[n - 1]));
ctx.lineTo(width, yOf(d[n - 1]));
}
}
// Filled polygon
const baseY = lo < 0 ? yOf(0) : height;
ctx.beginPath();
ctx.moveTo(0, baseY);
for (let i = 0; i < n; i++) {
const [bx, bw] = xOf(i);
const y = yOf(d[i]);
ctx.lineTo(bx, y);
ctx.lineTo(bx + bw, y);
}
traceCurve();
ctx.lineTo(width, baseY);
ctx.closePath();
// Vertical gradient fill
const grad = ctx.createLinearGradient(0, height, 0, 0);
if (colorAt) {
// Explicit colorAt: sample across the value range
const steps = 8;
for (let s = 0; s <= steps; s++) {
const frac = s / steps;
grad.addColorStop(frac, colorAt(lo + frac * range).toString());
}
} else if (thresholds.length) {
// Auto-derive from thresholds as smooth gradient
const sorted = thresholds.slice().sort((a, b) => a.value - b.value);
grad.addColorStop(0, strokeColor.toString());
for (const th of sorted) {
@ -137,7 +150,6 @@ Canvas {
}
grad.addColorStop(1, sorted[sorted.length - 1].color.toString());
} else {
// Uniform fill
grad.addColorStop(0, strokeColor.toString());
grad.addColorStop(1, strokeColor.toString());
}
@ -146,15 +158,7 @@ Canvas {
// Stroke line on top
ctx.beginPath();
for (let i = 0; i < n; i++) {
const [bx, bw] = xOf(i);
const y = yOf(d[i]);
if (i === 0)
ctx.moveTo(bx, y);
else
ctx.lineTo(bx, y);
ctx.lineTo(bx + bw, y);
}
traceCurve();
ctx.strokeStyle = strokeColor.toString();
ctx.lineWidth = root.lineWidth;
ctx.stroke();