Программируем скринсейвер для Илона

Программируем скринсейвер для Илона

Анимация движения звёзд на JavaScript.

Попробуем новую для нас библиотеку в JavaScript — p5. Её используют, чтобы расширить стандартные возможности рисования в JavaScript и писать для этого более крутые скрипты. 

Сегодняшний проект — полёт в космосе к звёздам, как будто мы летим на ракете:

Попробуем новую для нас библиотеку в JavaScript — p5

Логика работы

Чтобы всё это нарисовать, нам нужно:

  1. Подготовить страницу так, чтобы содержимое занимало весь размер окна браузера.
  2. Запрограммировать общее поведение для звёзд — разлетаться к краю экрана, постепенно увеличиваясь в размере.
  3. Предусмотреть такое: если звезда улетела за границы окна — создать новую звезду с таким же поведением.
  4. Создать много звёзд с разными координатами.
  5. Запустить анимацию.

Большинство функций реализуем с помощью библиотеки p5 — в ней много готовых команд, которые упрощают разработку и отрисовку любых объектов.

Что за P5 и зачем она нужна

Библиотека P5.js — это продвинутая библиотека для рисования чего угодно в браузере. Она основана на фреймворке Processing — а он, в свою очередь, создавался, чтобы помочь художникам использовать для своих художественных дел инструменты программирования.

Короче: P5.js — это удобная рисовалка. Сейчас увидите. 

Подготовка

Используем для проекта стандартный HTML-шаблон. Единственное, что мы добавим нового в шаблон — пропишем название проекта.

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Полёт в космос</title>
 
  <style type="text/css">

  </style>

</head>
<body>

	<!-- основной скрипт -->
	<script type="text/javascript">
	
	</script>

</body>
</html>

Подготавливаем стили

Чтобы во всех браузерах наши звёзды выглядели одинаково, подключим нормализатор стилей:

  <link rel=»stylesheet» href=»https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css»>

Задача нормализатора — привести все стили по умолчанию во всех браузерах к одному общему виду.

Ещё нам нужно поменять настройку управления размерами во всех элементах на странице. Сделаем так, чтобы мы могли задавать размеры всех элементов в разных единицах одновременно, например, высоту в пикселях, а ширину в процентах. Это поможет нам гибко обрабатывать положения и размеры звёзд.

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

<style type="text/css">
  	*{
  		/* сделаем так, чтобы мы могли задавать размеры всех элементов в разных единицах одновременно, например, высоту в пикселях, а ширину в процентах */
		box-sizing: border-box;
	}
	html, body{
		/* убираем границы и отступы */
		margin: 0;
		padding: 0;
		/* разворачиваем космос на всю ширину окна браузера */
		width: 100%;
		height: 100vh;
		/* не отображаем звёзды, которые вылетели за пределы окна*/
		overflow: hidden;
	}
  </style>

Подключаем библиотеку p5 и создаём основной скрипт

Для подключения библиотеки просто добавляем её вызов в тело страницы:

<!— подключаем p5 — библиотеку для рисования в JavaScrpt —>

<script src=’https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js’></script>

После этого создаём основной скрипт, который будет отвечать за всё, что происходит на экране. Всё остальное будем писать внутри него.

<!— основной скрипт —>

<script type=»text/javascript»>

</script>

Программируем звёзды

Чтобы не описывать каждую звезду отдельно, а задать для них общее поведение, будем использовать классы и методы. Если вы не знаете, что это такое, — почитайте наш мини-цикл про объектно-ориентированное программирование

Вот что мы сделаем с помощью класса:

  1. Пропишем общую логику создания звезды — где именно она появится и как близко к виртуальной передней границе экрана. За это в классах отвечает конструктор — в нём пишутся команды, что именно нужно сделать при создании новой звезды.
  2. Запрограммируем, как будут меняться координаты звезды во время движения. Чтобы изменения произошли, нужно создать новый метод и вызвать его в нужный момент.
  3. Добавим возможность отрисовки звезды по текущим координатам. За это тоже будет отвечать свой метод.

Этими свойствами и возможностями будут обладать все звёзды на основе этого класса — в этом и смысл ООП. Даже если мы создадим миллион звёзд, нам не придётся прописывать логику каждой звезды — за нас это сделает класс.

// сколько звёзд может быть одновременно на экране
var starsCount = 800;
// на старте массив со звёздами будет пустой
var stars = [];
// класс, на основе которого будут сделаны все звёзды
class Star{
	// конструктор, который вызывается при создании каждого объекта на основе этого класса
	constructor(){
		// у новой звезды будут случайные координаты
		this.x = random(-width, width);
		this.y = random(-height, height);
		// глубина — насколько виртуально близко к экрану появится звезда
		this.z = random(width);
	}
	// метод, который обновляет координаты звезды
	update(){
		// скорость полёта
		var speed = 12;
		// приближаем звезду к краю экрана, уменьшая глубину на значение скорости
		this.z -= speed;
		// если звезда вылетела за край экрана — делаем из неё новую звезду, для этого меняем координаты
		if(this.z < 1){
			this.x = random(-width, width);
			this.y = random(-height, height);
			// для новой звезды вместо старой глубину появления теперь выбираем не случайным образом, а задаём прямо
			this.z = width;
		}
	}

	// метод, который отрисовывает звезду на экране
	drawStar(){
		// каждая звезда — белого цвета
		fill(255);
		// и без контура
		noStroke();

		// с помощью функции map() из библиотеки p5.js получаем новые координаты для отрисовки звезды 
		var sx = map(this.x / this.z, 0, 1, 0, width);
		var sy = map(this.y / this.z, 0, 1, 0, height);

		// чем ближе к краю экрана (чем меньше глубина z) — тем больше радиус 
		var r = map(this.z, 0, width, 10, 0);
		// рисуем звезду в новых координатах и новым размером
		ellipse(sx, sy, r, r);
	}
}

Готовимся к запуску

В библиотеке p5 есть специальная функция setup() — код, который в ней написан, выполнится сразу после загрузки страницы со скриптом. Сделаем такую функцию в нашем скрипте, чтобы в ней запрограммировать создание холста и всех звёзд.

// подготавливаем всё к запуску — то, что написано здесь выполнится автоматически сразу после загрузки
function setup(){
	// создаём холст, на котором будем рисовать
	createCanvas(innerWidth, innerHeight);
	// размещаем сразу все звёзды на холсте
	for (var i = 0; i < starsCount; i++) {
		// каждая новая звезда — объект класса Star и умеет то же самое, что и все остальные звёзды
		stars[i] = new Star();
	}
}

Рисуем и запускаем анимацию

Ещё одна специальная функция из той же библиотеки — draw(). Она отвечает за рисование и анимацию. Всё, что написано внутри этой функции, будет выполняться раз за разом, по кругу, пока мы не закроем страницу. Используем это, чтобы сделать анимацию для звёзд. 

👉 При каждом проходе draw() не очищает холст, нам нужно сделать это самостоятельно. Для этого будем просто закрашивать всё чёрным и рисовать звёзды заново.

/ пока мы не закроем страницу, постоянно будет выполняться функция drw() 
function draw(){
	// ставим чёрный фон и указываем скорость обновления фона — чем меньше второе число, тем больший шлейф будут оставлять звёзды
	background(0, 180);
	// формируем центр экрана, куда «полетим» сквозь звёзды
	translate(width/2, height/2);

	// отрисовываем каждый раз все звёзды и меняем их положение
	for (var i = 0; i < starsCount; i++) {
		stars[i].drawStar();
		stars[i].update();
	}
}

Следим за размерами экрана

Чтобы у нас полёт рисовался пропорционально экрану даже при изменении размера окна, добавим обработчик этого события. Он сработает при каждом изменении размеров окна и сразу изменит размер холста.

// если поменяется размер окна браузера — сразу меняем размер холста
addEventListener(‘resize’, () => {
resizeCanvas(innerWidth, innerHeight);
})

Результат

Посмотреть, что получилось с новой библиотекой, можно на странице проекта. Если вы хотите запустить такое у себя на компьютере, скопируйте готовый код и откройте HTML-страницу в браузере.

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Полёт в космос</title>
  <!-- сделаем так, чтобы в каждых браузерах результат выглядел одинаково — подключим нормализатор стилей -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <style type="text/css">
  	*{
  		/* сделаем так, чтобы мы в блоке управляли размерами не контента, а самого блока — это понадобится для звёзд */
		box-sizing: border-box;;
	}
	html, body{
		/* убираем границы и отступы */
		margin: 0;
		padding: 0;
		/* разворачиваем космос на всю ширину окна браузера */
		width: 100%;
		height: 100vh;
		/* не отображаем звёзды, которые вылетели за пределы окна*/
		overflow: hidden;
	}
  </style>

</head>
<body>
	<!-- подключаем p5 — библиотеку для рисования в JavaScrpt -->
	<script src='https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js'></script>
	<!-- основной скрипт -->
	<script type="text/javascript">
	// сколько звёзд может быть одновременно на экране
	var starsCount = 800;
	// на старте массив со звёздами будет пустой
	var stars = [];
	// класс, на основе которого будут сделаны все звёзды
	class Star{
		// конструктор, который вызывается при создании каждого объекта на основе этого класса
		constructor(){
			// у новой звезды будут случайные координаты
			this.x = random(-width, width);
			this.y = random(-height, height);
			// глубина — насколько виртуально близко к экрану появится звезда
			this.z = random(width);
		}
		// метод, который обновляет координаты звезды
		update(){
			// скорость полёта
			var speed = 12;
			// приближаем звезду к краю экрана, уменьшая глубину на значение скорости
			this.z -= speed;
			// если звезда вылетела за край экрана — делаем из неё новую звезду, для этого меняем координаты
			if(this.z < 1){
				this.x = random(-width, width);
				this.y = random(-height, height);
				// для новой звезды вместо старой глубину появления теперь выбираем не случайным образом, а задаём прямо
				this.z = width;
			}
		}

		// метод, который отрисовывает звезду на экране
		drawStar(){
			// каждая звезда — белого цвета
			fill(255);
			// и без контура
			noStroke();

			// с помощью функции map() из библиотеки p5.js получаем новые координаты для отрисовки звезды 
			var sx = map(this.x / this.z, 0, 1, 0, width);
			var sy = map(this.y / this.z, 0, 1, 0, height);

			// чем ближе к краю экрана (чем меньше глубина z) — тем больше радиус 
			var r = map(this.z, 0, width, 10, 0);
			// рисуем звезду в новых координатах и новым размером
			ellipse(sx, sy, r, r);
		}
	}

	// подготавливаем всё к запуску — то, что написано здесь выполнится автоматически сразу после загрузки
	function setup(){
		// создаём холст, на котором будем рисовать
		createCanvas(innerWidth, innerHeight);
		// размещаем сразу все звёзды на холсте
		for (var i = 0; i < starsCount; i++) {
			// каждая новая звезда — объект класса Star и умеет то же самое, что и все остальные звёзды
			stars[i] = new Star();
		}
	}

// пока мы не закроем страницу, постоянно будет выполняться функция drw() 
function draw(){
	// ставим чёрный фон и указываем скорость обновления фона — чем меньше второе число, тем больший шлейф будут оставлять звёзды
	background(0, 180);
	// формируем центр экрана, куда «полетим» сквозь звёзды
	translate(width/2, height/2);

	// отрисовываем каждый раз все звёзды и меняем их положение
	for (var i = 0; i < starsCount; i++) {
		stars[i].drawStar();
		stars[i].update();
	}
}

	// если поменяется размер окна браузера — сразу меняем размер холста
	addEventListener('resize', () => {
		resizeCanvas(innerWidth, innerHeight);
	})	
	</script>

</body>
</html>

Что дальше

Дальше сделаем ещё несколько проектов с библиотекой p5. Заодно попрактикуемся в ООП — создадим объекты на основе объектов из другого класса.

Текст

Михаил Полянин


Код

Sikriti Dakua


Редактор

Максим Ильяхов


Художник

Даня Берковский


Корректор

Ирина Михеева


Вёрстка

Мария Дронова


Соцсети

Олег Вешкурцев

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства и компенсация, если вы пойдёте работать в «Яндекс».
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
Улучшаем автоматизацию новостей
Улучшаем автоматизацию новостей

И получаем готовый рабочий продукт.

Подключаем нейросеть к веб-камере

Готовьтесь — искусственный интеллект уберёт вас из кадра.

Делаем свой блокировщик любой рекламы за 3 минуты
Делаем свой блокировщик любой рекламы за 3 минуты

Хакерский метод победить рекламодателей.

Делаем свой таймер на Python

Код — проще, возможностей — больше.

Пишем код: как поменять местами значения переменных
Пишем код: как поменять местами значения переменных

Что делать, если третью переменную использовать нельзя.

Моделируем игру в рулетку на Python
Моделируем игру в рулетку на Python

И строим наглядные графики.

Uncaught SyntaxError: Unexpected identifier — что это означает?
Uncaught SyntaxError: Unexpected identifier — что это означает?

Вредная ошибка, которую легко исправить.

Uncaught TypeError: Cannot read property — что это означает
Uncaught TypeError: Cannot read property — что это означает

Нельзя прочитать то, чего нет.

Как добавить кнопки «Поделиться» на страницу
Как добавить кнопки «Поделиться» на страницу

Простой рецепт от Яндекса.

ReferenceError: math is not defined — что это означает

Противная и неочевидная ошибка, которую очень легко исправить.

Универсальная аналитическая машина

Она может предсказать что угодно, если правильно её настроить.

Что означает ошибка UnboundLocalError: local variable referenced before assignment
Что означает ошибка UnboundLocalError: local variable referenced before assignment

Одна из самых частых ошибок у начинающих в Python.

Генератор статей для Кода
Генератор статей для Кода

Почти настоящие статьи на цепях Маркова

medium