138 lines
3.9 KiB
TypeScript
138 lines
3.9 KiB
TypeScript
// chart.js
|
|
export function drawChart(canvas: HTMLCanvasElement, history: number[]): void {
|
|
if (!canvas || !history?.length) return;
|
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
const displayHeight = rect.height || 200;
|
|
canvas.width = rect.width || 800;
|
|
canvas.height = displayHeight;
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) return;
|
|
|
|
const w = canvas.width;
|
|
const h = canvas.height;
|
|
|
|
const padLeft = 45;
|
|
const padRight = 15;
|
|
const padTop = 10;
|
|
const padBottom = 25;
|
|
const plotW = w - padLeft - padRight;
|
|
const plotH = h - padTop - padBottom;
|
|
|
|
// Фон
|
|
ctx.fillStyle = "#0a0f0a";
|
|
ctx.fillRect(0, 0, w, h);
|
|
|
|
// Сетка
|
|
const maxVal = 63;
|
|
const gridLines = 4;
|
|
|
|
ctx.strokeStyle = "#0d2a0d";
|
|
ctx.lineWidth = 0.5;
|
|
ctx.setLineDash([4, 4]);
|
|
|
|
for (let i = 0; i <= gridLines; i++) {
|
|
const val = (maxVal / gridLines) * i;
|
|
const y = padTop + plotH - (val / maxVal) * plotH;
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(padLeft, y);
|
|
ctx.lineTo(w - padRight, y);
|
|
ctx.stroke();
|
|
|
|
ctx.fillStyle = "#00ff88";
|
|
ctx.font = "11px monospace";
|
|
ctx.textAlign = "right";
|
|
ctx.fillText(Math.round(val).toString(), padLeft - 8, y + 4);
|
|
}
|
|
|
|
ctx.setLineDash([]);
|
|
|
|
// Подписи осей
|
|
ctx.fillStyle = "#00ff88";
|
|
ctx.font = "bold 11px monospace";
|
|
|
|
ctx.save();
|
|
ctx.translate(12, padTop + plotH / 2);
|
|
ctx.rotate(-Math.PI / 2);
|
|
ctx.textAlign = "center";
|
|
ctx.fillText("ЗНАЧЕНИЕ", 0, 0);
|
|
ctx.restore();
|
|
|
|
ctx.textAlign = "center";
|
|
ctx.fillText("БУФЕР ПРИНЯТЫХ ДАННЫХ", padLeft + plotW / 2, h - 4);
|
|
|
|
const hasSignal = history.some(v => v > 0);
|
|
|
|
if (!hasSignal) {
|
|
ctx.fillStyle = "#ff4444";
|
|
ctx.font = "14px monospace";
|
|
ctx.textAlign = "center";
|
|
ctx.fillText("Ожидание сигнала...", w / 2, h / 2);
|
|
return;
|
|
}
|
|
|
|
let plotData = history;
|
|
if (history.length > plotW) {
|
|
const step = history.length / plotW;
|
|
plotData = [];
|
|
for (let i = 0; i < plotW; i++) {
|
|
const idx = Math.floor(i * step);
|
|
plotData.push(history[idx]);
|
|
}
|
|
}
|
|
|
|
ctx.strokeStyle = "#e6852d";
|
|
ctx.lineWidth = 1.5;
|
|
ctx.shadowColor = "#e6852d";
|
|
ctx.shadowBlur = 3;
|
|
|
|
ctx.beginPath();
|
|
const stepX = plotW / Math.max(plotData.length - 1, 1);
|
|
|
|
for (let i = 0; i < plotData.length; i++) {
|
|
const x = padLeft + i * stepX;
|
|
const y = padTop + plotH - (plotData[i] / maxVal) * plotH;
|
|
if (i === 0) ctx.moveTo(x, y);
|
|
else ctx.lineTo(x, y);
|
|
}
|
|
|
|
ctx.stroke();
|
|
ctx.shadowBlur = 0;
|
|
|
|
if (history.length > 0) {
|
|
const lastVal = history[history.length - 1];
|
|
const lastX = padLeft + (plotData.length - 1) * stepX;
|
|
const lastY = padTop + plotH - (lastVal / maxVal) * plotH;
|
|
|
|
ctx.fillStyle = "#c46b1f";
|
|
ctx.beginPath();
|
|
ctx.arc(lastX, lastY, 4, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
const labelText = `${lastVal}`;
|
|
const labelWidth = ctx.measureText(labelText).width;
|
|
|
|
ctx.fillStyle = "rgba(0,0,0,0.8)";
|
|
ctx.fillRect(lastX - labelWidth / 2 - 4, lastY - 22, labelWidth + 8, 18);
|
|
|
|
ctx.fillStyle = "#00ff88";
|
|
ctx.font = "bold 12px monospace";
|
|
ctx.textAlign = "center";
|
|
ctx.fillText(labelText, lastX, lastY - 8);
|
|
|
|
const peak = Math.max(...history);
|
|
const min = Math.min(...history);
|
|
|
|
ctx.fillStyle = "rgba(0,0,0,0.7)";
|
|
ctx.fillRect(w - 120, padTop, 110, 42);
|
|
|
|
ctx.fillStyle = "#00ff88";
|
|
ctx.font = "10px monospace";
|
|
ctx.textAlign = "left";
|
|
ctx.fillText(`Пик: ${peak}`, w - 110, padTop + 14);
|
|
ctx.fillText(`Мин: ${min}`, w - 110, padTop + 28);
|
|
ctx.fillText(`Точек: ${history.length}`, w - 110, padTop + 42);
|
|
}
|
|
} |