// 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 = 50; // место под значения Y слева const padRight = 20; // отступ справа const padTop = 15; // отступ сверху const padBottom = 30; // отступ снизу для подписей X 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; // 4 линии + 0 = 5 уровней: 0, 16, 32, 48, 64 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); } // Вертикальные линии (временные метки) const timeMarks = 5; ctx.setLineDash([4, 4]); for (let i = 0; i <= timeMarks; i++) { const x = padLeft + (plotW / timeMarks) * i; ctx.beginPath(); ctx.moveTo(x, padTop); ctx.lineTo(x, padTop + plotH); ctx.stroke(); // Подпись снизу const pct = Math.round((i / timeMarks) * 100); ctx.fillStyle = "#008844"; ctx.font = "9px monospace"; ctx.textAlign = "center"; ctx.fillText(`${100 - pct}%`, x, padTop + plotH + 16); } ctx.setLineDash([]); // сброс пунктира // ===== ПОДПИСИ ОСЕЙ ===== ctx.fillStyle = "#00ff88"; ctx.font = "bold 12px monospace"; // Y ctx.save(); ctx.translate(12, padTop + plotH / 2); ctx.rotate(-Math.PI / 2); ctx.textAlign = "center"; ctx.fillText("ЗНАЧЕНИЕ (0–63)", 0, 0); ctx.restore(); // X ctx.textAlign = "center"; ctx.fillText("ВРЕМЯ ← новые", w / 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; } // Линия графика ctx.strokeStyle = "#e6852d"; ctx.lineWidth = 1.5; ctx.shadowColor = "#e6852d"; ctx.shadowBlur = 3; ctx.beginPath(); const stepX = plotW / Math.max(history.length - 1, 1); for (let i = 0; i < history.length; i++) { const x = padLeft + i * stepX; const y = padTop + plotH - (history[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 lastIdx = history.length - 1; const lastX = padLeft + lastIdx * stepX; const lastY = padTop + plotH - (history[lastIdx] / maxVal) * plotH; const lastVal = history[lastIdx]; // Кружок на последней точке ctx.fillStyle = "#c46b1f"; ctx.beginPath(); ctx.arc(lastX, lastY, 4, 0, Math.PI * 2); ctx.fill(); // Подпись значения над точкой ctx.fillStyle = "#00ff88"; ctx.font = "bold 12px monospace"; ctx.textAlign = "left"; ctx.fillText(`${lastVal}`, lastX + 8, lastY - 8); // Карточка статистики в правом верхнем углу const avg = Math.round(history.reduce((a, b) => a + b, 0) / history.length); const peak = Math.max(...history); const min = Math.min(...history); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(w - 140, padTop, 130, 55); ctx.fillStyle = "#00ff88"; ctx.font = "10px monospace"; ctx.textAlign = "left"; ctx.fillText(`Пик: ${peak}`, w - 130, padTop + 14); ctx.fillText(`Сред: ${avg}`, w - 130, padTop + 28); ctx.fillText(`Мин: ${min}`, w - 130, padTop + 42); ctx.fillText(`Всего: ${history.length}`, w - 130, padTop + 56); } }