151 lines
4.4 KiB
JavaScript
151 lines
4.4 KiB
JavaScript
// chart.js
|
||
export function drawChart(canvas, history) {
|
||
if (!canvas || !history?.length) return;
|
||
|
||
const rect = canvas.getBoundingClientRect();
|
||
const displayHeight = rect.height || 200;
|
||
const displayWidth = rect.width || 800;
|
||
|
||
canvas.width = displayWidth;
|
||
canvas.height = displayHeight;
|
||
|
||
const ctx = canvas.getContext("2d");
|
||
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), padLeft - 8, y + 4);
|
||
}
|
||
|
||
ctx.setLineDash([]);
|
||
|
||
// ===== ПОДПИСЬ ОСИ Y =====
|
||
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();
|
||
|
||
// ===== ПОДПИСЬ ОСИ X =====
|
||
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);
|
||
}
|
||
} |