step
This commit is contained in:
parent
390ee703a3
commit
866e0c05b7
@ -4,28 +4,39 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
const launchButton = document.getElementById('launchButton');
|
const launchButton = document.getElementById('launchButton');
|
||||||
const velocityInput = document.getElementById('velocity');
|
const accelerationInput = document.getElementById('acceleration');
|
||||||
|
const burnTimeInput = document.getElementById('burnTime');
|
||||||
|
const angleInput = document.getElementById('angle');
|
||||||
const resultMessage = document.getElementById('result-message');
|
const resultMessage = document.getElementById('result-message');
|
||||||
|
|
||||||
// --- Параметры симуляции ---
|
// --- Параметры симуляции ---
|
||||||
const GRAVITY = 9.8; // Ускорение свободного падения (м/с^2)
|
const GRAVITY = 9.8;
|
||||||
const ESCAPE_VELOCITY = 11200; // Вторая космическая скорость (м/с)
|
const PIXELS_PER_METER = 0.01;
|
||||||
const PIXELS_PER_METER = 0.005; // Масштаб: сколько пикселей в одном метре
|
const TIME_STEP = 1.0;
|
||||||
const TIME_STEP = 2.0; // Шаг времени для симуляции (влияет на скорость анимации)
|
|
||||||
|
|
||||||
let rocket = {};
|
let rocket = {};
|
||||||
let animationFrameId;
|
let simulationState = {};
|
||||||
let initialVelocity = 0;
|
|
||||||
|
function initializeState() {
|
||||||
|
return {
|
||||||
|
animationFrameId: null,
|
||||||
|
elapsedTime: 0,
|
||||||
|
acceleration: 0,
|
||||||
|
burnTime: 0,
|
||||||
|
angleRad: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function initializeRocket() {
|
function initializeRocket() {
|
||||||
return {
|
return {
|
||||||
x: canvas.width / 2,
|
x: 30,
|
||||||
y: canvas.height - 45, // Позиция на холсте (пиксели)
|
y: canvas.height - 45,
|
||||||
width: 10,
|
width: 10,
|
||||||
height: 25,
|
height: 25,
|
||||||
// Физические свойства
|
x_meters: 0,
|
||||||
y_meters: 0, // Высота в метрах
|
y_meters: 0,
|
||||||
velocityY_meters_per_sec: 0, // Скорость в м/с
|
vx: 0, // Скорость по X в м/с
|
||||||
|
vy: 0, // Скорость по Y в м/с
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,21 +48,48 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawRocket() {
|
function drawRocket() {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(rocket.x, rocket.y);
|
||||||
|
|
||||||
|
// Угол поворота ракеты зависит от вектора скорости
|
||||||
|
// Но до старта (скорость 0) она стоит вертикально
|
||||||
|
const angle = (rocket.vx === 0 && rocket.vy === 0)
|
||||||
|
? -Math.PI / 2
|
||||||
|
: Math.atan2(rocket.vy, rocket.vx);
|
||||||
|
|
||||||
|
// Поворачиваем ракету. Наша модель "смотрит" вправо, поэтому доп. вращение не нужно.
|
||||||
|
ctx.rotate(angle);
|
||||||
|
|
||||||
|
// Рисуем пламя, если двигатель работает
|
||||||
|
if (simulationState.elapsedTime > 0 && simulationState.elapsedTime <= simulationState.burnTime) {
|
||||||
|
ctx.fillStyle = `rgba(255, ${Math.random() * 150 + 100}, 0, 0.8)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
const flameLength = rocket.height * (1.5 + Math.random() * 0.5);
|
||||||
|
ctx.moveTo(-rocket.width / 2, 0);
|
||||||
|
ctx.lineTo(-rocket.width / 2 - flameLength, rocket.width / 2);
|
||||||
|
ctx.lineTo(-rocket.width / 2 - flameLength, -rocket.width / 2);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Корпус ракеты (перерисовываем, чтобы был поверх пламени)
|
||||||
|
// Модель ракеты теперь "смотрит" вправо (по оси X)
|
||||||
ctx.fillStyle = '#d0d0d0';
|
ctx.fillStyle = '#d0d0d0';
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(rocket.x, rocket.y - rocket.height / 2);
|
ctx.moveTo(rocket.height / 2, 0);
|
||||||
ctx.lineTo(rocket.x - rocket.width / 2, rocket.y + rocket.height / 2);
|
ctx.lineTo(-rocket.height / 2, -rocket.width / 2);
|
||||||
ctx.lineTo(rocket.x + rocket.width / 2, rocket.y + rocket.height / 2);
|
ctx.lineTo(-rocket.height / 2, rocket.width / 2);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetSimulation() {
|
function resetSimulation() {
|
||||||
if (animationFrameId) {
|
if (simulationState.animationFrameId) {
|
||||||
cancelAnimationFrame(animationFrameId);
|
cancelAnimationFrame(simulationState.animationFrameId);
|
||||||
animationFrameId = null;
|
|
||||||
}
|
}
|
||||||
initialVelocity = 0;
|
simulationState = initializeState();
|
||||||
rocket = initializeRocket();
|
rocket = initializeRocket();
|
||||||
resultMessage.innerHTML = ' ';
|
resultMessage.innerHTML = ' ';
|
||||||
resultMessage.className = 'alert alert-secondary';
|
resultMessage.className = 'alert alert-secondary';
|
||||||
@ -65,14 +103,27 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
// 1. Обновляем физику (пошаговая симуляция)
|
simulationState.elapsedTime += TIME_STEP;
|
||||||
// Уменьшаем скорость под действием гравитации
|
|
||||||
rocket.velocityY_meters_per_sec -= GRAVITY * TIME_STEP;
|
|
||||||
// Обновляем высоту на основе новой скорости
|
|
||||||
rocket.y_meters += rocket.velocityY_meters_per_sec * TIME_STEP;
|
|
||||||
|
|
||||||
// 2. Обновляем позицию для отрисовки
|
// 1. Обновляем физику
|
||||||
|
// Если двигатель работает, применяем ускорение
|
||||||
|
if (simulationState.elapsedTime <= simulationState.burnTime) {
|
||||||
|
const currentAccelerationX = simulationState.acceleration * Math.cos(simulationState.angleRad);
|
||||||
|
const currentAccelerationY = simulationState.acceleration * Math.sin(simulationState.angleRad);
|
||||||
|
rocket.vx += currentAccelerationX * TIME_STEP;
|
||||||
|
rocket.vy += currentAccelerationY * TIME_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Всегда применяем гравитацию (она действует на ось Y)
|
||||||
|
rocket.vy -= GRAVITY * TIME_STEP;
|
||||||
|
|
||||||
|
// Обновляем позицию в метрах
|
||||||
|
rocket.x_meters += rocket.vx * TIME_STEP;
|
||||||
|
rocket.y_meters += rocket.vy * TIME_STEP;
|
||||||
|
|
||||||
|
// 2. Обновляем позицию для отрисовки в пикселях
|
||||||
const groundY = canvas.height - 45;
|
const groundY = canvas.height - 45;
|
||||||
|
rocket.x = 30 + (rocket.x_meters * PIXELS_PER_METER);
|
||||||
rocket.y = groundY - (rocket.y_meters * PIXELS_PER_METER);
|
rocket.y = groundY - (rocket.y_meters * PIXELS_PER_METER);
|
||||||
|
|
||||||
// 3. Отрисовываем сцену
|
// 3. Отрисовываем сцену
|
||||||
@ -81,62 +132,53 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
drawRocket();
|
drawRocket();
|
||||||
|
|
||||||
// 4. Проверяем условия завершения
|
// 4. Проверяем условия завершения
|
||||||
const hasEscaped = initialVelocity >= ESCAPE_VELOCITY && rocket.y < -rocket.height;
|
const hasCrashed = rocket.y_meters < 0 && simulationState.elapsedTime > 1;
|
||||||
const hasCrashed = rocket.y_meters <= 0 && initialVelocity < ESCAPE_VELOCITY;
|
const outOfBounds = rocket.x > canvas.width + rocket.height;
|
||||||
|
|
||||||
if (hasEscaped) {
|
if (hasCrashed || outOfBounds) {
|
||||||
cancelAnimationFrame(animationFrameId);
|
|
||||||
animationFrameId = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasCrashed) {
|
|
||||||
// Фиксируем ракету на земле
|
|
||||||
rocket.y_meters = 0;
|
rocket.y_meters = 0;
|
||||||
rocket.y = groundY;
|
rocket.y = groundY;
|
||||||
clearCanvas();
|
clearCanvas();
|
||||||
drawEarth();
|
drawEarth();
|
||||||
drawRocket();
|
drawRocket();
|
||||||
|
|
||||||
resultMessage.innerHTML = "<strong>Неудача.</strong> Ракета упала обратно на Землю.";
|
resultMessage.innerHTML = outOfBounds
|
||||||
|
? "<strong>Неудача.</strong> Ракета улетела за пределы видимости."
|
||||||
|
: "<strong>Неудача.</strong> Ракета упала обратно на Землю.";
|
||||||
resultMessage.className = 'alert alert-danger';
|
resultMessage.className = 'alert alert-danger';
|
||||||
|
|
||||||
cancelAnimationFrame(animationFrameId);
|
cancelAnimationFrame(simulationState.animationFrameId);
|
||||||
animationFrameId = null;
|
simulationState.animationFrameId = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Продолжаем анимацию
|
simulationState.animationFrameId = requestAnimationFrame(animate);
|
||||||
animationFrameId = requestAnimationFrame(animate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
launchButton.addEventListener('click', () => {
|
launchButton.addEventListener('click', () => {
|
||||||
const velocityValue = parseFloat(velocityInput.value);
|
const accelValue = parseFloat(accelerationInput.value);
|
||||||
|
const burnTimeValue = parseFloat(burnTimeInput.value);
|
||||||
|
const angleValue = parseFloat(angleInput.value);
|
||||||
|
|
||||||
if (isNaN(velocityValue) || velocityValue < 0) {
|
if (isNaN(accelValue) || isNaN(burnTimeValue) || isNaN(angleValue) || accelValue <= 0 || burnTimeValue <= 0 || angleValue < 0 || angleValue > 90) {
|
||||||
resultMessage.innerHTML = 'Пожалуйста, введите положительное число.';
|
resultMessage.innerHTML = 'Пожалуйста, введите корректные значения (ускорение > 0, время > 0, угол 0-90).';
|
||||||
resultMessage.className = 'alert alert-warning';
|
resultMessage.className = 'alert alert-warning';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSimulation();
|
resetSimulation();
|
||||||
|
|
||||||
initialVelocity = velocityValue;
|
simulationState.acceleration = accelValue;
|
||||||
rocket.velocityY_meters_per_sec = initialVelocity;
|
simulationState.burnTime = burnTimeValue;
|
||||||
|
simulationState.angleRad = angleValue * (Math.PI / 180);
|
||||||
|
|
||||||
if (initialVelocity >= ESCAPE_VELOCITY) {
|
resultMessage.innerHTML = "Запуск...";
|
||||||
resultMessage.innerHTML = "<strong>Успешный запуск!</strong> Ракета улетит в космос.";
|
resultMessage.className = 'alert alert-info';
|
||||||
resultMessage.className = 'alert alert-success';
|
|
||||||
} else {
|
|
||||||
resultMessage.innerHTML = "Запуск... Ракета должна упасть.";
|
|
||||||
resultMessage.className = 'alert alert-info';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!animationFrameId) {
|
if (!simulationState.animationFrameId) {
|
||||||
animationFrameId = requestAnimationFrame(animate);
|
simulationState.animationFrameId = requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Начальная отрисовка
|
|
||||||
resetSimulation();
|
resetSimulation();
|
||||||
});
|
});
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="text-center section-title">Симулятор Запуска Ракеты</h1>
|
<h1 class="text-center section-title">Симулятор Запуска Ракеты</h1>
|
||||||
<p class="text-center mb-4">Введите начальную скорость ракеты в метрах в секунду (м/с).<br>Чтобы преодолеть притяжение Земли, нужна скорость не менее 11 200 м/с.</p>
|
<p class="text-center mb-4">Задайте параметры запуска: ускорение, время работы двигателя и угол.<br>Наблюдайте, как ракета набирает скорость и летит по траектории.</p>
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-lg-5">
|
<div class="col-md-6 col-lg-5">
|
||||||
@ -60,8 +60,16 @@
|
|||||||
<!-- Форма теперь без перезагрузки страницы -->
|
<!-- Форма теперь без перезагрузки страницы -->
|
||||||
<form onsubmit="return false;">
|
<form onsubmit="return false;">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="velocity" class="form-label">Начальная скорость (м/с):</label>
|
<label for="acceleration" class="form-label">Ускорение (м/с²):</label>
|
||||||
<input type="number" class="form-control" id="velocity" name="velocity" placeholder="Например, 12000" value="10000" required>
|
<input type="number" class="form-control" id="acceleration" name="acceleration" placeholder="Например, 100" value="100" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="burnTime" class="form-label">Время работы двигателя (сек):</label>
|
||||||
|
<input type="number" class="form-control" id="burnTime" name="burnTime" placeholder="Например, 60" value="60" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="angle" class="form-label">Угол запуска (°):</label>
|
||||||
|
<input type="number" class="form-control" id="angle" name="angle" placeholder="от 0 до 90" value="90" min="0" max="90" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<!-- Кнопка теперь запускает JS-функцию -->
|
<!-- Кнопка теперь запускает JS-функцию -->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user