addsub / index.html
ffonavia's picture
Add 2 files
a06186a verified
<!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>