Привет! Сегодня я хотел бы начать цикл статей на тему того, как работать с canvas в HTML5 и как применять знания в области матанализа для реализации необычных и интересных эффектов.

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

Сегодня мы рассмотрим, как рисовать различные фигуры, и реализуем алгоритм fibonacci flower, также известный как golden ratio или phyllotaxis.

Подготовительные аспекты

Для начала создадим пустой html документ, добавим в него canvas с id = canvas и пропишем немного стилей:

#canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #fcfcfc;
}

Далее получим ссылку на созданый элемент:

const canvas = document.getElementById("canvas");

Для начала мы будем рассматривать принципы canvas api, webgl api рассмотрим в рамках других статей. Для того, чтобы использовать canvas api, необходимо обратиться к интерфейсу CanvasRenderingContext2D, для этого создадим новую переменную context:

const ctx = canvas.getContext('2d');

А также установим высоту и ширину canvas, равную высоте и ширине окна браузера:

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

На этом подготовительные аспекты закончены, и можно приступать к непосредственно работе с графикой.

Arc

Arc метод — один из самых базовых методов canvas api и используется для рисования окружностей, а также кривых линий. Метод принимает следующие аргументы:
x
Горизонтальная координата центра дуги
y
Вертикальная координата центра дуги
radius
Радиус дуги
startAngle
Угол начала дуги в радианах
endAngle
Угол конца дуги в радианах

Сначала давайте нарисуем окружность. Для этого вызовем метод beginPath(), а после — метод arc следующим образом:

ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();

Таким образом можно получить окружность.

fillRect

Также мы можем нарисовать прямоугольник используя метод fillRect, который принимает следующие параметры:
x
Координата начальной точки прямоугольника по оси X
y
Координата начальной точки прямоугольника по оси Y
width
Ширина прямоугольника
height
Высота прямоуголника

ctx.fillRect(0, 0, 100, 200);

Анимация

Анимация в мире canvas — всего-навсего цикличная перерисовка, которая создает иллюзию движения. А как же можно создать цикличную перерисовку? Для этого создадим функцию animate и внутри данной функции вызовем метод requestAnimationFrame, передав в него ссылку на созданную функцию. Таким образом функция animate будет перевызываться вновь и вновь до тех пор, пока мы не остановим анимацию:

function animate() {
  window.requestAnimationFrame(animate);
}

Поместим код для рисования окружности во внутрь функции animate и вызовем ее:

function animate() {
  ctx.beginPath();
	ctx.arc(100, 100, 50, 0, Math.PI * 2);
	ctx.closePath();
	ctx.stroke();
  window.requestAnimationFrame(animate);
}

animate();

Как мы видим, анимация статична. Для того, чтобы проверить, что все работает корректно, создадим переменную radius и будем использовать ее в качестве аргумента метода arc:

function animate() {
	size += 0.01;
  // ...
	ctx.arc(100, 100, size, 0, Math.PI * 2);
	// ...
}

Также мы можем заставить двигаться нашу окружность, изменяя, например, параметр x метода arc:

function animate() {
	size += 0.01;
  positionX += 0.1;
  // ...
	ctx.arc(positionX, 100, size, 0, Math.PI * 2);
	// ...
}

Однако, как можно заметить, мы будем видеть шлейф, идущий за окружностью. Это происходит потому, что мы видим элементы предыдущих фреймов. Для того, чтобы избавиться от данного поведения, можно использовать метод clearRect, который принимает такие же аргументы, как и метод fillRect. В данном случае мы хотим полностью обновить canvas, поэтому вызовем метод следующим образом:

ctx.clearRect(0, 0, canvas.width, canvas.height);

Fibonacci flower

Перед тем, как мы рассмотрим принцип реализации данного алгоритма, давайте немного отрефакторим методы анимации. Для этого весь код, который отвечает за рисование на canvas, вынесем в функцию draw. Далее определимся с основными терминами. В основе алгоритма fibonacci flower лежит приницип движения окружности по круговой траектории. Исходя из определения кругового движения, нам необходимо будет считать X как X = originX + \cos (angle)*radius, а Y как Y = originY + \sin (angle)*radius

Это работает потому, что sin и cos математически связаны с единичной окружностью. Таким образом получим следующий код:

Развернуть
function draw() {
  let positionX = 0;
	let positionY = 0;
	let angle = 0;
  let radius = 0;
  
  ctx.beginPath();
  ctx.arc(positionX, positionY, 20, 0, Math.PI * 2);
  ctx.closePath();
  ctx.stroke();
}

function animate() {
  draw();
  window.requestAnimationFrame(animate);
}

animate();

Далее заведем переменный count и scale, в которых будем хранить количество окружностей (частиц) и коэффициент для расчета радиуса соответственно. Таким образом, код примет вид:

let count = 0;
let scale = 10;

function draw() {
  let angle = count;
  let radius = scale * Math.sqrt(count);
  let positionX = radius * Math.sin(angle);
  let positionY = radius * Math.cos(angle);
  
	// ...
  
  count++;
}

Здесь мы на каждой итерации изменяем переменную count, которая увеличивает значение radius, а также positionX и positionY, но чтобы это не происходило слишком быстро, будем использовать квадратный корень от count. Дело за малым, расположить fibonacci flower по центру экрана. Для этого прибавим к positionX и positionY canvas.width / 2 и canvas.height / 2 соответственно.

Меняя коэффициент перед count можно добиться различных результатов, таких как ниже:

Результаты
Коэффициент = 1
Коэффициент = 1
Коэффициент = 2
Коэффициент = 2
Коэффициент = 10
Коэффициент = 10

Чтобы изменить поведение отрисовки, можно изменить параметр globalCompositeOperation на значение destination-over, тогда новые фреймы будут рисоваться под уже отрисованными, что даст вполне интересный эффект:

Результаты
Коэффициент = 0.6
Коэффициент = 0.6
Коэффициент = 0.3
Коэффициент = 0.3

Финальный листинг будет выглядеть так:

Финальный листинг
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.globalCompositeOperation = "destination-over";

let count = 0;
let scale = 10;

function draw() {
  let angle = count * 0.1;
  let radius = scale * Math.sqrt(count);
  let positionX = radius * Math.sin(angle) + canvas.width / 2;
  let positionY = radius * Math.cos(angle) + canvas.height / 2;

  ctx.fillStyle = "#F24949";
  ctx.strokeStyle = "#8B428C";
  ctx.beginPath();
  ctx.arc(positionX, positionY, 20, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();

  count++;
}

function animate() {
  draw();
  if (count > 1000) return;
  window.requestAnimationFrame(animate);
}

animate();

В дальнейших статьях мы рассмотрим другие, не менее интересные приемы работы с canvas api и webgl. До скорых встреч!

Комментарии (0)