Я уже более 5 лет являюсь фронт енд разработчиком и всегда пытаюсь быть в курсе всех новостей. Помню что два года назад я заинтересовался графикой в JavaScript. И тут за пару месяцев я начал делать исследование про графику в JavaScript, так как у меня был тогда проект на D3.js. Во первых я начал изучать Canvas и SVG что является частью HTML5 но тоже нужно писать код в JS, например у меня проект был кустомизировать дорогие часы на Canvas. Следующий проект с графикой был с D3.js он был довольно прост, там нужно было на базе данных всех продаж компании показать графики в D3.js. Последний мои проект на базе гафики будет на p5.js и я решил узнать всё про него, и в этой публикации я хочу поделится своим исследованием про этот p5.js.
P5.js был создан командой Processing в 2014 году. Это JavaScript библиотека, с особым акцентом на изобразительное искусство. Цель P5.js совпадает с изначальной целью Processing — это снизить порог входа в программирование для художников, дизайнеров, учителей. С P5.js можно легко создавать бесконечно большое количество вариантов отображения информации: от веб-формата, статичных картинок для печати, до анимированных и интерактивных схем.
Processing был создан в 2001 году с целью обучить программированию непрограммистов, но с тех пор он стал основным языком для десятков тысяч художников и дизайнеров. P5.js делает рендеринг файлов PDE с исходным кодом Processing. При использовании этой библиотеки программист может вообще не знать JavaScript. Детально об отличиях Processing и p5 можно почитать в этой статье.
Для рендеринга используется canvas, а также p5.js поддерживает работу со звуком и видео. P5.js включает в себя совокупность обьектов, функций, констант с помощью которых можно создавать произвольные формы. Весь ряд возможностей p5.js можно найти в справочнике на официальном сайте.
P5 setup() выполняется один раз, когда проект загружается, так что это идеальное место, для инициализации. Подключить библиотеку можно с сервера или CDN:
<script src="../p5.min.js"></script>
<script src="//cdn.jsdelivr.net/p5.js/0.0.0/p5.min.js"></script>
Давайте создадим наш первый проект на p5.js. Наша цель состоит в том, чтобы создать инструмент для рисования, который преобразует простое изображение в область анимированных звезд. Для начала, мы определим несколько глобальных переменных и напишем setup(). Каждая отдельная графическая работа в p5.js называется «скетч», а каждый скетч состоит как минимум из двух функций — setup и draw.
Загрузить файлы вы можете здесь.
var hintImage, skyImage, stars = [];
function setup() {... }
Внутри нашей функции setup(), мы создадим холст и скроем курсор мыши чтоб иметь возможность рисовать от себя. P5 добавляет контур вокруг формы по умолчанию, в нашем случае, нам нужно отключить строки.
createCanvas(800,500);
noCursor();
noStroke();
Далее, мы загрузим два изображения. Одно будет служить в качестве фона — в нашем случае, сцена ночного неба. Другим будет «hint» изображение. Идея заключается в том, чтобы положить большинство звезд на черные пиксели нашего hint изображения, чтобы воссоздать дизайн фоновой сцены. Эти изображения достаточно легко создать с помощью p5 инструментов для рисования, но для краткости мы будем использовать статические изображения.
hintImage = loadImage("//bit.ly/hintImage");
skyImage = loadImage("//bit.ly/skyImage");
Еще одна ключевая функция это draw(). Она вызывается в непрерывном цикле, что полезно для анимации.
function draw() {... }
В функции Draw, наша первая задача состоит в том, чтобы заполнить canvas фоновым изображением. P5 не очистит холст между вызовами draw() автоматически, так что мы должны делать это каждый кадр, ибо в конечном итоге получим странные эффекты накопления. Чтобы поместить загруженное изображение на холст, используйте функцию image() и дайте ей координаты х и у для определения местоположения.
image(skyImage, 0, 0);
Далее, мы захватываем текущее местоположение мыши и сохраняем его в качестве p5.Vector используя createVector(). Этот объект включает в себя удобные функции, для работы с точками в пространстве, но мы будем в основном его использовать в качестве контейнера.
var position = createVector(mouseX, mouseY);
Используя недавно сохраненную позицию мыши, мы можем нарисовать наш курсор. Мы установим цвет курсора с помощью fill(), передавая значения RGB и будем использовать ellipse(), дабы нарисовать круг на месте мыши.
fill(255, 192, 0);
ellipse(position.x, position.y, 8, 8);
С помощью mouseIsPressed, мы сделаем так чтобы во время зажатия мыши рисовались новые звезды. Во время зажатия мыши, нам нужно вычислить хорошее место для следующей звезды. Мы сделаем это с помощью специальной функции под названием findPixel(), которую мы определим позже.
Как только у нас появится сигнал, мы создадим новый экземпляр объекта Star и прижмем его к концу нашего звездного массива. А если общее количество звезд превысит 2000, мы начнем отбрасывать самые старые из них.
if (mouseIsPressed) {
var target = findPixel();
var star = new Star(position, target);
stars.push(star);
if (stars.length > 2000) stars.shift();
}
В конце, мы пройдемся через наш массив звезд и вызовем update() и draw() на каждом из них. Мы рассмотрим эти методы позже.
for (var i = 0; i < stars.length; i++) {
stars[i].update();
stars[i].draw();
}
Теперь setup() и draw() на месте, а мы будем работать над вспомогательными функциями и объектами. Во-первых, мы определим функцию, которая находит место для каждой новой звезды. Все, что нам нужно сделать, это проверить рандомные пиксели в нашем изображении, используя get(), чтобы увидеть, черные они или белые.
На самом деле мы должны смотреть только на значение красного, так как в обоих случаях RGB значения совпадает.
function findPixel() {
var x, y;
for (var i = 0; i < 15; i++) {
x = floor(random(hintImage.width));
y = floor(random(hintImage.height));
if (red(hintImage.get(x,y)) < 255) break;
}
return createVector(x,y);
}
Теперь нам нужен многоразовый контейнер, который хранит информацию о каждой звезде, а также будет обновлять и рисования их. В JavaScript нет классического способа для создания классов в традиционном смысле этого слова, но мы можем получить тот же результат, определив функцию и настроив ее для собственных нужд.
Каждая звезда имеет несколько свойств (исходное положение, конечное местоположение и диаметр генерируемый случайным образом), мы их определим в «функции конструкторе», которая вызывается для каждой новой звезды, созданной в нашем цикле отрисовки.
function Star(position, target) {
this.position = position;
this.target = target;
this.diameter = random(1, 5);
}
Далее мы добавим метод update() для функции Star, который будет использовать p5.Vector lerp(), чтобы вычислить новое положение между текущим и целевым позициям звезды. В этом случае, мы движемся на 4% оставшегося расстояния на каждой итерации.
Star.prototype.update = function() {
this.position = p5.Vector.lerp(
this.position,
this.target,
0.04
);
};
И, наконец, метод draw(), который рисует звезду на холсте. Еще раз, мы используем fill() и ellipse(), хотя на этот раз мы вызываем fill() со значением alpha для прозрачности.
Чтобы дать звездам огонька, значение alpha определяется с помощью функции noise(). Это возвращает значение шума Перлина для указанных координат, то есть вы получаете гладкую, последовательность случайных чисел. В третий параметр, мы вводим значение на основе времени, с помощью него шум будет анимироваться в течение некоторого времени.
Star.prototype.draw = function() {
var alpha = noise(
this.target.x,
this.target.y,
millis()/1000.0
);
fill(255, alpha * 255);
ellipse(
this.position.x, this.position.y,
this.diameter, this.diameter
);
};
Вот что у нас в итоге получилось:
На этом работа над нашим первым скетчем закончена.
Еще один хороший пример работы с p5.js можно посмотреть в этом видео.
Справочный материал:
В заключении для меня p5.js был хорошим открытием, и надеюсь что всё моё исследование помогло узнать больше про p5.js, и мои тестовый первый проект будет хорошим примером для ознакомление с этим JS фреймворком.