Калькулятор суточной нормы калорий (TDEE)

Суточная норма калорий (TDEE) — расчет для поддержания веса, похудения или набора массы, учитывая уровень активности.

Шаг 1: Личные данные

Шаг 2: Активность

Шаг 3: Цели

Шаг 3.1: Желаемый вес

Шаг 4: Состав тела (дополнительно)

Шаг 5: Доля макронутриентов (опционально)

Укажите желаемое соотношение белков, углеводов и жиров в процентах. Это поможет более точно настроить рекомендации по питанию.

Настройка макронутриентов важна для достижения ваших целей: белки способствуют сохранению мышц, углеводы дают энергию, а жиры необходимы для гормонального баланса и усвоения витаминов.

Ваши результаты

Полная суточная потребность в энергии (TDEE): ккал / кДж

Базальный метаболизм (BMR)

По формуле Mifflin-St Jeor: ккал / кДж

По формуле Harris-Benedict: ккал / кДж

Термический эффект пищи (TEF)

ккал (~10% от TDEE)

Распределение макронутриентов

Белки: г (%)

Углеводы: г (%)

Жиры: г (%)

Рекомендации и предупреждения

    Прогноз изменения веса

    document.addEventListener('DOMContentLoaded', () => { const form = document.getElementById('tdee-form'); const steps = document.querySelectorAll('.tdee-step'); const progressBar = document.getElementById('tdee-progress'); const resultsDiv = document.getElementById('tdee-results'); const resetBtn = document.querySelector('.tdee-reset-btn'); const stepOrder = ['1', '2', '3', '3_1', '4', '5']; let currentStepIndex = 0; const heightUnitSelect = document.getElementById('height-unit'); const heightCmGroup = document.getElementById('height-cm-group'); const heightFtInGroup = document.getElementById('height-ft-in-group'); const heightCmInput = document.getElementById('height-cm'); const heightFtInput = document.getElementById('height-ft'); const heightInInput = document.getElementById('height-in'); const weightUnitSelect = document.getElementById('weight-unit'); const weightKgGroup = document.getElementById('weight-kg-group'); const weightLbGroup = document.getElementById('weight-lb-group'); const weightKgInput = document.getElementById('weight-kg'); const weightLbInput = document.getElementById('weight-lb'); const goalSelect = document.getElementById('goal'); const weightChangeSpeedGroup = document.getElementById('weight-change-speed-group'); const weightChangeSpeedInput = document.getElementById('weight-change-speed-input'); const weightChangeSpeedButtonsContainer = document.getElementById('weight-change-speed-buttons'); const targetWeightKgInput = document.getElementById('target-weight-kg'); const targetWeightDisplay = document.getElementById('target-weight-display'); function updateProgressBar() { let actualStepOrder; if (goalSelect.value === 'maintain') { actualStepOrder = stepOrder.filter(s => s !== '3_1'); } else { actualStepOrder = stepOrder; } const currentActualIndex = actualStepOrder.indexOf(stepOrder[currentStepIndex]); const progress = ((currentActualIndex + 1) / actualStepOrder.length) * 100; progressBar.style.width = `${progress}%`; } function showStep(stepNumId, direction = 'next') { const prevStepElement = document.querySelector(`.tdee-step[data-step="${stepOrder[currentStepIndex]}"]`); const nextStepElement = document.querySelector(`.tdee-step[data-step="${stepNumId}"]`); if (prevStepElement) { if (direction === 'next') { prevStepElement.classList.add('tdee-slide-left'); } else { prevStepElement.classList.add('tdee-hidden'); } prevStepElement.style.pointerEvents = 'none'; } if (nextStepElement) { nextStepElement.classList.remove('tdee-hidden', 'tdee-slide-left'); nextStepElement.style.position = 'relative'; nextStepElement.style.transform = 'translateX(0)'; nextStepElement.style.opacity = '1'; nextStepElement.style.pointerEvents = 'auto'; } currentStepIndex = stepOrder.indexOf(stepNumId); updateProgressBar(); } function validateStep(stepId) { let isValid = true; const currentStepElement = document.querySelector(`.tdee-step[data-step="${stepId}"]`); const requiredInputs = currentStepElement.querySelectorAll('[required]:not([style*="display: none"])'); requiredInputs.forEach(input => { if (!input.value || (input.type === 'number' && isNaN(parseFloat(input.value)))) { isValid = false; input.reportValidity(); } }); if (stepId === '1') { if (heightUnitSelect.value === 'ft_in' && (!heightFtInput.value || !heightInInput.value)) { isValid = false; heightFtInput.reportValidity(); heightInInput.reportValidity(); } } if (stepId === '3_1') { const currentWeightKg = parseFloat(document.getElementById('weight-kg').value) || (parseFloat(document.getElementById('weight-lb').value) * 0.453592); const targetWeightKg = parseFloat(targetWeightKgInput.value); const goal = goalSelect.value; if (isNaN(targetWeightKg)) { isValid = false; targetWeightKgInput.reportValidity(); } else if (goal === 'lose' && targetWeightKg >= currentWeightKg) { isValid = false; alert('Желаемый вес для снижения должен быть меньше текущего веса.'); targetWeightKgInput.focus(); } else if (goal === 'gain' && targetWeightKg <= currentWeightKg) { isValid = false; alert('Желаемый вес для набора массы должен быть больше текущего веса.'); targetWeightKgInput.focus(); } } if (stepId === '5') { const proteinPercentage = parseFloat(document.getElementById('protein-percentage').value) || 0; const carbsPercentage = parseFloat(document.getElementById('carbs-percentage').value) || 0; const fatPercentage = parseFloat(document.getElementById('fat-percentage').value) || 0; const totalMacroPercentage = proteinPercentage + carbsPercentage + fatPercentage; if (totalMacroPercentage !== 100) { isValid = false; alert('Сумма процентов белков, углеводов и жиров должна быть равна 100%. Пожалуйста, скорректируйте.'); document.getElementById('protein-percentage').focus(); } } if (stepId === '3' && goalSelect.value !== 'maintain') { if (!weightChangeSpeedInput.value || isNaN(parseFloat(weightChangeSpeedInput.value))) { isValid = false; alert('Пожалуйста, выберите желаемую скорость изменения веса.'); } } return isValid; } document.querySelectorAll('.tdee-next-btn').forEach(button => { button.addEventListener('click', () => { const currentStepId = stepOrder[currentStepIndex]; if (validateStep(currentStepId)) { let nextStepId = button.dataset.nextStep; if (currentStepId === '3') { const goal = goalSelect.value; if (goal === 'maintain') { nextStepId = '4'; } else { nextStepId = '3_1'; } } showStep(nextStepId, 'next'); } }); }); document.querySelectorAll('.tdee-prev-btn').forEach(button => { button.addEventListener('click', () => { let prevStepId = button.dataset.prevStep; if (stepOrder[currentStepIndex] === '4') { const goal = goalSelect.value; if (goal === 'maintain') { prevStepId = '3'; } else { prevStepId = '3_1'; } } showStep(prevStepId, 'prev'); }); }); heightUnitSelect.addEventListener('change', () => { if (heightUnitSelect.value === 'cm') { heightCmGroup.classList.remove('tdee-hidden'); heightFtInGroup.classList.add('tdee-hidden'); heightCmInput.setAttribute('required', 'true'); heightFtInput.removeAttribute('required'); heightInInput.removeAttribute('required'); } else { heightCmGroup.classList.add('tdee-hidden'); heightFtInGroup.classList.remove('tdee-hidden'); heightCmInput.removeAttribute('required'); heightFtInput.setAttribute('required', 'true'); heightInInput.setAttribute('required', 'true'); } }); heightUnitSelect.dispatchEvent(new Event('change')); weightUnitSelect.addEventListener('change', () => { if (weightUnitSelect.value === 'kg') { weightKgGroup.classList.remove('tdee-hidden'); weightLbGroup.classList.add('tdee-hidden'); weightKgInput.setAttribute('required', 'true'); weightLbInput.removeAttribute('required'); } else { weightKgGroup.classList.add('tdee-hidden'); weightLbGroup.classList.remove('tdee-hidden'); weightKgInput.removeAttribute('required'); weightLbInput.setAttribute('required', 'true'); } }); weightUnitSelect.dispatchEvent(new Event('change')); goalSelect.addEventListener('change', () => { if (goalSelect.value === 'maintain') { weightChangeSpeedGroup.style.display = 'none'; weightChangeSpeedInput.removeAttribute('required'); targetWeightKgInput.removeAttribute('required'); targetWeightKgInput.value = ''; } else { weightChangeSpeedGroup.style.display = 'flex'; weightChangeSpeedInput.setAttribute('required', 'true'); targetWeightKgInput.setAttribute('required', 'true'); } updateProgressBar(); }); goalSelect.dispatchEvent(new Event('change')); weightChangeSpeedButtonsContainer.addEventListener('click', (event) => { const clickedButton = event.target.closest('.tdee-button-option'); if (clickedButton) { weightChangeSpeedButtonsContainer.querySelectorAll('.tdee-button-option').forEach(btn => { btn.classList.remove('tdee-selected'); }); clickedButton.classList.add('tdee-selected'); weightChangeSpeedInput.value = clickedButton.dataset.value; } }); weightChangeSpeedButtonsContainer.querySelector(`[data-value="${weightChangeSpeedInput.value}"]`).classList.add('tdee-selected'); resetBtn.addEventListener('click', () => { form.reset(); resultsDiv.classList.remove('tdee-show'); resultsDiv.classList.add('tdee-hidden'); showStep(stepOrder[0]); heightUnitSelect.dispatchEvent(new Event('change')); weightUnitSelect.dispatchEvent(new Event('change')); weightChangeSpeedInput.value = '0.5'; weightChangeSpeedButtonsContainer.querySelectorAll('.tdee-button-option').forEach(btn => { btn.classList.remove('tdee-selected'); }); weightChangeSpeedButtonsContainer.querySelector(`[data-value="0.5"]`).classList.add('tdee-selected'); goalSelect.dispatchEvent(new Event('change')); }); form.addEventListener('submit', (e) => { e.preventDefault(); if (!validateStep(stepOrder[currentStepIndex])) { return; } const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); let heightCm = parseFloat(data['height-cm']); if (data['height-unit'] === 'ft_in') { const ft = parseFloat(data['height-ft']) || 0; const inches = parseFloat(data['height-in']) || 0; heightCm = (ft * 30.48) + (inches * 2.54); } let weightKg = parseFloat(data['weight-kg']); if (data['weight-unit'] === 'lb') { weightKg = parseFloat(data['weight-lb']) * 0.453592; } const gender = data.gender; const age = parseInt(data.age); const activityLevel = data['activity-level']; const goal = data.goal; const weightChangeSpeed = parseFloat(data['weight-change-speed']) || 0; const targetWeightKg = parseFloat(data['target-weight-kg']) || null; const proteinPercentage = parseFloat(data['protein-percentage']) || 25; const carbsPercentage = parseFloat(data['carbs-percentage']) || 50; const fatPercentage = parseFloat(data['fat-percentage']) || 25; let bmrMifflin; if (gender === 'male') { bmrMifflin = (10 * weightKg) + (6.25 * heightCm) - (5 * age) + 5; } else { bmrMifflin = (10 * weightKg) + (6.25 * heightCm) - (5 * age) - 161; } let bmrHarris; if (gender === 'male') { bmrHarris = 88.362 + (13.397 * weightKg) + (4.799 * heightCm) - (5.677 * age); } else { bmrHarris = 447.593 + (9.247 * weightKg) + (3.098 * heightCm) - (4.330 * age); } const activityFactors = { 'sedentary': 1.2, 'lightly_active': 1.375, 'moderately_active': 1.55, 'active': 1.725, 'very_active': 1.9 }; const activityFactor = activityFactors[activityLevel]; const tdeeKcal = bmrMifflin * activityFactor; const tdeeKj = tdeeKcal * 4.184; const tefKcal = tdeeKcal * 0.10; let targetKcal = tdeeKcal; let deficitSurplusText = ''; let deficitSurplusValue = 0; const caloriesPerKg = 7700; if (goal === 'lose') { const deficitKcal = (weightChangeSpeed * caloriesPerKg) / 7; targetKcal = tdeeKcal - deficitKcal; deficitSurplusText = 'дефицит'; deficitSurplusValue = deficitKcal; } else if (goal === 'gain') { const surplusKcal = (weightChangeSpeed * caloriesPerKg) / 7; targetKcal = tdeeKcal + surplusKcal; deficitSurplusText = 'профицит'; deficitSurplusValue = surplusKcal; } const targetKj = targetKcal * 4.184; const proteinGrams = (targetKcal * (proteinPercentage / 100)) / 4; const carbsGrams = (targetKcal * (carbsPercentage / 100)) / 4; const fatGrams = (targetKcal * (fatPercentage / 100)) / 9; let weeksToGoal = 'Не применимо'; if (goal !== 'maintain' && targetWeightKg !== null && weightChangeSpeed > 0) { const weightDifference = Math.abs(weightKg - targetWeightKg); weeksToGoal = (weightDifference / weightChangeSpeed).toFixed(0); } const recommendations = []; const MIN_CALORIES_MALE = 1500; const MIN_CALORIES_FEMALE = 1200; if (targetKcal < (gender === 'male' ? MIN_CALORIES_MALE : MIN_CALORIES_FEMALE)) { recommendations.push(`⚠️ Ваше целевое потребление калорий (${targetKcal.toFixed(0)} ккал) ниже рекомендованного минимума.`); } if (goal === 'lose' && weightChangeSpeed > 1) { recommendations.push('❗ Рекомендуется снижение веса не более 0.5-1 кг в неделю.'); } if (goal === 'gain' && weightChangeSpeed > 0.5) { recommendations.push('❗ Рекомендуется набирать не более 0.25-0.5 кг в неделю для минимизации набора жира.'); } document.getElementById('result-tdee-kcal').textContent = tdeeKcal.toFixed(0); document.getElementById('result-tdee-kj').textContent = tdeeKj.toFixed(0); document.getElementById('result-bmr-mifflin-kcal').textContent = bmrMifflin.toFixed(0); document.getElementById('result-bmr-mifflin-kj').textContent = (bmrMifflin * 4.184).toFixed(0); document.getElementById('result-bmr-harris-kcal').textContent = bmrHarris.toFixed(0); document.getElementById('result-bmr-harris-kj').textContent = (bmrHarris * 4.184).toFixed(0); document.getElementById('result-tef').textContent = tefKcal.toFixed(0); if (goal !== 'maintain') { document.getElementById('target-calories-title').style.display = 'block'; document.getElementById('target-calories-value').style.display = 'block'; document.getElementById('deficit-surplus-value').style.display = 'block'; document.getElementById('target-goal-text').textContent = goal === 'lose' ? 'снижения веса' : 'набора массы'; document.getElementById('result-target-kcal').textContent = targetKcal.toFixed(0); document.getElementById('result-target-kj').textContent = targetKj.toFixed(0); document.getElementById('deficit-surplus-text').textContent = deficitSurplusText; document.getElementById('result-deficit-surplus').textContent = deficitSurplusValue.toFixed(0); document.getElementById('weight-change-title').style.display = 'block'; document.getElementById('weight-change-info').style.display = 'block'; document.getElementById('goal-speed-info').textContent = `${weightChangeSpeed} кг/нед`; document.getElementById('weeks-to-goal').textContent = weeksToGoal; targetWeightDisplay.textContent = targetWeightKg.toFixed(1); } else { document.getElementById('target-calories-title').style.display = 'none'; document.getElementById('target-calories-value').style.display = 'none'; document.getElementById('deficit-surplus-value').style.display = 'none'; document.getElementById('weight-change-title').style.display = 'none'; document.getElementById('weight-change-info').style.display = 'none'; } document.getElementById('result-protein-g').textContent = proteinGrams.toFixed(1); document.getElementById('result-protein-percent').textContent = proteinPercentage; document.getElementById('result-carbs-g').textContent = carbsGrams.toFixed(1); document.getElementById('result-carbs-percent').textContent = carbsPercentage; document.getElementById('result-fat-g').textContent = fatGrams.toFixed(1); document.getElementById('result-fat-percent').textContent = fatPercentage; const recommendationsList = document.getElementById('recommendations-list'); recommendationsList.innerHTML = ''; if (recommendations.length > 0) { document.getElementById('recommendations-title').style.display = 'block'; recommendations.forEach(rec => { const li = document.createElement('li'); li.innerHTML = rec; recommendationsList.appendChild(li); }); } else { document.getElementById('recommendations-title').style.display = 'none'; } // --- БЛОК СОЗДАНИЯ ТАБЛИЦЫ ПРОГНОЗА --- const tableContainer = document.getElementById('weight-prognosis-table-container'); const tableElement = document.getElementById('weight-prognosis-table'); if (goal !== 'maintain' && targetWeightKg !== null && weightChangeSpeed > 0) { tableContainer.classList.remove('tdee-hidden'); const initialWeight = weightKg; let tableHTML = ` Неделя Прогнозируемый вес (кг) `; tableHTML += ` Начало ${initialWeight.toFixed(1)} `; // Безопасный лимит в 520 недель (10 лет) for (let i = 1; i <= 520; i++) { const predictedWeight = (goal === 'lose') ? initialWeight - (weightChangeSpeed * i) : initialWeight + (weightChangeSpeed * i); const isTargetReached = (goal === 'lose') ? predictedWeight <= targetWeightKg : predictedWeight >= targetWeightKg; if (isTargetReached) { tableHTML += ` Цель ${targetWeightKg.toFixed(1)} `; break; } else { tableHTML += ` ${i} ${predictedWeight.toFixed(1)} `; } } tableHTML += ``; tableElement.innerHTML = tableHTML; } else { tableContainer.classList.add('tdee-hidden'); } form.classList.add('tdee-hidden'); resultsDiv.classList.remove('tdee-hidden'); setTimeout(() => { resultsDiv.classList.add('tdee-show'); }, 50); }); showStep(stepOrder[0]); // Загрузчик Chart.js полностью удален });