Вы находитесь на странице: 1из 76

Лабораторне заняття 5.

Створення динамічних елементів

Цель занятия: создание статических и динамических изображений с помощью технологии


canvas HTML5 в JavaScript.

Задание 1 Изучение технологии canvas HTML5 на примерах создания простых


геометрических примитивов

Реализуется канвас с помощью нового тега <canvas>.

<canvas> — это HTML элемент, использующийся для рисования графики средствами


языков программирования (обычно это JavaScript). Он может, к примеру, использоваться для
рисования графов, создания коллажей или простой (и не очень) анимации.
Впервые <canvas> использовался компанией Apple для создания Mac OS X Dashboard, а
затем был реализован в Web-браузерах. На сегодняшний день все основные браузеры
поддерживают работу с <canvas>. Тег <canvas> часть спецификации WhatWG Web applications
1.0 также известной как HTML5.

Канвас реализуется с помощью нового тега <canvas>, который появился в HTML5. Для
начала работы необходимо подготовить поле для рисования. Это поле делается с помощью
тега <canvas>, которому следует указать ширину и высоту (укажем также и id, чтобы легко
можно было обратиться к нему через JavaScript):

<canvas id="canvas" width="200" height="200"></canvas>

Затем нужно получить поле для рисования с помощью функции getElementById(). Функция
возвращает ссылку на элемент по его идентификатору (ID). Идентификатор может быть
определен при помощи атрибута id в HTML или из скрипта. Например:

var canvas = document.getElementById('canvas');


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

Переменная ctx - это объект, содержащий в себе так называемый контекст выполнения.
Все рисование будет происходить с помощью методов этого объекта.

Задание 1.1.
Реализовать данный пример и провести эксперимент с параметром прозрачности синего
прямоугольника

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script type="application/javascript">
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');

ctx.fillStyle = 'rgb(200, 0, 0)';


ctx.fillRect(10, 10, 50, 50);
ctx.fillStyle = 'rgba(0, 0, 200, 0.5)';
ctx.fillRect(30, 30, 50, 50);

}
}
</script>
</head>
<body onload="draw();">
<canvas id="canvas" width="150" height="150"></canvas>
</body>
</html>
Результат выполнения кода:

Данную страницу HTML будем использовать в качестве шаблона для изучения различных
инструментов рисования.

Сетка
Перед тем, как рисовать, нужно определить понятие сетки canvas или координатной
плоскости. HTML каркас из предыдущей страницы включал в себя элемент canvas 150 пикселей
в ширину и 150 пикселей в высоту. Справа можно увидеть этот canvas с сеткой, накладываемой
по умолчанию. Обычно 1 единица на сетке соответствует 1 пикселю на canvas. Начало
координат этой сетки расположено в верхнем левом углу в координате (0,0). Все элементы
размещены относительно этого начала. Таким образом, положение верхнего левого угла синего
квадрата составляет х пикселей слева и у пикселей сверху, на координате (х, у). Позже увидим,
как можно перевести начало координат в другое место, вращать сетку и даже масштабировать
ее, сейчас будем придерживаться настроек сетки по умолчанию.
Рисование прямоугольников
В отличие от SVG, <canvas> поддерживает только одну примитивную фигуру:
прямоугольник. Все другие фигуры должны быть созданы комбинацией одного или большего
количества контуров (paths), набором точек, соединенных в линии. К счастью в ассортименте
рисования контуров у нас есть функции, которые делают возможным составление очень
сложных фигур.
Сначала рассмотрим прямоугольник. Ниже представлены три функции рисования
прямоугольников в canvas:

fillRect(x, y, width, height)

Рисование заполненного прямоугольника.

strokeRect(x, y, width, height)

Рисование прямоугольного контура.

clearRect(x, y, width, height)

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


Каждая из приведенных функций принимает несколько параметров:
x, y - устанавливают положение верхнего левого угла прямоугольника в canvas
(относительно начала координат);
width(ширина) и height(высота) - определяют размеры прямоугольника.

Ниже приведен фрагмент функции draw(), использующая эти три функции.

ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);

В процессе выполнения функции draw() на холсте появится результат

Задание 1.2.
Пояснить алгоритм прорисовки данного квадрата.
Рисование контуров (path)
Остальные примитивные фигуры создаются контурами. Контур - это набор точек, которые,
соединяясь в отрезки линий, могут образовывать различные фигуры, изогнутые или нет, разной
ширины и разного цвета. Контур (или субконтур) может быть закрытым.
Создание фигур, используя контуры, происходит в несколько важных шагов:
Сначала вы создаете контур.
Затем, используя команды рисования, рисуете контур.
Потом закрываете контур.
Созданный контур можно обвести или залить для его отображения.
Здесь приведены функции, которые можно использовать в описанных шагах:

beginPath()

Создает новый контур. После создания используется в дальнейшем командами рисования


при построении контуров.
Path методы
Методы для установки различных контуров объекта.

closePath()

Закрывает контур, так что будущие команды рисования вновь направлены контекст.
stroke()

Рисует фигуру с внешней обводкой.

fill()

Рисует фигуру с заливкой внутренней области.


Первый шаг создания контура заключается в вызове функции beginPath(). Внутри
содержатся контуры в виде набора субконтуров (линии, дуги и др.), которые вместе образуют
форму фигуры. Каждый вызов этого метода очищает набор, и мы можем начинать рисовать
новые фигуры.
Если текущий контур пуст (например, как после вызова beginPath() или на вновь созданном
canvas), первой командой построения контура всегда является функция moveTo(). Поэтому мы
всегда можем установить начальную позицию рисования контура после перезагрузки.
Вторым шагом является вызов методов, определяемых видом контура, который нужно
нарисовать. Их мы рассмотрим позднее.
Третий и необязательный шаг - это вызов closePath(). Этот метод пытается закрыть фигуру,
рисуя прямую линию из текущей точки в начальную. Если фигура была уже закрыта или
является просто точкой, то функция ничего не делает.
Когда вы вызываете fill(), то каждая открытая фигура закрывается автоматически, так что
вы можете не использовать closePath(). Это обстоятельство не имеет место в случае вызова
stroke().
Рисование треугольника
Например, код для рисования треугольника будет выглядеть как-то так:

ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();

Результат

Передвижение пера
Одна очень полезная функция, которая ничего не рисует, но связана по смыслу с
вышеописанными функциями - это moveTo(). Вы можете представить это как отрыв (подъем)
пера от бумаги и его перемещение в другое место.
moveTo(x, y)

Перемещает перо в точку с координатами x и y.


При инициализации canvas или при вызове beginPath(), вы захотите использовать функцию
moveTo() для перемещения в точку начала рисования. Можно использовать moveTo() и для
рисования несвязанного (незакрытого) контура. Посмотрите на смайлик ниже.
Вы можете проверить это сами, используя участок кода ниже. Просто вставьте в функцию
draw(), рассмотренную ранее.

ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true);
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false);
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true);
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true);
ctx.stroke();

Результат этого ниже:


Если вы захотите увидеть соединительные линии, то можете удалить вызов moveTo().

Задание 1.3.
Прокомментируйте все операторы фрагмента функции draw() и внесите изменения во
фрагмент так, чтобы линия прорисовки смайлика была непрерывной.

Линии
Для рисования прямых линий используйте метод lineTo().

lineTo(x, y)

Рисует линию с текущей позиции до позиции, определенной x и y.


Этот метод принимает два аргумента x и y, которые являются координатами конечной
точки линии. Начальная точка зависит от ранее нарисованных путей, причём конечная точка
предыдущего пути является начальной точкой следующего и т. д. Начальная точка также может
быть изменена с помощью метода moveTo().
Пример ниже рисует два треугольника, один закрашенный и другой обведен контуром.

ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill();
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();

Отрисовка начинается с вызова beginPath(), чтобы начать рисовать путь новой фигуры.
Затем мы используем метод moveTo(), чтобы переместить начальную точку в нужное
положение. Ниже рисуются две линии, которые образуют две стороны треугольника.
Вы заметите разницу между закрашенным и обведенным контуром треугольниками. Это,
как упоминалось выше, из-за того, что фигуры автоматически закрываются, когда путь заполнен
(т. е. закрашен), но не тогда, когда он очерчен (т. е. обведен контуром). Если бы мы не учли
closePath() для очерченного треугольника, тогда только две линии были бы нарисованы, а не
весь треугольник.

Дуги
Для рисования дуг и окружностей, используем методы arc() и arcTo().

arc(x, y, radius, startAngle, endAngle, anticlockwise)


Рисуем дугу с центром в точке (x,y) радиусом radius, начиная с угла startAngle и заканчивая
в endAngle в направлении против часовой стрелки anticlockwise (по умолчанию по ходу
движения часовой стрелки).
arcTo(x1, y1, x2, y2, radius)

Рисуем дугу с заданными контрольными точками и радиусом, соединяя эти точки прямой
линией.
Рассмотрим детальнее метод arc(), который имеет пять параметров: x и y — это
координаты центра окружности, в которой должна быть нарисована дуга. radius — не требует
пояснений. Углы startAngle и endAngle определяют начальную и конечную точки дуги в радианах
вдоль кривой окружности. Отсчет происходит от оси x. Параметр anticlockwise — логическое
значение, которое, если true, то рисование дуги совершается против хода часовой стрелки;
иначе рисование происходит по ходу часовой стрелки.
Углы в функции arc() измеряют в радианах, не в градусах. Для перевода градусов в
радианы вы можете использовать JavaScript-выражение: radians = (Math.PI/180)*degrees.
Следующий пример немного сложнее, чем мы рассматривали ранее. Здесь нарисованы 12
различных дуг с разными углами и заливками.
Два for цикла размещают дуги по столбцам и строкам. Для каждой дуги, мы начинаем
новый контур, вызывая beginPath(). В этом коде каждый параметр дуги для большей ясности
задан в виде переменной, но вам не обязательно делать так в реальных проектах.
Координаты x и y должны быть достаточно ясны. radius and startAngle — фиксированы.
endAngle начинается со 180 градусов (полуокружность) в первой колонке и, увеличиваясь с
шагом 90 градусов, достигает кульминации полноценной окружностью в последнем столбце.
Установка параметра clockwise определяет результат; в первой и третьей строках
рисование дуг происходит по часовой стрелке, а во второй и четвертой - против часовой
стрелки. Благодаря if-условию верхняя половина дуг образуется с контуром, (обводкой), а
нижняя половина дуг - с заливкой.
Этот пример требует немного большего холста (canvas), чем другие на этой странице: 150
x 200 pixels.

for(var i=0;i<4;i++){
for(var j=0;j<3;j++){
ctx.beginPath();
var x = 25+j*50;
var y = 25+i*50;
var radius = 20;
var startAngle = 0;
var endAngle = Math.PI+(Math.PI*j)/2;
var anticlockwise = i%2==0 ? false : true;
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
if (i>1){
ctx.fill();
} else {
ctx.stroke();

Задание 1.4.
Реализовать вышеприведенный фрагмент примера функции draw() и пояснить полученный
результат.
Безье и квадратичные кривые
Следующим типом доступных контуров являются кривые Безье, и к тому же доступны в
кубическом и квадратичном вариантах. Обычно они используются при рисовании сложных
составных фигур.

quadraticCurveTo(cp1x, cp1y, x, y)

Рисуется квадратичная кривая Безье с текущей позиции пера в конечную точку с


координатами x и y, используя контрольную точку с координатами cp1x и cp1y.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

Рисуется кубическая кривая Безье с текущей позиции пера в конечную точку с


координатами x и y, используя две контрольные точки с координатами (cp1x, cp1y) и (cp2x,
cp2y).
Различие между ними можно увидеть на рисунке, изображенном справа. Квадратичная
кривая Безье имеет стартовую и конечную точки (синие точки) и всего одну контрольную точку
(красная точка), в то время как кубическая кривая Безье использует две контрольные точки.
Параметры x и y в этих двух методах являются координатами конечной точки. cp1x и cp1y
— координаты первой контрольной точки, а cp2x и cp2y — координаты второй контрольной
точки.
Использование квадратичных или кубических кривых Безье может быть спорным выходом,
так как в отличие от приложений векторной графики типа Adobe Illustrator, мы не имеем полной
видимой обратной связи с тем, что мы делаем. Этот факт делает довольно сложным процесс
рисования сложных фигур. В следующем примере мы нарисуем совсем простую составную
фигуру, но, если у вас есть время и ещё больше терпения, можно создать более сложные
составные фигуры.
В этом примере нет ничего слишком тяжелого. В обоих случаях мы видим
последовательность кривых, рисуя которые, в результате получим составную фигуру.
Квадратичные кривые Безье
В этом примере многократно используются квадратичные кривые Безье для рисования
речевой выноски.

ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
Кубические кривые Безье
В этом примере нарисовано сердце с использованием кубических кривых Безье.

ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
Прямоугольники
Все эти методы мы видели в Рисование прямоугольников, которые рисуют прямоугольники
сразу в canvas, так же есть метод rect(), который не отображает, а только добавляет контур
рисования (path) заданного прямоугольника к последнему открытому контуру.

rect(x, y, width, height)

Добавляет в path прямоугольник, верхний левый угол которого указан с помощью (x, y) с
вашими width и height
Когда этот метод вызван, автоматически вызывается метод moveTo() с параметрами (x, y).
Другими словами, позиция курсора устанавливается в начало добавленного прямоугольника.

Применение стилей и цветов


Цвета
До сих пор мы видели только методы рисования контекста. Если мы хотим применить
цвета к фигуре, то есть два важных свойства, которые мы можем использовать: fillStyle и
strokeStyle.

fillStyle = color

Устанавливает стиль для фона фигур.

strokeStyle = color

Устанавливает стиль контура фигуры.


color может быть цветом, (строка, представленная в CSS <color>), градиентом или
паттерном. Градиенты и паттерны мы рассмотрим позже. По умолчанию цвет фона и контура —
черный (значение CSS цвета #000000).
Когда вы устанавливаете значения strokeStyle и/или fillStyle, то новое значение становится
стандартным для всех фигур, которые будут нарисованы с этого момента. Когда вам нужен
другой цвет, вы должны перезаписать значение в fillStyle или в strokeStyle для каждой фигуры.
Чтобы строка color считалась валидной, она должна соответствовать CSS <color>. Далее
приведены примеры того, как можно по-разному задать один и тот же цвет.
// these all set the fillStyle to 'orange'

ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";

Пример fillStyle
В этом примере воспользуемся двойным циклом, чтобы нарисовать сетку из
прямоугольников, каждый из которых имеет свой цвет. Окончательное изображение должно
иметь вид, как показано на скриншоте. Здесь не происходит ничего сверхъестественного. Мы
используем две переменные i и j для генерации уникального RGB цвета для каждого квадрата и
изменяем только красные и зеленые значения. Синий канал представляет собой
фиксированное значение. Путем изменения каналов вы можете генерировать всю палитру.
Увеличив количество шагов вы можете достигнуть такого вида палитры, какая используется в
Photoshop.

for (var i=0;i<6;i++){


for (var j=0;j<6;j++){
ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)';
ctx.fillRect(j*25,i*25,25,25);
} }
Результат выглядит так:

Задание 1.5.
В вышеприведенном примере установить шаг сетки в два раза больше чем есть.

Пример strokeStyle
Этот пример похож на предыдущий, но мы используем свойство strokeStyle чтобы изменить
цвета очертаний фигур. Так же мы используем метод arc() для рисования окружностей вместо
квадратов.

for (var i=0;i<6;i++){


for (var j=0;j<6;j++){
ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ')';
ctx.beginPath();
ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
ctx.stroke();
} }
Результат выглядит так:

Задание 1.6.
В вышеприведенном примере заполнить пространство полуокружностями.

Прозрачность
В дополнении к рисованию непрозрачных фигур, мы также можем рисовать прозрачные
(полупрозрачные) фигуры. Это делается через установку свойства globalAlpha или задачи
полупрозрачного цвета фона или контура.

globalAlpha = transparencyValue
Для применения, указывается значения прозрачности для всех будущих фигур, что будут
нарисованы на canvas. Значение полупрозрачности могут быть между 0.0 (полная
прозрачность) и 1.0 (полная непрозрачность). Значение 1.0 (полная непрозрачность)
установлено по умолчанию.
Свойство globalAlpha может быть использовано, если вы хотите рисовать формы с
одинаковой прозрачностью, но в иной ситуации, обычно устанавливают прозрачность
индивидуально к каждой форме, когда указывают их цвет.
Так как свойства strokeStyle и fillStyle принимают цветовые значения rgba через CSS, мы
можем использовать следующее обозначение для назначения прозрачных цветов.

ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.fillStyle = "rgba(255,0,0,0.5)";

Функция rgba() похожа на функцию rgb(), но имеет один дополнительный параметр.


Последний параметр устанавливает значение прозрачности для конкретного цвета.
Действующий диапазон значений находится между 0.0 (полная прозрачность) и 1.0 (полная
непрозрачность).

Пример globalAlpha
В данном примере мы нарисуем фон и четыре квадрата с различными цветами. Сверху
изображения будет выведен набор полупрозрачных кругов. Установим свойство globalAlpha
значением 0.2, которое будет использовано для всех последующих форм. Каждый шаг цикла
рисует круг с большим радиусом. По окончанию получим радиальный градиент. Накладывая
еще больше кругов друг на друга, мы фактически сможем уменьшить прозрачность ранее
нарисованных кругов. Увеличив счетчик итераций, при этом рисуя еще круги, мы сможем
добиться исчезновение центра изображения.

// фон изображения
ctx.fillStyle = '#FD0';
ctx.fillRect(0,0,75,75);
ctx.fillStyle = '#6C0';
ctx.fillRect(75,0,75,75);
ctx.fillStyle = '#09F';
ctx.fillRect(0,75,75,75);
ctx.fillStyle = '#F30';
ctx.fillRect(75,75,75,75);
ctx.fillStyle = '#FFF';
// устанавливаем значение прозрачности
ctx.globalAlpha = 0.2;
// Рисуем полупрозрачные круги
for (i=0;i<7;i++){
ctx.beginPath();
ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
ctx.fill();
}

Пример использования rgba()


В этом втором примере мы делаем что-то похожее на предыдущее, но вместо рисования
кругов друг над другом, я рисовал маленькие прямоугольники с увеличением непрозрачности.
Использование rgba() добавляет контроля и гибкости, поскольку мы можем индивидуально
настраивать стиль заливки и штриха.

// Нарисовать фон
ctx.fillStyle = 'rgb(255,221,0)';
ctx.fillRect(0,0,150,37.5);
ctx.fillStyle = 'rgb(102,204,0)';
ctx.fillRect(0,37.5,150,37.5);
ctx.fillStyle = 'rgb(0,153,255)';
ctx.fillRect(0,75,150,37.5);
ctx.fillStyle = 'rgb(255,51,0)';
ctx.fillRect(0,112.5,150,37.5);

// Нарисовать полупрозрачные прямоугольники


for (var i=0;i<10;i++){
ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
for (var j=0;j<4;j++){
ctx.fillRect(5+i*14,5+j*37.5,14,27.5);
} }
Стили линий
Есть несколько свойств, которые позволяют нам стилизовать линии.

lineWidth = value

Устанавливает ширину линий, рисуемых в будущем.

lineCap = type

Устанавливает внешний вид концов линий.

lineJoin = type

Устанавливает внешний вид «углов», где встречаются линии.


miterLimit = value
Устанавливает ограничение на митру, когда две линии соединяются под острым углом,
чтобы вы могли контролировать её толщину.

getLineDash()

Возвращает текущий массив тире штриховки, содержащий четное число неотрицательных


чисел.
setLineDash(segments)

Устанавливает текущий пунктир линии.

lineDashOffset = value

Указывает, где следует начинать тире массива в строке.


Вы лучше поймете, что они делают, глядя на приведенные ниже примеры.

Пример lineWidth
Это свойство задает толщину текущей строки. Значения должны быть положительными. По
умолчанию для этого значения установлено 1.0 единицы.
Ширина линии - это толщина хода, центрированного по данному пути. Другими словами,
область, которая нарисована, простирается до половины ширины линии по обе стороны пути.
Поскольку координаты холста не напрямую ссылаются на пиксели, особое внимание следует
уделять получению четких горизонтальных и вертикальных линий.
В приведенном ниже примере 10 прямых линий рисуются с увеличением ширины линий.
Линия в крайнем левом углу - 1.0 единицы. Тем не менее, толщина левой и всех других линий
нечетной ширины не выглядят четкими из-за позиционирования пути.

for (var i = 0; i < 10; i++){


ctx.lineWidth = 1+i;
ctx.beginPath();
ctx.moveTo(5+i*14,5);
ctx.lineTo(5+i*14,140);
ctx.stroke();
}

Получение четких строк требует понимания путей сглаживания. На рисунках ниже


представлена сетка координат холста. Квадраты между сетками являются фактическими
экранными пикселями. В первом изображении сетки ниже прямоугольник от (2, 1) до (5, 5)
заполняется. Вся область между ними (светло-красный) падает на границы пикселей, поэтому
полученный заполненный прямоугольник будет иметь четкие края.
Если вы рассмотрите путь от (3, 1) до (3, 5) с толщиной строки 1.0, вы получите ситуацию
во втором изображении. Фактическая заполняемая область, (синяя), распространяется только
наполовину в пикселях по обе стороны пути. Приблизительно это означает, что частично
затенённые пиксели приводят к заполнению всей области (светло-голубой и синей) цветом,
только наполовину темным, чем фактический цвет штриха. Это то, что происходит с линией
шириной 1.0 в предыдущем примере кода.
Чтобы исправить это, вы должны быть более точными при создании пути. Зная, что линия
шириной 1.0 занимает половину единицы по обе стороны пути, создание пути от (3.5, 1) до (3.5,
5) приведёт к ситуации в третьем изображении - ширина линии 1.0 закончится верно, точно
заполняя вертикальную линию с одним пикселем.

Примечание: Имейте в виду, что в нашем примере с вертикальной линией позиция Y по-
прежнему ссылается на целочисленную позицию сетки - иначе мы увидели бы пиксели с
половинным охватом в конечных точках (также обратите внимание, что это поведение зависит
от текущего стиля lineCap, значение по умолчанию - butt; вы можете вычислить согласованные
штрихи с полупиксельными координатами для линий с нечетной шириной, установив стиль
lineCap в square, чтобы внешняя граница вокруг конечной точки линии автоматически
расширялась, охватывая весь пиксель в точку).
Также обратите внимание, что затронуты только начальные и конечные точки пути: если
путь закрыт с помощью closePath(), - нет начальной и конечной точки; вместо этого все
конечные точки в пути подключены к их прикрепленному предыдущему и следующему
сегментам и при текущей настройке стиля lineJoin в значении по умолчанию - miter, с эффектом
автоматического расширения внешних границ подключенных сегментов до их точки
пересечения - обработанный ход будет точно покрывать полные пиксели с центром в каждой
конечной точке, если эти связанные сегменты горизонтальны и/или вертикальны). См.
следующие два раздела, демонстрирующие эти дополнительные стили.
Для линий с четной шириной каждая половина заканчивается как целое количество
пикселей, поэтому вам нужен путь, который находится между пикселями (то есть (3,1) - (3,5)),
вместо середины пикселей.
Хотя это и необычно, когда изначально работаешь с масштабируемой 2D-графикой,
обращая внимание на сетку пикселей и положение путей, но вы убедитесь, что ваши рисунки
будут выглядеть правильно, независимо от масштабирования или любых других
преобразований. Вертикальная линия ширины 1,0, построенная таким образом, станет четкой 2-
пиксельной линией при увеличении на 2 и появится в правильном положении.
Пример lineCap
Свойство lineCap определяет, как выводятся конечные точки каждой строки. Для этого
свойства есть три возможных значения: butt, round и square. По умолчанию для этого свойства
установлено значение butt.

butt
Концы линий соответствуют крайним точкам.
round
Концы линий округлены.
square
Концы линий описаны квадратом с равной шириной и половиной высоты толщины линии.
В этом примере мы проведем три строки, каждая из которых имеет другое значение для
свойства lineCap. Я также добавил два руководства, чтобы увидеть точные различия между
ними. Каждая из этих линий начинается и заканчивается именно на этих направляющих.
Строка слева использует butt опцию по умолчанию. Вы заметите, что она полностью
очищена от направляющих. Второй вариант - round опция. Это добавляет полукруг к концу,
который имеет радиус, равный половине ширины линии. Строка справа использует square
опцию. Это добавляет поле с равной шириной и половиной высоты толщины линии.

var lineCap = ['butt','round','square'];

// Draw guides
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(140,10);
ctx.moveTo(10,140);
ctx.lineTo(140,140);
ctx.stroke();
// Draw lines
ctx.strokeStyle = 'black';
for (var i=0;i<lineCap.length;i++){
ctx.lineWidth = 15;
ctx.lineCap = lineCap[i];
ctx.beginPath();
ctx.moveTo(25+i*50,10);
ctx.lineTo(25+i*50,140);
ctx.stroke();
}

Пример lineJoin
Свойство lineJoin определяет, как соединяются два сегмента (линий, дуг или кривых) с
ненулевой длиной в форме (вырожденные сегменты с нулевой длиной, заданные конечные
точки и контрольные точки находятся точно в том же положении - пропущены).
Для этого свойства есть три возможных значения: round, bevel и miter. По умолчанию для
этого свойства установлено значение miter. Обратите внимание, что настройка lineJoin не
действует, если два связанных сегмента имеют одно и то же направление, потому что в этом
случае не будет добавлена область соединения.
round
Радиус заполняемой части для скругленных углов равен половине ширины линии. центр
этого радиуса совпадает с концами подключенных сегментов.
bevel
Заполняет дополнительную треугольную область между общей конечной точкой
подключенных сегментов и отдельными внешними прямоугольными углами каждого сегмента.
miter
Подключенные сегменты соединяются путем расширения их внешних краев для
соединения в одной точке с эффектом заполнения дополнительной области в форме пастилки.
Эта настройка выполняется с помощью свойства miterLimit, которое объясняется ниже.
В приведенном ниже примере показаны три разных пути, демонстрирующие каждый из этих
трех свойств lineJoin; результат - выше.

var lineJoin = ['round','bevel','miter'];


ctx.lineWidth = 10;
for (var i=0;i<lineJoin.length;i++){
ctx.lineJoin = lineJoin[i];
ctx.beginPath();
ctx.moveTo(-5,5+i*40);
ctx.lineTo(35,45+i*40);
ctx.lineTo(75,5+i*40);
ctx.lineTo(115,45+i*40);
ctx.lineTo(155,5+i*40);
ctx.stroke();
}
Рисование текста
Контекст рендеринга canvas предоставляет два метода для рисования текста:

fillText(text, x, y [, maxWidth])

Вставляет заданный текст в положении (x,y). Опционально может быть указана


максимальная ширина.

strokeText(text, x, y [, maxWidth])

Вставляет контур заданного текста в положении (x,y). Опционально может быть указана
максимальная ширина.

Пример fillText
Текст вставлен с использованием текущего fillStyle.

ctx.font = "48px serif";


ctx.fillText("Hello world", 10, 50);

Пример strokeText
Текст вставлен с использованием текущего strokeStyle.
ctx.font = "48px serif";
ctx.strokeText("Hello world", 10, 50);

Стиль текста
В примерах выше мы уже использовали свойство font для изменения размера текста.
Кроме него существуют еще несколько свойств, позволяющие настроить вывод текста на
canvas:
font = value
Это основной стиль, который будет использоваться для вывода текста. Строка имеет такой
же синтаксис как CSS-свойство font. По умолчанию равно 10px sans-serif.
textAlign = value
Настройка выравнивания текста. Возможные значения: start, end, left, right or center. По
умолчанию равно start.
textBaseline = value
Настройка выравнивания текста по вертикали. Возможные значения: top, hanging, middle,
alphabetic, ideographic, bottom. По умолчанию равно alphabetic.
direction = value
Направление текста. Возможные значения: ltr, rtl, inherit. По умолчанию равно inherit.
Эти свойства могут быть вам знакомы если вы имеете опыт с CSS.
Изображение от WHATWG ниже показывает различные варианты свойства textBaseline.

Пример textBaseline
Редактируя код ниже вы можете видеть как меняется отображение текста на canvas в
реальном времени:

ctx.font = "48px serif";


ctx.textBaseline = "hanging";
ctx.strokeText("Hello world!", 0, 100);
Измерение текста
Для измерения ширины текста (без рисования его на canvas) можно воспользоваться
следующим методом:
measureText()
Возвращает объект TextMetrics, содержащий ширину текста в пикселах когда он будет
нарисован на canvas.
Пример ниже показывает как можно измерить ширину текста.

var text = ctx.measureText("foo"); // TextMetrics object


text.width; // 16;

Использование изображений
Для рисования изображения на canvas используется функция drawImage().

Использование изображений для рисования


Canvas API может использовать все перечисленные далее типы данных как источник
изображения:
HTMLImageElement
Эти изображения созданы, используя конструктор Image(), также как все<img> элементы.
HTMLVideoElement
Используя HTML <video> элемент как источник изображения захватывает текущий кадр из
видео и использует его как изображение.
HTMLCanvasElement
Вы можете использовать другой <canvas> элемент как источник изображения.
Эти источники совместно именуемые по типу CanvasImageSource.

Создание изображений с нуля


Другой способ это создать новые HTMLImageElement объекты в нашем скрипте. Чтобы это
сделать, вы можете использовать удобный Image() конструктор:

var img = new Image(); // Создает новый элемент изображения


img.src = 'myImage.png'; // Устанавливает путь

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


Если вы попытаетесь вызвать функцию drawImage() перед тем как изображение загрузится,
то скрипт ничего не сделает (или, в старых браузерах, может даже выдать исключение).
Поэтому вам необходимо использовать событие load, чтобы вы не пытались сделать это
прежде, чем изображение загрузится:

var img = new Image(); // Создает новое изображение


img.addEventListener("load", function() {
// здесь выполняет drawImage функцию
}, false);
img.src = 'myImage.png'; // Устанавливает источник файла

Если вы используете только одно стороннее изображение, то этот метод может быть
хорошим примером, но если нужно следить за несколькими изображениями, то этот способ не
работает.

Рисование изображений
Как только мы получили ссылку на источник объекта изображения, мы можем использовать
метод drawImage() для включения его в canvas. Как мы увидим далее, метод drawImage()
перегружен и у него есть несколько вариантов. В базовом варианте он выглядит как:

drawImage(image, x, y)

Рисует изображение, указанное в CanvasImageSource в координатах (x, y).

Пример простого линейного графика


В примере используется внешнее изображение в качестве фона для небольшого
линейного графика. На фоновом изображении, размером 180 Х 130 пкс, изображены оси
графика. Использование фонов может сделать скрипт значительно меньше, потому что можно
избежать необходимости писать код для создания фона. В примере используется один образ,
поэтому можно использовать обработчик событий для выполнения операторов рисования.
drawImage() метод определяющий место фона с координатами (0, 0), которые привязаны к
верхнему левому углу canvas.

var img = new Image();


img.onload = function(){
ctx.drawImage(img,0,0);
ctx.beginPath();
ctx.moveTo(30,96);
ctx.lineTo(70,66);
ctx.lineTo(103,76);
ctx.lineTo(170,15);
ctx.stroke();
};
img.src = 'backdrop.png';

Получившийся график выглядит так:


Задание 1.7.
В рамках вышеприведенного примера изменить фоновый рисунок (выполнить оцифровку
оси абсцисс) и повторно запустить скрипт на выполнение.

Изменение размеров
Второй вариант метода drawImage() добавляет два новых параметра и позволяет
разместить изображение в canvas с измененными размерами.
drawImage(image, x, y, width, height)
Это добавляет параметр ширины и высоты, которые указывают до какого размера нужно
изменить изображение при рисовании его в canvas.
Пример: Тайлинг изображения
В этом примере, мы будем использовать изображение в качестве обоев и повторим его в
canvas несколько раз. Это может быть сделано просто через цикл, располагая измененные
изображения на разных позициях. В коде внизу, первый цикл for проходит по рядам. Второй
цикл for проходит по колонкам. Изображение уменьшено на треть от реального размера,
которое было 50x38 пикселей.
Обратите внимание: Изображения могут стать размытыми, при большом увеличении или
зернистыми при значительном уменьшении. Возможно, лучше всего не изменять размеры
изображения, если на них есть текст, который должен остаться читаемым.

var img = new Image();


img.onload = function(){
for (var i=0;i<4;i++){
for (var j=0;j<3;j++){
ctx.drawImage(img,j*54,i*39,54,39);
}
}
};
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';

Получившийся рисунок canvas выглядит так:


Нарезка
У третьего и последнего варианта метода drawImage() в дополнении к источнику
изображения есть еще восемь параметров . Он позволяет нам вырезать кусок из изображения,
затем изменить его размер и нарисовать его в canvas.

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)


В данном изображении, эта функция берет фрагмент из изображения, в виде
прямоугольника, левый верхний угол которого - (sx, sy), ширина и высота - sWidth и sHeight и
рисует в canvas, располагая его в точке (dx, dy) и изменяя его размер на указанные величины
в dWidth и dHeight.
Чтобы понять что делает нарезка, можно посмотреть на изображение справа. Первые
четыре параметра определяют местоположение и размер фрагмента исходного изображения.
Последние четыре параметра определяют прямоугольник, в который будет вписано
изображение на целевом рисунке canvas.
Нарезка может быть полезным инструментом, когда вы захотите сделать композицию. Вы
могли бы собрать все элементы в одном файле изображения и использовать этот метод для
создания композиции. Например, если вы захотите сделать график, вы могли бы сделать PNG
изображение, содержащее все необходимые тексты в одном файле и в зависимости от ваших
данных, могли бы достаточно просто изменять график. Другим преимуществом является то, что
нет необходимости загружать каждое изображение по отдельности, получив возможность
увеличить скорость загрузки.

Transformations
Ранее мы узнали о сетке холста и координатном пространстве . До сих пор мы
использовали только сетку по умолчанию и изменили размер всего холста для наших нужд. При
преобразованиях существуют более мощные способы изменения исходных координат в
различные положение, поворот сетки и даже масштабирование.

Сохранение и восстановление состояния


Прежде чем перейти к методам преобразования, давайте рассмотрим два других метода,
которые необходимы, когда вы начинаете создавать все более сложные чертежи.
save()
Сохраняет все состояние холста.
restore()
Восстанавливает последнее сохраненное состояние холста.
Состояние холста сохраняется в стеке.
При вызове метода restore() последнее сохранённое состояние будет считано из стека и
все сохранённые настройки будут восстановлены.
Трансляция

Первый метод для трансформирования холста translate(). Он используется для


перемещения холста в любую точку нашей сетки.
translate(x, y)

Перемещение холста на сетке. x задет горизонтальную дистанцию смещения, и y задает


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

Пример перевода
Этот пример демонстрирует некоторые преимущества перевода происхождения холста.
Без метода translate () все прямоугольники были бы нарисованы в одной позиции (0,0). Метод
translate () также дает нам возможность разместить прямоугольник в любом месте холста без
необходимости вручную настраивать координаты в функции fillRect (). Это немного упрощает
понимание и использование.
В функции draw () мы вызываем функцию fillRect () девять раз, используя два цикла for. В
каждом цикле холст переводится, прямоугольник рисуется, а холст возвращается в исходное
состояние. Обратите внимание, как при вызове fillRect () каждый раз используются одни и те же
координаты, полагаясь на translate () для корректировки позиции рисования.

for (var i = 0; i < 3; i++) {


for (var j = 0; j < 3; j++) {
ctx.save();
ctx.fillStyle = 'rgb(' + (255 - 51 * i) + ', ' + (51 * i) + ', 120)';
ctx.translate(10 + i * 50, 10 + i * 50);
ctx.fillRect(0, 0, 25, 25);
ctx.restore();
}
}

Поворот
Второй метод трансформации rotate(). Используется он для поворота холста.

rotate(angle)

Поворачивает наш холст по часовой стрелке вокруг начальной точки на anglе число в
радианах.
Центр вращения всегда является исходной точкой холста. Чтобы изменить центральную
точку, нам нужно будет переместить холст с помощью метода translate ().
Пример поворота
В этом примере мы будем использовать метод rotate(), чтобы сначала повернуть
прямоугольник из начала холста, а затем из центра самого прямоугольника с помощью
translate().
Напоминание: углы указываются в радианах, а не в градусах. Для конвертации мы
используем: radians = (Math.PI/180)*degrees.

// левые прямоугольники, повернуть от начала холста


ctx.save();
// синий прямоугольник
ctx.fillStyle = '#0000FF';
ctx.fillRect(30, 30, 100, 100);
ctx.rotate((Math.PI / 180) * 25);
// красный прямоугольник
ctx.fillStyle = '#FF0000';
ctx.fillRect(30, 30, 100, 100);
ctx.restore();
// правые прямоугольники, повернуть от центра прямоугольника
// нарисовать синий прямоугольник
ctx.fillStyle = '#0000FF';
ctx.fillRect(150, 30, 100, 100);
ctx.translate(200, 80); // перевести в центр прямоугольника
ctx.rotate((Math.PI / 180) * 60); // вращать
ctx.translate(-200, -80); // перевести обратно
// нарисовать красный прямоугольник
ctx.fillStyle = '#FF0000';
ctx.fillRect(150, 30, 100, 100);

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


центр прямоугольника, затем вращаем холст, затем переводим холст обратно на 0,0, а затем
рисуем прямоугольник.

Масштабирование

Следующий метод трансформации - масштабирование. Мы используем его для увеличения


или уменьшения единиц в нашей сетке холста. Это можно использовать для рисования
уменьшенных или увеличенных фигур и растровых изображений.
scale (x, y)

Масштабирует единицы холста по x по горизонтали и по y по вертикали. Оба параметра


являются действительными числами. Значения меньше 1.0 уменьшают размер блока, а
значения выше 1.0 увеличивают размер блока. При значении 1.0 единицы остаются того же
размера.
Используя отрицательные числа, вы можете выполнить зеркальное отображение оси
(например, используя translate(0, canvas.height); scale (1, -1); у вас будет хорошо известная
декартова система координат с началом координат в нижнем левом углу).
По умолчанию одна единица на холсте равна ровно одному пикселю. Если мы применим,
например, коэффициент масштабирования 0,5, результирующая единица станет 0,5 пикселя, и
фигуры будут нарисованы с половинным размером. Аналогичным образом установка
коэффициента масштабирования на 2,0 увеличит размер единицы, и теперь одна единица
становится двумя пикселями. Это приводит к тому, что фигуры отображаются вдвое больше.
Пример анимации

<html>
<body>
<canvas id="canvas" width="300" height="60" style="border:3px dotted blueviolet"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var pos = {x:0, y:0};
ctx.strokeStyle = "#f00";
function draw() {
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.strokeRect(pos.x, pos.y, 20, 20);
ctx.stroke();
}
function update() {
if(pos.x < canvas.width - 20) pos.x += 4;
else pos.x = 0; // cбpоc кооpдинaты x
if(pos.y < canvas.height - 20) pos.y++;
else pos.y = 0;
draw();
}
setInterval(update, 100); // вызов update каждые 100 мсек
</script>
</body>
</html>

Результат

Задание 1.8.
Для вышеприведенного примера:
1. Прокомментировать операторы скрипта.
2. Объект анимации «квадрат» заменить на «круг».
3. Пунктирную рамку области движения заменить на сплошную.

Задание 2 Создание гистограммы

Начните с создания нового текстового файла с именем *.html и наберите в нём:

<html>
<body>
<canvas width="500" height="500" id="canvas"></canvas>
<script>
var data = [ 16, 68, 20, 30, 54 ];
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');

</script>
</body>
</html>
Страница выше содержит элементы <canvas> и <script>. Элемент <canvas> представляет
собой прямоугольник на экране, в котором будет происходить рисование. width и height
определяют, насколько он будет большой. <canvas> это блочный элемент похожий на <div>, так
что вы можете стилизовать его или позиционировать так же, как и всё остальное на странице.
Переменная data в скрипте хранит набор точек данных, которые мы отобразим в
гистограмме.
Далее определяем фон и размеры гистограммы.

c.fillStyle = "white";
c.fillRect(0,0,500,500);

С помощью следующей группы операторов прорисовываем саму гистограмму


c.fillStyle = "blue";
for(var i=0; i<data.length; i++) {
var dp = data[i];
c.fillRect(40 + i*100, 460-dp*5 , 50, dp*5);
}

Далее идут осевые линии.

c.fillStyle = "black";
c.lineWidth = 2.0;
c.beginPath();
c.moveTo(30,10);
c.lineTo(30,460);
c.lineTo(490,460);
c.stroke();
На следующем шаге оформляем вертикальную ось.

c.fillStyle = "black";
for(var i=0; i<6; i++) {
c.fillText((5-i)*20 + "",4, i*80+60);
c.beginPath();
c.moveTo(25,i*80+60);
c.lineTo(30,i*80+60);
c.stroke();
}
И наконец оформляем горизонтальную ось.

var labels = ["JAN","FEB","MAR","APR","MAY"];


for(var i=0; i<5; i++) {
c.fillText(labels[i], 50+ i*100, 475);
}
Теперь окончательно диаграмма выглядит так.

Провести эксперимент, связанный с изменением:


цвета столбцов и фона;
размеров столбцов;
оформления вертикальной и горизонтальной оси.

Задание 3 Создание круговой диаграммы


Теперь возьмём те же данные и нарисуем круговую диаграмму. Код очень похож.

<html>
<body>
<canvas width="500" height="500" id="canvas"></canvas>
<script>
var data = [ 100, 68, 20, 30, 100 ];
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');
c.fillStyle = "white";
c.fillRect(0,0,500,500);

</script>
</body>
</html>

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

var colors = [ "orange", "green", "blue", "yellow", "teal"]; // список цветов


var total = 0;
for(var i=0; i<data.length; i++) {
total += data[i];
}

Рисование настоящих секторов кажется сложным, но на самом деле это довольно легко.
Каждый сектор начинается в центре круга (250,250), затем рисуется дуга от предыдущего угла
до нового угла. Угол представляет собой данные конвертированные в радианы. Предыдущая
угол — это угол от предыдущей итерации цикла (начиная с 0). Дуга с центром в 250,250 имеет
радиус 100. Затем проводим линию обратно в центр, заливаем и обводим фигуру.

var prevAngle = 0;
for(var i=0; i<data.length; i++) {
var fraction = data[i]/total;
var angle = prevAngle + fraction*Math.PI*2;
c.fillStyle = colors[i];
c.beginPath();
c.moveTo(250,250);
c.arc(250,250, 100, prevAngle, angle, false);
c.lineTo(250,250);
c.fill();
c.strokeStyle = "black";
c.stroke();
prevAngle = angle;
}
Окончательно добавим текст ниже графика. Для центрирования текста необходимо
сначала рассчитать ширину текста:

c.fillStyle = "black";
c.font = "24pt sans-serif";
var text = "Sales Data from 2025";
var metrics = c.measureText(text);
c.fillText(text, 250-metrics.width/2, 400);

И вот как это выглядит:

Провести эксперимент, связанный с изменением:


цвета секторов, фона и обводки;
размеров секторов;
оформления пояснительного текста.

Задание 4 Создание симулятора частиц

Это всё, что на самом деле нужно для анимации. Рисовать что-то снова и снова.
Запрограммируем симулятор частиц. Мы хотим, чтобы некоторые частицы падали вниз по
экрану подобно снегу. Для этого мы реализуем классический алгоритм.
Начните с создания нового текстового файла с именем *.html и наберите в нём:

<!doctype html>
<html>
<body padding: 0; margin: 0; >
<canvas width="650" height="650" id="canvas"></canvas>
<script>
// слой с резервным вариантом setTimeout
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){ window.setTimeout(callback, 1000 / 60); }; }) ();
var canvas = document.getElementById('canvas');

</script>
</body>
</html>

Симулятор частиц содержит список зацикленных частиц. В каждом кадре положение всех
частиц обновляется, основываясь на некотором уравнении, при необходимости частицы
уничтожаются (создаются), на основе некоторого условия. Затем частицы рисуются.
Вначале мы создаём основу симулятора частиц. Это функция цикла, которая вызывается
каждые 30 мс. Для структуры данных нам требуется пустой массив частиц и счётчик тактов. На
каждой итерации цикла выполняется четыре части.

var particles = [];


var tick = 0;
function loop() {
window.requestAnimFrame(loop);
createParticles();
updateParticles();
drawParticles();
killParticles();
}

window.requestAnimFrame(loop);

Функция createParticles() проверяет, сколько у нас частиц. Если их меньше 100, то


создаётся новая частица. Обратите внимание, что проверка выполняется только каждые 10
тактов. Это позволяет начать с пустого экрана, а затем постепенно наращивать число частиц, а
не создавать все 100 с самого начала. Вы можете настроить параметры в зависимости от
желаемого эффекта. Я использую Math.random() и другую арифметику, чтобы убедиться, что
снежинки располагаются в разных местах и не выглядят одинаковыми. Так снег становится
более натуральным.

function createParticles() {
if(particles.length < 100) {
particles.push({
x: Math.random()*canvas.width,
y: 0,
speed: 2+Math.random()*3,
radius: 5+Math.random()*5,
color: "white",
});
}
}

Функция updateParticles() довольно простая. Она обновляет координату каждой частицы,


добавляя ей скорость. Это заставляет снежинку двигаться вниз по экрану.

function updateParticles() {
for(var i in particles) {
var part = particles[i];
part.y += part.speed;
}
}

Рисуем частицы. Опять же это очень просто: очистите фон, нарисуйте круг с текущими
координатами, радиусом и цветом частицы.

function drawParticles() {
var c = canvas.getContext('2d');
c.fillStyle = "black";
c.fillRect(0,0,canvas.width,canvas.height);
for(var i in particles) {
var part = particles[i];
c.beginPath();
c.arc(part.x,part.y, part.radius, 0, Math.PI*2);
c.closePath();
c.fillStyle = part.color;
c.fill();
}
}

Функция killParticles() проверяет что частица находится ниже нижнего края холста. В
некоторых симуляторах вы уничтожаете частицу и удаляете её из списка. Поскольку это
приложение показывает непрерывный снег, то мы повторно задействуем частицу, установив её
обратно в 0.

function killParticles() {
for(var i in particles) {
var part = particles[i];
if(part.y > canvas.height) {
part.y = 0;
}
}
}
Теперь это выглядит так.

Провести эксперимент, связанный с изменением:


цвета снежинок и фона;
диапазона размеров снежинок;
диапазона скорости падения снежинок;
количества генерируемых снежинок.

Вам также может понравиться