Академический Документы
Профессиональный Документы
Культура Документы
Канвас реализуется с помощью нового тега <canvas>, который появился в HTML5. Для
начала работы необходимо подготовить поле для рисования. Это поле делается с помощью
тега <canvas>, которому следует указать ширину и высоту (укажем также и id, чтобы легко
можно было обратиться к нему через JavaScript):
Затем нужно получить поле для рисования с помощью функции getElementById(). Функция
возвращает ссылку на элемент по его идентификатору (ID). Идентификатор может быть
определен при помощи атрибута id в HTML или из скрипта. Например:
Переменная 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');
}
}
</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:
ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
Задание 1.2.
Пояснить алгоритм прорисовки данного квадрата.
Рисование контуров (path)
Остальные примитивные фигуры создаются контурами. Контур - это набор точек, которые,
соединяясь в отрезки линий, могут образовывать различные фигуры, изогнутые или нет, разной
ширины и разного цвета. Контур (или субконтур) может быть закрытым.
Создание фигур, используя контуры, происходит в несколько важных шагов:
Сначала вы создаете контур.
Затем, используя команды рисования, рисуете контур.
Потом закрываете контур.
Созданный контур можно обвести или залить для его отображения.
Здесь приведены функции, которые можно использовать в описанных шагах:
beginPath()
closePath()
Закрывает контур, так что будущие команды рисования вновь направлены контекст.
stroke()
fill()
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();
Результат
Передвижение пера
Одна очень полезная функция, которая ничего не рисует, но связана по смыслу с
вышеописанными функциями - это moveTo(). Вы можете представить это как отрыв (подъем)
пера от бумаги и его перемещение в другое место.
moveTo(x, y)
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();
Задание 1.3.
Прокомментируйте все операторы фрагмента функции draw() и внесите изменения во
фрагмент так, чтобы линия прорисовки смайлика была непрерывной.
Линии
Для рисования прямых линий используйте метод lineTo().
lineTo(x, y)
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 определяют начальную и конечную точки дуги в радианах
вдоль кривой окружности. Отсчет происходит от оси 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)
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) заданного прямоугольника к последнему открытому контуру.
Добавляет в path прямоугольник, верхний левый угол которого указан с помощью (x, y) с
вашими width и height
Когда этот метод вызван, автоматически вызывается метод moveTo() с параметрами (x, y).
Другими словами, позиция курсора устанавливается в начало добавленного прямоугольника.
fillStyle = color
strokeStyle = color
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";
Пример fillStyle
В этом примере воспользуемся двойным циклом, чтобы нарисовать сетку из
прямоугольников, каждый из которых имеет свой цвет. Окончательное изображение должно
иметь вид, как показано на скриншоте. Здесь не происходит ничего сверхъестественного. Мы
используем две переменные i и j для генерации уникального RGB цвета для каждого квадрата и
изменяем только красные и зеленые значения. Синий канал представляет собой
фиксированное значение. Путем изменения каналов вы можете генерировать всю палитру.
Увеличив количество шагов вы можете достигнуть такого вида палитры, какая используется в
Photoshop.
Задание 1.5.
В вышеприведенном примере установить шаг сетки в два раза больше чем есть.
Пример strokeStyle
Этот пример похож на предыдущий, но мы используем свойство strokeStyle чтобы изменить
цвета очертаний фигур. Так же мы используем метод arc() для рисования окружностей вместо
квадратов.
Задание 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)";
Пример 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();
}
// Нарисовать фон
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);
lineWidth = value
lineCap = type
lineJoin = type
getLineDash()
lineDashOffset = value
Пример lineWidth
Это свойство задает толщину текущей строки. Значения должны быть положительными. По
умолчанию для этого значения установлено 1.0 единицы.
Ширина линии - это толщина хода, центрированного по данному пути. Другими словами,
область, которая нарисована, простирается до половины ширины линии по обе стороны пути.
Поскольку координаты холста не напрямую ссылаются на пиксели, особое внимание следует
уделять получению четких горизонтальных и вертикальных линий.
В приведенном ниже примере 10 прямых линий рисуются с увеличением ширины линий.
Линия в крайнем левом углу - 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
опцию. Это добавляет поле с равной шириной и половиной высоты толщины линии.
// 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; результат - выше.
fillText(text, x, y [, maxWidth])
strokeText(text, x, y [, maxWidth])
Вставляет контур заданного текста в положении (x,y). Опционально может быть указана
максимальная ширина.
Пример fillText
Текст вставлен с использованием текущего fillStyle.
Пример 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 в
реальном времени:
Использование изображений
Для рисования изображения на canvas используется функция drawImage().
Если вы используете только одно стороннее изображение, то этот метод может быть
хорошим примером, но если нужно следить за несколькими изображениями, то этот способ не
работает.
Рисование изображений
Как только мы получили ссылку на источник объекта изображения, мы можем использовать
метод drawImage() для включения его в canvas. Как мы увидим далее, метод drawImage()
перегружен и у него есть несколько вариантов. В базовом варианте он выглядит как:
drawImage(image, x, y)
Изменение размеров
Второй вариант метода drawImage() добавляет два новых параметра и позволяет
разместить изображение в canvas с измененными размерами.
drawImage(image, x, y, width, height)
Это добавляет параметр ширины и высоты, которые указывают до какого размера нужно
изменить изображение при рисовании его в canvas.
Пример: Тайлинг изображения
В этом примере, мы будем использовать изображение в качестве обоев и повторим его в
canvas несколько раз. Это может быть сделано просто через цикл, располагая измененные
изображения на разных позициях. В коде внизу, первый цикл for проходит по рядам. Второй
цикл for проходит по колонкам. Изображение уменьшено на треть от реального размера,
которое было 50x38 пикселей.
Обратите внимание: Изображения могут стать размытыми, при большом увеличении или
зернистыми при значительном уменьшении. Возможно, лучше всего не изменять размеры
изображения, если на них есть текст, который должен остаться читаемым.
Transformations
Ранее мы узнали о сетке холста и координатном пространстве . До сих пор мы
использовали только сетку по умолчанию и изменили размер всего холста для наших нужд. При
преобразованиях существуют более мощные способы изменения исходных координат в
различные положение, поворот сетки и даже масштабирование.
Пример перевода
Этот пример демонстрирует некоторые преимущества перевода происхождения холста.
Без метода translate () все прямоугольники были бы нарисованы в одной позиции (0,0). Метод
translate () также дает нам возможность разместить прямоугольник в любом месте холста без
необходимости вручную настраивать координаты в функции fillRect (). Это немного упрощает
понимание и использование.
В функции draw () мы вызываем функцию fillRect () девять раз, используя два цикла for. В
каждом цикле холст переводится, прямоугольник рисуется, а холст возвращается в исходное
состояние. Обратите внимание, как при вызове fillRect () каждый раз используются одни и те же
координаты, полагаясь на translate () для корректировки позиции рисования.
Поворот
Второй метод трансформации rotate(). Используется он для поворота холста.
rotate(angle)
Поворачивает наш холст по часовой стрелке вокруг начальной точки на anglе число в
радианах.
Центр вращения всегда является исходной точкой холста. Чтобы изменить центральную
точку, нам нужно будет переместить холст с помощью метода translate ().
Пример поворота
В этом примере мы будем использовать метод rotate(), чтобы сначала повернуть
прямоугольник из начала холста, а затем из центра самого прямоугольника с помощью
translate().
Напоминание: углы указываются в радианах, а не в градусах. Для конвертации мы
используем: radians = (Math.PI/180)*degrees.
Масштабирование
<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. Пунктирную рамку области движения заменить на сплошную.
<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 = "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();
}
И наконец оформляем горизонтальную ось.
<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>
Теперь добавьте список цветов (один для каждой точки данных) и вычислим суммарное
значение всех данных.
Рисование настоящих секторов кажется сложным, но на самом деле это довольно легко.
Каждый сектор начинается в центре круга (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);
Это всё, что на самом деле нужно для анимации. Рисовать что-то снова и снова.
Запрограммируем симулятор частиц. Мы хотим, чтобы некоторые частицы падали вниз по
экрану подобно снегу. Для этого мы реализуем классический алгоритм.
Начните с создания нового текстового файла с именем *.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 мс. Для структуры данных нам требуется пустой массив частиц и счётчик тактов. На
каждой итерации цикла выполняется четыре части.
window.requestAnimFrame(loop);
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",
});
}
}
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;
}
}
}
Теперь это выглядит так.