sparkline: smooth quadratic curves between data points
This commit is contained in:
parent
2ed0b3da3a
commit
f6c93ad9d9
1 changed files with 23 additions and 19 deletions
|
|
@ -103,30 +103,43 @@ Canvas {
|
||||||
ctx.stroke();
|
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;
|
const baseY = lo < 0 ? yOf(0) : height;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, baseY);
|
ctx.moveTo(0, baseY);
|
||||||
for (let i = 0; i < n; i++) {
|
traceCurve();
|
||||||
const [bx, bw] = xOf(i);
|
|
||||||
const y = yOf(d[i]);
|
|
||||||
ctx.lineTo(bx, y);
|
|
||||||
ctx.lineTo(bx + bw, y);
|
|
||||||
}
|
|
||||||
ctx.lineTo(width, baseY);
|
ctx.lineTo(width, baseY);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
|
|
||||||
// Vertical gradient fill
|
// Vertical gradient fill
|
||||||
const grad = ctx.createLinearGradient(0, height, 0, 0);
|
const grad = ctx.createLinearGradient(0, height, 0, 0);
|
||||||
if (colorAt) {
|
if (colorAt) {
|
||||||
// Explicit colorAt: sample across the value range
|
|
||||||
const steps = 8;
|
const steps = 8;
|
||||||
for (let s = 0; s <= steps; s++) {
|
for (let s = 0; s <= steps; s++) {
|
||||||
const frac = s / steps;
|
const frac = s / steps;
|
||||||
grad.addColorStop(frac, colorAt(lo + frac * range).toString());
|
grad.addColorStop(frac, colorAt(lo + frac * range).toString());
|
||||||
}
|
}
|
||||||
} else if (thresholds.length) {
|
} else if (thresholds.length) {
|
||||||
// Auto-derive from thresholds as smooth gradient
|
|
||||||
const sorted = thresholds.slice().sort((a, b) => a.value - b.value);
|
const sorted = thresholds.slice().sort((a, b) => a.value - b.value);
|
||||||
grad.addColorStop(0, strokeColor.toString());
|
grad.addColorStop(0, strokeColor.toString());
|
||||||
for (const th of sorted) {
|
for (const th of sorted) {
|
||||||
|
|
@ -137,7 +150,6 @@ Canvas {
|
||||||
}
|
}
|
||||||
grad.addColorStop(1, sorted[sorted.length - 1].color.toString());
|
grad.addColorStop(1, sorted[sorted.length - 1].color.toString());
|
||||||
} else {
|
} else {
|
||||||
// Uniform fill
|
|
||||||
grad.addColorStop(0, strokeColor.toString());
|
grad.addColorStop(0, strokeColor.toString());
|
||||||
grad.addColorStop(1, strokeColor.toString());
|
grad.addColorStop(1, strokeColor.toString());
|
||||||
}
|
}
|
||||||
|
|
@ -146,15 +158,7 @@ Canvas {
|
||||||
|
|
||||||
// Stroke line on top
|
// Stroke line on top
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
for (let i = 0; i < n; i++) {
|
traceCurve();
|
||||||
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);
|
|
||||||
}
|
|
||||||
ctx.strokeStyle = strokeColor.toString();
|
ctx.strokeStyle = strokeColor.toString();
|
||||||
ctx.lineWidth = root.lineWidth;
|
ctx.lineWidth = root.lineWidth;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue