|
<!DOCTYPE html> |
|
<html lang="ru"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Визуализация сложения и вычитания чисел</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
|
<style> |
|
.number-line { |
|
position: relative; |
|
height: 60px; |
|
margin: 40px 0; |
|
border-top: 2px solid #4b5563; |
|
} |
|
.number-tick { |
|
position: absolute; |
|
top: 0; |
|
width: 1px; |
|
height: 10px; |
|
background-color: #4b5563; |
|
transform: translateX(-50%); |
|
} |
|
.number-label { |
|
position: absolute; |
|
top: 15px; |
|
transform: translateX(-50%); |
|
font-size: 12px; |
|
color: #4b5563; |
|
} |
|
.operation-arrow { |
|
position: absolute; |
|
height: 2px; |
|
background-color: #3b82f6; |
|
top: 30px; |
|
} |
|
.operation-arrow::after { |
|
content: ''; |
|
position: absolute; |
|
right: -6px; |
|
top: -4px; |
|
width: 0; |
|
height: 0; |
|
border-left: 6px solid #3b82f6; |
|
border-top: 6px solid transparent; |
|
border-bottom: 6px solid transparent; |
|
} |
|
.negative-arrow { |
|
background-color: #ef4444; |
|
} |
|
.negative-arrow::after { |
|
border-left-color: #ef4444; |
|
} |
|
.start-point { |
|
position: absolute; |
|
width: 10px; |
|
height: 10px; |
|
border-radius: 50%; |
|
background-color: #10b981; |
|
top: 26px; |
|
transform: translateX(-50%); |
|
} |
|
.result-point { |
|
position: absolute; |
|
width: 10px; |
|
height: 10px; |
|
border-radius: 50%; |
|
background-color: #8b5cf6; |
|
top: 26px; |
|
transform: translateX(-50%); |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50 min-h-screen"> |
|
<div class="container mx-auto px-4 py-8"> |
|
<h1 class="text-3xl font-bold text-center text-gray-800 mb-6">Визуализация сложения и вычитания чисел</h1> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8"> |
|
<div class="flex flex-col md:flex-row gap-6 mb-6"> |
|
<div class="flex-1"> |
|
<label class="block text-sm font-medium text-gray-700 mb-1">Первое число</label> |
|
<div class="flex"> |
|
<select id="firstSign" class="bg-gray-100 border border-gray-300 text-gray-700 py-2 px-3 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
<option value="+">+</option> |
|
<option value="-">-</option> |
|
</select> |
|
<input type="number" id="firstNumber" value="5" class="flex-1 min-w-0 block w-full px-3 py-2 rounded-r-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
</div> |
|
</div> |
|
|
|
<div class="flex-1"> |
|
<label class="block text-sm font-medium text-gray-700 mb-1">Операция</label> |
|
<select id="operation" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
<option value="+">Сложение (+)</option> |
|
<option value="-">Вычитание (-)</option> |
|
</select> |
|
</div> |
|
|
|
<div class="flex-1"> |
|
<label class="block text-sm font-medium text-gray-700 mb-1">Второе число</label> |
|
<div class="flex"> |
|
<select id="secondSign" class="bg-gray-100 border border-gray-300 text-gray-700 py-2 px-3 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
<option value="+">+</option> |
|
<option value="-">-</option> |
|
</select> |
|
<input type="number" id="secondNumber" value="3" class="flex-1 min-w-0 block w-full px-3 py-2 rounded-r-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<button id="calculateBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out transform hover:scale-105"> |
|
Показать на числовой оси |
|
</button> |
|
</div> |
|
|
|
<div id="resultSection" class="bg-white rounded-lg shadow-md p-6 mb-8 hidden"> |
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">Результат:</h2> |
|
<div class="mb-4"> |
|
<span id="calculationText" class="text-lg font-medium"></span> |
|
<span id="resultText" class="text-2xl font-bold text-purple-600 ml-2"></span> |
|
</div> |
|
|
|
<div class="number-line-container overflow-x-auto py-4"> |
|
<div id="numberLine" class="number-line" style="width: 100%; min-width: 600px;"></div> |
|
</div> |
|
|
|
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div> |
|
<h3 class="text-lg font-medium text-gray-800 mb-2">Пошаговое объяснение:</h3> |
|
<div id="explanation" class="prose prose-sm max-w-none text-gray-700"></div> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-lg font-medium text-gray-800 mb-2">Графическое представление:</h3> |
|
<canvas id="numberChart" height="200"></canvas> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6"> |
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">Практические примеры</h2> |
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="5" data-op="+" data-second="3"> |
|
5 + 3 |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="5" data-op="+" data-second="-3"> |
|
5 + (-3) |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="-5" data-op="+" data-second="3"> |
|
-5 + 3 |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="-5" data-op="+" data-second="-3"> |
|
-5 + (-3) |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="5" data-op="-" data-second="3"> |
|
5 - 3 |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="5" data-op="-" data-second="-3"> |
|
5 - (-3) |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="-5" data-op="-" data-second="3"> |
|
-5 - 3 |
|
</button> |
|
<button class="example-btn bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition" data-first="-5" data-op="-" data-second="-3"> |
|
-5 - (-3) |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
const calculateBtn = document.getElementById('calculateBtn'); |
|
const firstSign = document.getElementById('firstSign'); |
|
const firstNumber = document.getElementById('firstNumber'); |
|
const operation = document.getElementById('operation'); |
|
const secondSign = document.getElementById('secondSign'); |
|
const secondNumber = document.getElementById('secondNumber'); |
|
const resultSection = document.getElementById('resultSection'); |
|
const calculationText = document.getElementById('calculationText'); |
|
const resultText = document.getElementById('resultText'); |
|
const numberLine = document.getElementById('numberLine'); |
|
const explanation = document.getElementById('explanation'); |
|
const exampleBtns = document.querySelectorAll('.example-btn'); |
|
let numberChart = null; |
|
|
|
|
|
calculateBtn.addEventListener('click', visualizeCalculation); |
|
|
|
|
|
exampleBtns.forEach(btn => { |
|
btn.addEventListener('click', function() { |
|
const first = this.getAttribute('data-first'); |
|
const op = this.getAttribute('data-op'); |
|
const second = this.getAttribute('data-second'); |
|
|
|
|
|
firstNumber.value = Math.abs(first); |
|
firstSign.value = first.startsWith('-') ? '-' : '+'; |
|
operation.value = op; |
|
secondNumber.value = Math.abs(second); |
|
secondSign.value = second.startsWith('-') ? '-' : '+'; |
|
|
|
|
|
visualizeCalculation(); |
|
}); |
|
}); |
|
|
|
function visualizeCalculation() { |
|
|
|
const firstNum = parseInt(firstNumber.value) * (firstSign.value === '-' ? -1 : 1); |
|
const op = operation.value; |
|
const secondNum = parseInt(secondNumber.value) * (secondSign.value === '-' ? -1 : 1); |
|
|
|
|
|
let result; |
|
if (op === '+') { |
|
result = firstNum + secondNum; |
|
} else { |
|
result = firstNum - secondNum; |
|
} |
|
|
|
|
|
calculationText.textContent = `${firstNum} ${op} ${secondNum} =`; |
|
resultText.textContent = result; |
|
|
|
|
|
resultSection.classList.remove('hidden'); |
|
|
|
|
|
buildNumberLine(firstNum, op, secondNum, result); |
|
|
|
|
|
generateExplanation(firstNum, op, secondNum, result); |
|
|
|
|
|
buildChart(firstNum, op, secondNum, result); |
|
} |
|
|
|
function buildNumberLine(firstNum, op, secondNum, result) { |
|
|
|
numberLine.innerHTML = ''; |
|
|
|
|
|
const min = Math.min(firstNum, result, 0) - 2; |
|
const max = Math.max(firstNum, result, 0) + 2; |
|
const range = max - min; |
|
const widthPerUnit = 600 / range; |
|
|
|
|
|
numberLine.style.width = `${range * widthPerUnit}px`; |
|
|
|
|
|
for (let i = min; i <= max; i++) { |
|
const tick = document.createElement('div'); |
|
tick.className = 'number-tick'; |
|
tick.style.left = `${((i - min) * widthPerUnit)}px`; |
|
numberLine.appendChild(tick); |
|
|
|
const label = document.createElement('div'); |
|
label.className = 'number-label'; |
|
label.textContent = i; |
|
label.style.left = `${((i - min) * widthPerUnit)}px`; |
|
numberLine.appendChild(label); |
|
} |
|
|
|
|
|
const startPoint = document.createElement('div'); |
|
startPoint.className = 'start-point'; |
|
startPoint.style.left = `${((firstNum - min) * widthPerUnit)}px`; |
|
numberLine.appendChild(startPoint); |
|
|
|
|
|
const arrow = document.createElement('div'); |
|
arrow.className = 'operation-arrow'; |
|
|
|
let arrowLength, arrowLeft; |
|
if (op === '+') { |
|
|
|
arrowLength = secondNum * widthPerUnit; |
|
arrowLeft = (firstNum - min) * widthPerUnit; |
|
|
|
if (secondNum < 0) { |
|
arrow.classList.add('negative-arrow'); |
|
arrowLeft += arrowLength; |
|
} |
|
} else { |
|
|
|
arrowLength = -secondNum * widthPerUnit; |
|
arrowLeft = (firstNum - min) * widthPerUnit; |
|
|
|
if (secondNum > 0) { |
|
arrow.classList.add('negative-arrow'); |
|
arrowLeft += arrowLength; |
|
} |
|
} |
|
|
|
arrow.style.width = `${Math.abs(arrowLength)}px`; |
|
arrow.style.left = `${arrowLeft}px`; |
|
numberLine.appendChild(arrow); |
|
|
|
|
|
const resultPoint = document.createElement('div'); |
|
resultPoint.className = 'result-point'; |
|
resultPoint.style.left = `${((result - min) * widthPerUnit)}px`; |
|
numberLine.appendChild(resultPoint); |
|
} |
|
|
|
function generateExplanation(firstNum, op, secondNum, result) { |
|
let explanationText = ''; |
|
|
|
if (op === '+') { |
|
explanationText = ` |
|
<p><strong>Шаг 1:</strong> Начинаем с числа ${firstNum} на числовой оси.</p> |
|
<p><strong>Шаг 2:</strong> Выполняем сложение с числом ${secondNum}.</p> |
|
${secondNum >= 0 ? |
|
`<p>Так как второе число положительное, двигаемся вправо на ${secondNum} единиц.</p>` : |
|
`<p>Так как второе число отрицательное, двигаемся влево на ${Math.abs(secondNum)} единиц.</p>` |
|
} |
|
<p><strong>Шаг 3:</strong> Получаем результат: ${firstNum} ${op} ${secondNum} = ${result}.</p> |
|
`; |
|
} else { |
|
explanationText = ` |
|
<p><strong>Шаг 1:</strong> Начинаем с числа ${firstNum} на числовой оси.</p> |
|
<p><strong>Шаг 2:</strong> Выполняем вычитание числа ${secondNum}.</p> |
|
<p><strong>Шаг 2.1:</strong> Вычитание числа ${secondNum} эквивалентно сложению с числом ${-secondNum}.</p> |
|
${-secondNum >= 0 ? |
|
`<p>Так как ${-secondNum} положительное, двигаемся вправо на ${-secondNum} единиц.</p>` : |
|
`<p>Так как ${-secondNum} отрицательное, двигаемся влево на ${Math.abs(secondNum)} единиц.</p>` |
|
} |
|
<p><strong>Шаг 3:</strong> Получаем результат: ${firstNum} ${op} ${secondNum} = ${result}.</p> |
|
`; |
|
} |
|
|
|
explanation.innerHTML = explanationText; |
|
} |
|
|
|
function buildChart(firstNum, op, secondNum, result) { |
|
const ctx = document.getElementById('numberChart').getContext('2d'); |
|
|
|
|
|
if (numberChart) { |
|
numberChart.destroy(); |
|
} |
|
|
|
|
|
numberChart = new Chart(ctx, { |
|
type: 'bar', |
|
data: { |
|
labels: ['Первое число', 'Второе число', 'Результат'], |
|
datasets: [{ |
|
label: 'Значения', |
|
data: [firstNum, op === '+' ? secondNum : -secondNum, result], |
|
backgroundColor: [ |
|
'rgba(16, 185, 129, 0.7)', |
|
'rgba(59, 130, 246, 0.7)', |
|
'rgba(139, 92, 246, 0.7)' |
|
], |
|
borderColor: [ |
|
'rgba(16, 185, 129, 1)', |
|
'rgba(59, 130, 246, 1)', |
|
'rgba(139, 92, 246, 1)' |
|
], |
|
borderWidth: 1 |
|
}] |
|
}, |
|
options: { |
|
responsive: true, |
|
scales: { |
|
y: { |
|
beginAtZero: false, |
|
title: { |
|
display: true, |
|
text: 'Значение' |
|
} |
|
} |
|
}, |
|
plugins: { |
|
tooltip: { |
|
callbacks: { |
|
label: function(context) { |
|
return `${context.dataset.label}: ${context.raw}`; |
|
} |
|
} |
|
}, |
|
legend: { |
|
display: false |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
}); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ffonavia/sub1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |