Atmospherika
  • Slide1
  • Slide2
  • Slide3

Интерактив для сайта на processing.js и p5.js

Небольшое вступление.

У меня совсем не было времени писать в блог. Но рада видеть, что свою функцию он выполняет-по статьям переходят, их довольно внимательно читают, а мне приятно, что посты пришлись кому-то полезными. Уже давно решила сделать запись на тему processing и processing.js, т.к. статей на русском про этот язык мало, а фишки на нем можно делать действительно интересные. На днях узнала, что вышла новая библиотека p5.js от разработчиков processing, что меня сразу заинтересовало. Ведь я давно думала добавить один интерактивный элемент для сайта из моего портфолио. Так почему не совместить создание этого элемента, изучение новой библиотеки и написание познавательной (надеюсь) статьи-урока?:) И вот сегодня отложила все намеченные дела и занялась сравнением этих двух библиотек, чтобы самой разобраться и другим поведать.  Статья-урок предполагает быть длинной, поэтому начнем по порядку!

 

Processing

 

От себя добавлю, что познакомилась я с его «плодами», как оказалось, лет 5 назад, когда интересовалась программами по созданию фракталов и не знала нормально ни одного ЯП, просто посмотрев вот это видео.  

Правда, вдохновляет? Здесь можно увидеть больше примеров применения processing.   Processing — это язык программирования, среда разработки, а также онлайн сообщество. Он был разработан в 2001 году и основан на Java. Используется для создания мультимедия приложений («скетчей»), позволяет создавать графику, анимацию, интерактивные приложения, игры, подгружать аудио/видео,  а также работать со сторонними библиотеками (PDF, OpenGL). Чем он интересен именно web-разработчикам? А тем, что с помощью библиотеки processing.js ваш скетч может быть представлен на web-странице как java-аплет.

 

Особенности processing

 

- Это open source, скачать его можно бесплатно с офф. сайта либо с github

- Имеет средства поддержки OpenGL для создания 3D объектов

- Поддерживается Windows, GNU/Linux, Mac OS X

- Базовые функции могут быть расширены более чем 100 библиотеками

- Хорошая документация, большое количество наглядных примеров

Собственно, начать работу можно скачав processing для вашей ОС и запустив исполняемый файл в корне папки, которая откроет окно, в котором будет писаться ваш первый «скетч».

 

Первый скетч — интерактивный хамелеон.

 

Задача состоит в том, чтобы сделать хамелеона с подвижным глазом (глазами) для последующей его посадки на элемент фона на сайте. До тех пор пока курсор мыши не окажется в “поле зрения” хамелеона, его зрачок движется рандомно. Как только курсор мыши оказывается достаточно близко – хамелеон должен начать следить глазом за курсором. За поле зрения возьмем размер холста, на котором происходит действие. Самого хамелеона рисовать не будем. Я просто загрузила более менее подходящее изображение из интернета. Система координат processing начинается в верхнем левом углу, ось X направлена вправо, ось Y — вниз. Далее приведу части кода, благо он небольшой, и последовательно всё прокомментирую.

class Eye 
{
  int ex, ey;
  int size;
  float angle = 0.0;

  Eye(int x, int y, int s) 
  {
    ex = x;
    ey = y;
    size = s;
  }
         void update(int mx, int my)
          {
          angle = atan2(my-ey, mx-ex); 
          }

          void display() 
          {
           pushMatrix();
          translate(ex, ey);

          fill(132,151,10);
          ellipse(0, 0, size, size);

          rotate(angle);          

          fill(146,168,9);
          ellipse(size/16, 0, size*0.9, size*0.9);  

          fill(176,199,31);
          ellipse(size/8, 0, size/1.3, size/1.3);

          fill(194,221,31);    
          ellipse(size/6, 0, size/1.8, size/1.8);

          fill(0);   
          ellipse(size/4, 0, size/3, size/3);  
          popMatrix();

        }

}

Это класс Eye, который описывает глаз(круг). В самом начале объявляем переменные класса, язык строго типизированный, делать это нужно обязательно. Строки 7-12 – это объект класса с такими его свойствами как размер (size), координаты расположения на экране x и y. Метод класса update принимает в качестве аргументов координаты курсора. Функция atan2 рассчитывает угол наклона зрачка в радианах. Метод display отвечает за размер, цвет и отображение кругов. Здесь нужно сказать, что круг можно сделать один – сам зрачок, но для большей реалистичности сделаем несколько перемещающихся кругов, имитирующих складки кожи, как они выглядят у настоящих хамелеонов. Функция translate(x,y) переносит начало системы координат в точку (x,y). fill() задает цвет первого круга в rgb, а ellipse(0,0,size,size) рисует эллипс с центром в точке 0,0, ширины size и высоты size (если эти 2 величины равны, как здесь, то отрисовывается круг). В строке 26 функция rotate поворачивает все объекты, нарисованные после нее, на угол angle. c 28 по 41  строку рисуются 4 круга разных цветов, которые затем будут вместе поворачиваться.

А зачем нужны функции pushMatrix() и popMatrix()? Именно данный пример с одним глазом работает и без этих функций, но без них он не будет корректно работать при большем количестве объектов. Дело в том, что при каждом повороте или перемещении система координат меняется относительно предыдущих изменений. Т. е. последовательно написанные translate(0,20) и затем translate(0,70) приравниваются к одному перемещению translate(0,90).И последующие объекты оказываются совсем не в том месте, в котором ожидалось. С помощью pushMatrix() в строке 21 мы сохраняем текущее состояние системы координат, далее производим все преобразования относительно точки 0,0 (в верхнем левом углу). В конце, после всех необходимых перемещений и поворотов, popMatrix() возвращает систему координат в исходное состояние(левый верхний угол).

PImage ch; //изображением с именем ch типа PImage
Eye e4; //объявляем объект e4
int rx, ry;//рандомные x,y
int bgX=400;//размер “холста”
int bgY=400;
boolean randEye=true;//метка рандомного движения глаз
int lastX=20;//переменные, хранящие предыдущее положение координат зрачка
int lastY=20;

void setup() 
{
frameRate(60);
noStroke();
size(bgX, bgY);
background(255);
ch = loadImage("chameleon.png");
image(ch,150,0);
e4 = new Eye(229,  96,  24); 
}

С 1 по 8 строку идет объявление необходимых переменных. Далее представлена служебная функция setup(), она должна быть в каждой программе, в ней происходит загрузка начальных параметров скетча. size(bgX, bgY) инициализирует canvas(холст), на котором происходят все действия.framRate(60) задает частоту перерисовки холста. Её можно не задавать, если необходима частота 60 кадров в секунду, но для наглядности написала данную функцию. background задает цвет, loadImage() подгружает картинку с хамелеоном, image(сh,150,0) принимает первым аргументом загруженное ранее изображение, 2 других аргумента-x,y координаты изображения. noStroke() отключает обводку объектов.

void draw() 
{
 if(randEye)//изначально true
       {
           if(frameCount%100==0)
           {
            rx=int(random(1, bgX));//случайное число от 1 до 400
            ry=int(random(1, bgY));
            lastX=rx;//сохраняем предыдущие координаты
            lastY=ry;
            e4.update(rx, ry);
           }  
           else
            e4.update(lastX, lastY);    
       }

if (mouseX >5 && mouseX < (bgX-5) && mouseY< (bgY-5) && mouseY >5)
 {
    randEye = false; 
    e4.update(mouseX, mouseY);
 } 
else
  randEye = true; 

e4.display();
}

Еще одна обязательная функция draw(). Она вызывается с частотой, указанной как аргумент в функции frameRate(). В данной функции происходит вся анимация. В строках с 3 по 15 описывается движения глаза, если курсор не находится на холсте.frameCount — это системная переменная, которая увеличивается на 1 при каждой перерисовке. Здесь возникла такая проблема, что если в режиме слежения зрачка за курсором 30-60 кадров /c — это нормальная скорость, позволяющая двигаться зрачку плавно, то та же самая скорость в режиме рандомного перемещения глаза слишком высока! Если же задать frameRate(0.3), то скорость станет достаточно медленной, НО при попадании курсора на canvas программа не сразу распознает, что координаты мыши изменились — произойдет ощутимая задержка при смене режимов зрачка. Поэтому, чтобы перерисовка оставалась с той же частотой, а глаз двигался медленней — нужно было задать, что глаз должен менять угол наклона только когда frameRate кратна 100. (т.е. 100..500..2300 и т д). Рандомные координаты сохраняем в переменные lastY и lastY, затем обновляем угол наклона. Если frameRate не кратна 100, то вызываем ф-цию update с сохраненными координатами зрачка (если этого не сделать, то глаз будет исчезать в некратный frameRate). В строках 17-23 говорится, что если координаты мыши в пределах холста, то переходим в режим слежения глаза за курсором мыши, иначе — в режим случайного зрачка.

 

Почему рамки холста я сделала от 5 до 395? Опять же по той причине, что если резко выйти за пределы скетча, то холст не успеет перерисоваться и координаты мыши будут, например, не (400,123), а (397,123). (На web-странице такой проблемы не будет) Однако если «кругозор» хамелеона сделать от 50 до 350 px, то даже при резком движении режим «слежения» будет успевать выключаться. Ну и в самом конце функция показа глаза.

Программа написана. Исходник.

 

Processing.js

 

Итак, мы написали программу, которая запускается через IDE, как же запустить её на web-странице? Специально для этого существует библиотека processing.js.  Основная цель processing.js  - выполнение processing файлов в HTML5, при этом отпадает необходимость писать на нативном HTML5. Processing.js использует регулярные выражения для конвертирования Java в JS, результат работы отображается в теге canvas.

Ранее написанную программу нужно поместить в коде ниже после строки 3. Строка 3 подгружает изображение с хамелеоном, без нее изображения с помощью loadImage() подгружаться не будут.

<!DOCTYPE html>
<script type="text/processing" data-processing-target="mycanvas"> 
 /* @pjs preload="chameleon.png"; */ 

 </script> 
 <canvas id="mycanvas" width='400' height='400'></canvas>

Заменим теперь часть кода, которая идёт после функции setup().

void mouseOver() 
{
 randEye = false;  
}
void mouseOut() 
{
 randEye = true; 
}
void draw() 
{
 if(randEye)
       {
          if(frameCount%100==0)
           {
            rx=int(random(1, bgX));
            ry=int(random(1, bgY));
            lastX=rx;
            lastY=ry;
            e4.update(rx, ry);
          }  
         else
          e4.update(lastX, lastY);

       }

    if (!randEye) //on canvas
   e4.update(mouseX, mouseY);

e4.display();
}

Здесь появились две служебные функции: mouseOver()-срабатывает, когда курсор в пределах скетча, mouseOut()-срабатывает, когда курсор вне пределов скетча. Данные функции работают только на WEB-странице. Теперь нет надобности указывать границы скетча и программа работает точнее. Запускается приложение во всех современных браузерах, даже в IE(но только если не забыть написать вверху программы <!DOCTYPE html> )

 

P5.js

 

ProcessingJs — хороший инструмент для тех, кто хочет запускать свои скетчи в WEB, но для некоторых тяжело понять, как работают некоторые вещи на processing и тем более как исправить баги, как модифицировать или расширить библиотеку. P5.js — новая javascript-библиотека от разработчиков processing. О ней вы можете посмотреть презентацию, параллельно оценив её возможности и загрузить. Если processing.js интерпретировал код .pde файла, то здесь об этом уже не идёт речь. Теперь processing можно не знать вовсе, создавать скетчи можно на родном для очень многих разработчиков javascripte, при этом используя элементы библиотеки p5 для рисования и анимации.

Здесь также обязательными функциями являются setup() и draw(). Те, кто привык писать приложения на processing, найдут примеры, которые наглядно показывают различия в написании. Далее я приведу того же самого хамелеона на JS и p5.js.

 

 

<!DOCTYPE html>
<script src="p5.min.js"></script>
<script src="p5.dom.js"></script>
<style type="text/css">
  #defaultCanvas
  {
   position:absolute;
   z-index:100; 
  }
</style>
<script type="text/javascript">

function Eye(x, y, s) { 

  this.ex = x;
  this.ey = y;
  this.s = s;
}

Eye.prototype.display = function() {
	push();
	translate(this.ex, this.ey);

    fill(132,151,10);
    ellipse(0, 0, this.s, this.s);

    rotate(angle);

    fill(146,168,9);
    ellipse(this.s/16, 0, 0.9*this.s, 0.9*this.s);  

    fill(176,199,31);
    ellipse(this.s/8, 0, this.s/1.3, this.s/1.3);

    fill(194,221,31);
    ellipse(this.s/6, 0, this.s/1.8, this.s/1.8);

    fill(0);
    ellipse(this.s/4, 0, this.s/3, this.s/3);
	pop();

};

Eye.prototype.update = function(mx,my) {
angle = atan2(my-this.ey, mx-this.ex);
}
var e4; // Object
var bgX=400;
var bgY=400;
var randEye=true;
var lastX=20;
var lastY=20;
function setup() {

noStroke();
img = createImg("chameleon.png");
img.position(150, 0);
img.size(250, 376);
canvas=createCanvas(bgX, bgY);
background(255,255,255,0.1);

canvas.mouseOver(randEyeOff);
canvas.mouseOut(randEyeOn);

e4 = new Eye(221,  88,  24); 

}

function randEyeOff() {
 randEye = false;  
}

function randEyeOn() {
randEye = true; 
}
function draw() {

	if(randEye)
       {
           if(frameCount%100==1)
           { 
            rx=random(1, bgX);
            ry=random(1, bgY);
            lastX=rx;
            lastY=ry;
            e4.update(rx, ry);
           }  
         else
           e4.update(lastX, lastY);  

       }
if(!randEye)
  e4.update(mouseX, mouseY);

e4.display();
}
</script>

Здесь скажу только о значимых изменениях.Строки 59-61 — изображение здесь больше не загружается с помощью loadImage, хоть данная функция и осталась в p5Js. Дело в том, что не все браузеры позволяют таким образом загрузить изображение. createImg — это просто создание тега img в html документе, img.position(x,y) — сдвиг изображения, img.size(w,h) — его размер (в документации показано, что один параметр можно написать как AUTO, но лучше этого не делать, та же мозилла размажет изображение на пол экрана). В связи с тем, что изображение больше не часть canvas, а находится под ним, для корректного отображения в строках 5-9 написаны свойства холста.
В справке p5 вы не найдете упоминание о mouseOver/mouseOff, но благодаря библиотеке p5.dom здесь появляется возможность к каждому элементу добавить одноименные свойства!В данном примере я применяю его к canvas (а можно еще к изображению, но незачем). Немного изменила условие frameCount%100==1, а не 0, т.к. IE почему-то не соображал, какие числа кратны 100..

А вот и хамелеон в действии.

Спасибо, что дочитали до данной строки : )

Справочный материал:

-Processing

-Processing.js

-P5.js

 

Опубликовано: calendar2014-09-28  clock20:55

Метки: , , , ,

Добавить комментарий для REMONTDef Отменить

Ваш e-mail не будет опубликован.
Поля, отмеченные * обязательны для заполнения.

*


один × 8 =