Files

46 lines
2.2 KiB
JavaScript

// Hand-drawn SVG helpers. Output is built from numbers only, so it is safe as innerHTML.
// Bigger chart for the end-of-term review: weekly samples over the whole term,
// a gridline per year and per quarter of the value range.
export function lineChart(values, { w = 260, h = 96, min = 0, max = 100, yearEvery = 52 } = {}) {
const pad = 3;
const n = values.length;
const span = max - min || 1;
const x = (i) => pad + (n > 1 ? (i / (n - 1)) * (w - 2 * pad) : 0);
const y = (v) => h - pad - Math.min(1, Math.max(0, (v - min) / span)) * (h - 2 * pad);
const grid = [];
for (let i = yearEvery; i < n; i += yearEvery) grid.push(`<line x1="${x(i).toFixed(1)}" y1="0" x2="${x(i).toFixed(1)}" y2="${h}"/>`);
for (const f of [0.25, 0.5, 0.75]) grid.push(`<line x1="0" y1="${(h * f).toFixed(1)}" x2="${w}" y2="${(h * f).toFixed(1)}"/>`);
const pts = values.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
return `<svg class="chart" viewBox="0 0 ${w} ${h}" width="100%" preserveAspectRatio="none" aria-hidden="true">` +
`<g class="spark__grid">${grid.join("")}</g>` +
(n > 1 ? `<polyline class="spark__line" points="${pts}" vector-effect="non-scaling-stroke"/>` : "") +
`</svg>`;
}
// Small line chart on graph paper. `values` are plotted evenly left to right.
export function sparkline(values, { w = 84, h = 24, min = 0, max = 100 } = {}) {
const grid = [];
for (let x = 12; x < w; x += 12) grid.push(`<line x1="${x}" y1="0" x2="${x}" y2="${h}"/>`);
for (const f of [0.25, 0.5, 0.75]) grid.push(`<line x1="0" y1="${h * f}" x2="${w}" y2="${h * f}"/>`);
let line = "";
const n = values.length;
if (n > 1) {
const span = max - min || 1;
const pts = values.map((v, i) => {
const x = 2 + (i / (n - 1)) * (w - 4);
const y = h - 2 - Math.min(1, Math.max(0, (v - min) / span)) * (h - 4);
return [x.toFixed(1), y.toFixed(1)];
});
const last = pts[n - 1];
line =
`<polyline class="spark__line" points="${pts.map((p) => p.join(",")).join(" ")}"/>` +
`<circle class="spark__dot" cx="${last[0]}" cy="${last[1]}" r="2"/>`;
}
return `<svg class="spark" viewBox="0 0 ${w} ${h}" width="${w}" height="${h}" aria-hidden="true">` +
`<g class="spark__grid">${grid.join("")}</g>${line}</svg>`;
}