diff --git a/index.html b/index.html index 4e70942..5d878a7 100644 --- a/index.html +++ b/index.html @@ -490,6 +490,19 @@
+ + +
+

📈 График веса

+

Цель: 58 кг через 8 недель

+ +
+ Неделя 1 + Неделя 4 + Неделя 8 +
+
+
@@ -1018,6 +1031,126 @@ ).join('') : '

Пока нет записей. Заполни данные выше!

'; renderNutrition(); + renderWeightChart(); + } + + function renderWeightChart() { + const canvas = document.getElementById('weight-chart'); + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + const rect = canvas.getBoundingClientRect(); + canvas.width = rect.width; + canvas.height = rect.height; + + const entries = progressData.entries.sort((a, b) => new Date(a.date) - new Date(b.date)); + const startDate = new Date('2026-08-10'); + const totalWeeks = 8; + const startWeight = 65; + const targetWeight = 58; + + // Данные для графика + const weights = []; + const weeks = []; + + for (let w = 0; w <= totalWeeks; w++) { + weeks.push(w); + // Целевая линия: 65 - (w * 7/8) + weights.push({ + week: w, + target: startWeight - (w * (startWeight - targetWeight) / totalWeeks), + actual: null + }); + } + + // Добавляем фактические замеры + entries.forEach(e => { + const date = new Date(e.date); + const daysFromStart = (date - startDate) / (1000 * 60 * 60 * 24); + const week = Math.min(8, Math.max(0, daysFromStart / 7)); + + if (e.weight) { + const weekIndex = Math.round(week); + if (weekIndex >= 0 && weekIndex <= 8) { + weights[weekIndex].actual = parseFloat(e.weight); + } + } + }); + + // Очистка + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Настройки + const padding = 40; + const chartWidth = canvas.width - padding * 2; + const chartHeight = canvas.height - padding * 2; + const weightRange = 10; // 55-75 кг + const minWeight = 55; + + function x(w) { return padding + (w / totalWeeks) * chartWidth; } + function y(weight) { return padding + chartHeight - ((weight - minWeight) / weightRange) * chartHeight; } + + // Сетка + ctx.strokeStyle = '#e0e0e5'; + ctx.lineWidth = 1; + for (let w = 0; w <= totalWeeks; w += 2) { + ctx.beginPath(); + ctx.moveTo(x(w), padding); + ctx.lineTo(x(w), canvas.height - padding); + ctx.stroke(); + } + for (let wt = 56; wt <= 74; wt += 2) { + ctx.beginPath(); + ctx.moveTo(padding, y(wt)); + ctx.lineTo(canvas.width - padding, y(wt)); + ctx.stroke(); + } + + // Целевая линия (красная пунктирная) + ctx.strokeStyle = '#f5576c'; + ctx.lineWidth = 3; + ctx.setLineDash([10, 5]); + ctx.beginPath(); + ctx.moveTo(x(0), y(startWeight)); + ctx.lineTo(x(8), y(targetWeight)); + ctx.stroke(); + ctx.setLineDash([]); + + // Фактические замеры (синяя линия) + ctx.strokeStyle = '#667eea'; + ctx.lineWidth = 3; + ctx.beginPath(); + let started = false; + for (let w = 0; w <= 8; w++) { + if (weights[w].actual !== null) { + if (!started) { + ctx.moveTo(x(w), y(weights[w].actual)); + started = true; + } else { + ctx.lineTo(x(w), y(weights[w].actual)); + } + } + } + ctx.stroke(); + + // Точки замеров + for (let w = 0; w <= 8; w++) { + if (weights[w].actual !== null) { + ctx.fillStyle = '#667eea'; + ctx.beginPath(); + ctx.arc(x(w), y(weights[w].actual), 6, 0, Math.PI * 2); + ctx.fill(); + } + } + + // Подписи + ctx.fillStyle = '#667eea'; + ctx.font = 'bold 14px sans-serif'; + ctx.fillText('Цель: 58 кг', canvas.width - padding - 100, y(targetWeight) - 10); + + ctx.fillStyle = '#667eea'; + ctx.font = 'bold 14px sans-serif'; + ctx.fillText('Старт: 65 кг', padding, y(startWeight) - 10); } // === ПИТАНИЕ ===