Добавлен график веса с целевой линией (58 кг за 8 недель)
This commit is contained in:
parent
14fb2b7e81
commit
c15d07a9ad
133
index.html
133
index.html
@ -490,6 +490,19 @@
|
||||
<button class="btn" onclick="addProgress()">Записать</button>
|
||||
</div>
|
||||
<div class="progress-stats" id="progress-stats"></div>
|
||||
|
||||
<!-- График веса -->
|
||||
<div style="margin-top: 30px; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); border-radius: 15px; padding: 25px;">
|
||||
<h3>📈 График веса</h3>
|
||||
<p style="margin-bottom: 15px; color: #666;">Цель: <strong>58 кг</strong> через 8 недель</p>
|
||||
<canvas id="weight-chart" height="80" style="width: 100%; background: white; border-radius: 10px;"></canvas>
|
||||
<div style="display: flex; justify-content: space-between; margin-top: 10px; font-size: 12px; color: #666;">
|
||||
<span>Неделя 1</span>
|
||||
<span>Неделя 4</span>
|
||||
<span>Неделя 8</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="progress-history" style="margin-top: 30px;"></div>
|
||||
</div>
|
||||
|
||||
@ -1018,6 +1031,126 @@
|
||||
).join('') : '<p>Пока нет записей. Заполни данные выше!</p>';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// === ПИТАНИЕ ===
|
||||
|
||||
Loading…
Reference in New Issue
Block a user