От движущихся частиц к управляемой модели
К концу занятия модель будет обнаруживать сближение частиц, отмечать столкновения и реагировать на команды пользователя.
Основные результаты
- повторить изученные средства HTML, CSS и JavaScript;
- освоить новые элементы интерфейса;
- перебрать все пары частиц;
- определить столкновение по расстоянию между центрами;
- назначить обработчики событий через
addEventListener.
Красный цвет показывает, что частица соприкасается хотя бы с одной другой частицей.
Структура и элементы страницы
HTML (HyperText Markup Language — язык гипертекстовой разметки) описывает структуру и содержание документа. Теги обозначают элементы страницы, а атрибуты уточняют их свойства и связи.
| Конструкция | Назначение | Пример |
|---|---|---|
<!doctype html> | Указывает современный режим обработки HTML. | <!doctype html> |
<html> | Содержит весь HTML-документ. | <html lang="ru"> |
<head> | Содержит служебные сведения и подключения файлов. | <title>Проект</title> |
<body> | Содержит видимую часть страницы. | <body>...</body> |
<h1>–<h3> | Создают заголовки разных уровней. | <h1>Модель частиц</h1> |
<p>, <div> | Создают абзац и универсальный контейнер. | <p>Описание</p> |
<button> | Создаёт кнопку. | <button id="start">Пуск</button> |
<canvas> | Создаёт область для программного рисования. | <canvas width="600" height="300"> |
<link> | Подключает внешний файл CSS. | <link rel="stylesheet" href="style.css"> |
<script> | Подключает или содержит JavaScript. | <script src="script.js" defer></script> |
id, class | Задают уникальное имя и имя группы элементов. | id="scene" class="panel" |
| Комментарий | Оставляет пояснение, которое не отображается на странице. | <!-- панель управления --> |
id должно быть уникальным на странице. Одно значение class можно назначить нескольким элементам.Оформление элементов
CSS (Cascading Style Sheets — каскадные таблицы стилей) описывает представление документа. Правило CSS состоит из селектора и блока объявлений. Каждое объявление содержит свойство и его значение.
Селекторы
| Запись | Что выбирает |
|---|---|
button | Все элементы <button>. |
.panel | Все элементы класса panel. |
#scene | Элемент с идентификатором scene. |
button:hover | Кнопку, на которую наведён указатель. |
Общий вид правила
.panel {
background-color: white;
padding: 12px;
border: 1px solid green;
}
.panel — селектор; padding — свойство; 12px — значение; вся строка padding: 12px; — объявление.
| Свойство | Назначение | Пример |
|---|---|---|
color | Цвет текста. | color: white; |
background-color | Цвет фона. | background-color: #123d32; |
font-family, font-size | Гарнитура и размер шрифта. | font-size: 18px; |
text-align | Выравнивание текста. | text-align: center; |
width, height | Ширина и высота элемента. | width: 300px; |
padding | Внутренний отступ. | padding: 12px; |
margin | Внешний отступ. | margin: 10px; |
border | Рамка элемента. | border: 1px solid green; |
border-radius | Скругление углов. | border-radius: 8px; |
/* пояснение */. Комментарий помогает описать назначение правила и не влияет на оформление.Команды, данные и рисование
JavaScript — язык программирования, который задаёт поведение страницы: выполняет вычисления, изменяет элементы, обрабатывает события и управляет графикой Canvas.
Страница и функции
| Конструкция | Назначение |
|---|---|
document.getElementById("id") | Находит элемент по идентификатору. |
element.textContent = "..." | Изменяет текст элемента. |
element.style.backgroundColor | Изменяет цвет фона через JavaScript. |
function draw() { ... } | Объявляет функцию. |
draw() | Вызывает функцию. |
let, const | Объявляют изменяемую переменную и постоянную ссылку. |
Массивы, объекты и циклы
| Конструкция | Назначение |
|---|---|
[10, 20, 30] | Создаёт массив. |
{x: 10, y: 20} | Создаёт объект со свойствами. |
array[0], p.x | Читают элемент массива и свойство объекта. |
array.push(value) | Добавляет значение в конец массива. |
array.length | Возвращает количество элементов. |
for (...) | Повторяет команды, пока условие истинно. |
array.forEach(p => {...}) | Выполняет функцию для каждого элемента массива. |
Движение и условия
| Конструкция | Назначение |
|---|---|
Math.random() | Возвращает случайное число от 0 включительно до 1 не включительно. |
setInterval(tick, 30) | Повторяет вызов функции через заданный интервал. |
p.x += p.vx | Увеличивает координату на величину скорости. |
if (условие) {...} | Выполняет блок команд только при истинном условии. |
< > <= >= === | Сравнивают значения. |
a || b | Истинно, если истинно хотя бы одно условие. |
Canvas
| Команда | Назначение |
|---|---|
canvas.getContext("2d") | Получает двумерный контекст рисования. |
ctx.clearRect(...) | Очищает прямоугольную область. |
ctx.beginPath() | Начинает новый контур. |
ctx.arc(x, y, r, 0, Math.PI * 2) | Добавляет окружность к текущему контуру. |
ctx.fill() | Закрашивает текущий контур. |
Элементы управления и смысловые разделы
Новые элементы сразу используются в панели управления моделью. Они уточняют назначение частей документа и делают интерфейс понятнее.
<section>
Объединяет тематически связанную часть документа. Раздел обычно имеет собственный заголовок.
<section>
<h2>Управление</h2>
...
</section><label>
Создаёт подпись элемента управления. Атрибут for содержит id связанного элемента.
<label for="speed">Скорость</label><input type="range">
Создаёт ползунок. Атрибуты min, max, step и value задают допустимые значения.
<input id="speed" type="range"
min="1" max="5" step="1" value="2"><output>
Предназначен для вывода результата вычисления или текущего значения элемента управления.
<output id="speedValue"
for="speed">2</output>for="speed" и id="speed" должны совпадать. При выборе подписи браузер передаёт фокус связанному элементу управления.Компоновка и состояния интерфейса
display: flex
Превращает элемент в flex-контейнер. Его непосредственные дочерние элементы располагаются вдоль основной оси.
gap
Задаёт промежуток между элементами flex-контейнера без отдельных внешних отступов у каждого элемента.
:hover и transition
Псевдокласс :hover выбирает элемент при наведении указателя, а transition задаёт плавность изменения указанных свойств.
Пользовательские свойства
Запись --accent: blue; создаёт пользовательское свойство, а var(--accent) получает его значение.
.controls {
display: flex;
gap: 10px;
align-items: center;
}
.chip {
--accent: #165d8d;
border-color: var(--accent);
transition: 0.2s;
}
.chip:hover {
color: white;
background: var(--accent);
}
HTML и CSS
от распознавания к записиЗадания используют изученные темы, но имеют новые формулировки и примеры.
display: flex; gap: 12px; при стандартном направлении?id="speed".Массивы и циклы
анализ кодаparticles[2].vx?for (let j = ______; j < particles.length; j++)Скорость и условия
применениеx = 70, а скорость vx = -2. Чему станет равна координата после команды x += vx?Проверьте основу модели
Перед добавлением столкновений модель должна выполнять три действия: изменять координаты, отражать частицы от четырёх границ и заново рисовать сцену.
function tick() {
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
if (p.x < p.r ||
p.x > canvas.width - p.r) {
p.vx = -p.vx;
}
if (p.y < p.r ||
p.y > canvas.height - p.r) {
p.vy = -p.vy;
}
});
draw();
}
Проверка
Как перебрать все пары частиц
Один цикл выбирает первую частицу пары. Второй цикл выбирает вторую частицу из элементов, расположенных правее в массиве. Поэтому частица не сравнивается с собой, а одна и та же пара не проверяется дважды.
for (let i = 0;
i < particles.length;
i++) {
for (let j = i + 1;
j < particles.length;
j++) {
const a = particles[i];
const b = particles[j];
// здесь проверяем пару a и b
}
}
Пример для четырёх частиц
Индексы частиц: 0, 1, 2, 3.
Проверяемые пары:
(0,1), (0,2), (0,3), (1,2), (1,3), (2,3).
Всего получается шесть различных пар.
j = i + 1? Значение j = i сравнило бы частицу с самой собой. Значение меньше i повторило бы уже проверенную пару в обратном порядке.Расстояние между центрами
Два круга соприкасаются или пересекаются, если расстояние между их центрами не превышает сумму радиусов.
Алгоритм
- Найдите разности координат:
dxиdy. - Вычислите расстояние по теореме Пифагора.
- Сравните расстояние с суммой радиусов.
function distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
function isColliding(a, b) {
return distance(a, b) <= a.r + b.r;
}
| Расстояние | Сумма радиусов | Вывод |
|---|---|---|
| 30 | 20 | Между кругами есть промежуток. |
| 20 | 20 | Круги соприкасаются. |
| 16 | 20 | Круги пересекаются. |
Math.sqrt(value) вычисляет квадратный корень. Выражение dx * dx + dy * dy соответствует сумме квадратов катетов.Отметьте столкнувшиеся частицы
Сначала реализуйте надёжное обнаружение столкновения. Физически точный расчёт нового движения требует дополнительных формул, поэтому в базовой версии столкновение обозначается изменением цвета.
function findCollisions() {
particles.forEach(p => {
p.hit = false;
});
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1;
j < particles.length; j++) {
const a = particles[i];
const b = particles[j];
if (isColliding(a, b)) {
a.hit = true;
b.hit = true;
}
}
}
}
function drawParticle(p) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.r,
0, Math.PI * 2);
if (p.hit) {
ctx.fillStyle = "#c8413a";
} else {
ctx.fillStyle = "#1d5a49";
}
ctx.fill();
}
Дополнительная задача: упрощённое изменение движения
Можно поменять направления движения обеих частиц:
if (isColliding(a, b)) {
a.vx = -a.vx;
a.vy = -a.vy;
b.vx = -b.vx;
b.vy = -b.vy;
}Это учебное упрощение, а не точная физическая модель столкновения. Оно особенно заметно при касательных столкновениях.
Обработчики событий через addEventListener
Событие сообщает программе, что произошло определённое действие: пользователь нажал кнопку, переместил ползунок или изменил значение поля. Метод addEventListener назначает функцию, которая будет вызвана при наступлении выбранного события.
Щелчок по кнопке
const pauseButton =
document.getElementById("pause");
pauseButton.addEventListener(
"click",
function () {
running = !running;
}
);Изменение ползунка
const speedInput =
document.getElementById("speed");
speedInput.addEventListener(
"input",
function () {
speed = Number(speedInput.value);
}
);| Часть записи | Смысл |
|---|---|
pauseButton | Элемент, за событиями которого наблюдает программа. |
"click" | Тип события. |
function () {...} | Обработчик — функция, выполняемая после события. |
"input" | Событие изменения значения во время перемещения ползунка. |
Добавьте управление моделью
HTML
<section class="controls">
<button id="pause">Пауза</button>
<label for="speed">Скорость</label>
<input id="speed" type="range"
min="1" max="5" value="2">
<output id="speedValue"
for="speed">2</output>
</section>CSS
.controls {
display: flex;
gap: 10px;
align-items: center;
}
button {
transition: background-color 0.2s;
}
button:hover {
background-color: #1d5a49;
}let running = true;
let speed = 2;
pauseButton.addEventListener("click", function () {
running = !running;
pauseButton.textContent = running ? "Пауза" : "Пуск";
});
speedInput.addEventListener("input", function () {
speed = Number(speedInput.value);
speedValue.textContent = speed;
});
function tick() {
if (!running) return;
// изменение координат с учётом speed
}
! означает логическое отрицание. Если running равно false, выражение !running истинно, и команда return завершает текущий вызов функции.Выберите одно расширение модели
Сначала сохраните работающую базовую версию проекта. Затем создайте копию файла и добавьте одно расширение.
Базовые варианты
- изменение количества частиц;
- случайный цвет каждой частицы;
- кнопка полного сброса модели;
- переключатель показа столкновений.
Повышенная сложность
- разные радиусы частиц;
- счётчик столкновений;
- упрощённое изменение скорости при столкновении;
- собственный вариант управления.