Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development
Code

Изучение Canvas с нуля: Продвинутые методы рисования

by
Difficulty:IntermediateLength:MediumLanguages:
This post is part of a series called Canvas From Scratch.
Canvas From Scratch: Introducing Canvas
Canvas From Scratch: Transformations and Gradients

Russian (Pусский) translation by AlexBioJS (you can also view the original English article)

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


Подготавливаем страницу

Мы будем использовать тот же самый шаблон HTML, что и в предыдущем руководстве, поэтому откройте свой любимый редактор и вставьте туда следующий код:

Это всего лишь базовая HTML-страница с элементом canvas и некоторым кодом JavaScript, который выполняется после загрузки модели DOM (* Document Object Model – Объектная модель документа. Здесь и далее примеч. пер.). Ничего фантастического тут нет.


Рисуем круги

В последнем руководстве я показал вам, как рисовать базовые фигуры и пути (* последовательность линий на «холсте»); в этой статье я покажу вам, как справиться с более сложной задачей – рисованием кругов. Это не так просто, как могло бы показаться, однако при этом вовсе ничего сложного нет.

В Сanvas нет метода, который позволяет вам нарисовать круг при помощи единственной строчки кода, как в случае с рисованием прямоугольников (метод fillRect). Вместо этого вы должны рисовать круги при помощи пути, прорисовываемого с помощью метода arc; круг – всего лишь дуга, величина угла которой составляет 360 градусов. Это так, поскольку круги – очень сложные фигуры, и в методе arc предусмотрено все необходимое для рисования любых его сегментов. Например вы могли бы захотеть нарисовать только полукруг. Метод arc позволяет вам это осуществить. Вы бы могли даже совместно использовать метод arc и стандартные прямые пути для прорисовывания кусочков пиццы и четвертей круга.

Я объясню вам скоро, как работает метод arc, но пока давайте нарисуем круг за счет добавления следующего кода ниже переменной ctx:

За счет него будет нарисован круг, расположенный немного далее верхнего левого угла «полотна»:

Вроде все просто, не так ли? Так оно и есть, однако давайте рассмотрим более детально, что происходит.

Метод arc всего принимает шесть аргументов:

  • Первый – координата x исходной точки (центр круга).
  • Второй – координата y исходной точки.
  • Третий – радиус круга.
  • Четвертый – начальный угол круга.
  • Пятый – конечный угол круга.
  • И шестой – направление, в котором будет прорисован круг (если true, то круг будет прорисован против часовой стрелки, если false – по часовой стрелке).

В псевдокоде (* язык, напоминающий императивный язык программирования и используемый в качестве нотации для описания алгоритмов и/или структуры программы) метод arc с его аргументами выглядел бы следующим образом:

Первые три аргумента говорят сами за себя, как и последний, однако как насчет начального и конечного углов? Давайте поясню.

Как я упомянул ранее, круги – просто дуги, величина угла которых составляет 360 градусов. В Сanvas дугой считается кривая линия, которая начинается на некотором расстоянии от исходной точки, то есть на расстоянии, равном величине радиуса. Кривая начинается с угла, значение которого определяется при помощи аргумента для задания стартового угла (четвертого), и идет вдоль окружности воображаемого круга до тех пор, пока не достигнет угла, значение которого определяется при помощи аргумента для задания конечного угла (пятого). Звучит просто, не так ли?

Возможно, на иллюстрации будет понятнее:

Должно быть, это выглядит странновато, однако все становится на свои места, как только вы поймете эту концепцию.

Углы в Сanvas

На данном этапе, думаю, следует отметить, что углы в Сanvas измеряются в радианах (* плоский угол, который образуется, если соединить с центром круга концы дуги на его окружности, равной радиусу этого круга. Так как круг содержит 360 градусов, или 2π радиан, то радиан приблизительно равен 57,2958 градусов), а не градусах. Это означает, что углы измеряются от 0 до pi, помноженного на два. Отсчет углов в Сanvas также начинают с правой стороны, что проиллюстрировано на следующем изображении:

Если вы совсем недолюбливаете радианы, то вы можете пересчитать градусы в радианы при помощи следующей формулы на JavaScript:

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


Кривые Безье (* названа по имени её автора, французского математика Пьера Безье (Pierre Bézier), который работал в компании Рено и занимался моделированием поверхностей кузовов автомобилей. Относится к классу кубических функций.  Строится с использованием минимум четырёх точек: двух базовых - начала и конца - и двух промежуточных   (называемых управляющими метками-манипуляторами, handle), которые влияют на форму кривой, но обычно не лежат на ней непосредственно)

Работать с дугами весело и все такое, но их потенциал довольно ограничен для реализации тех кривых, которые могут быть созданы при помощи Canvas. Для реализации более сложных кривых мы обращаемся к методам quadraticCurveTo и bezierCurveTo для создания кривых Безье. Эти методы позволяют вам создавать искривленные пути с радиусом, который идет не от центра кривой, а также создавать пути со множеством кривых.

Для указания того, где и как прорисовать кривые Безье, используются управляющие метки-манипуляторы. Например в методе quadraticCurveTo используется одна метка-манипулятор, в то время как в bezierCurveTo – две. Ознакомьтесь со следующей иллюстрацией, чтобы понять, как эти метки-манипуляторы влияют на вид кривой:

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

Давайте перейдем к написанию кода и создадим квадратичную кривую Безье (задается тремя опорными точками). Замените код для рисования дуги следующим:

В результате выполнения этого кода будет прорисован кривой путь, который выглядит подобно тому, что был показан на иллюстрации выше:

Метод quadraticCurveTo принимает четыре аргумента:

  • Первый – координата х метки-манипулятора.
  • Второй – координата у метки-манипулятора.
  • Третий – координата х конечной опорной точки пути.
  • Четвертый – координата у конечной опорной точки пути.

В псевдокоде метод quadraticCurveTo выглядел бы следующим образом:

Стартовая точка кривой – та, в которой в данный момент находится конечная точка пути. Например в коде выше мы переместили стартовую точку пути при помощи вызова метода moveTo.

Давайте усложним задачу и создадим кубическую кривую Безье. Замените предыдущий код следующим:

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

Метод bezierCurveTo принимает шесть аргументов:

  • Первый – координата x первой метки-манипулятора.
  • Второй – координата у первой метки-манипулятора.
  • Третий – координата х второй метки-манипулятора.
  • Четвертый – координата у второй метки-манипулятора.
  • Пятый – координата х конечной опорной точки пути.
  • И шестой – координата у конечной опорной точки пути.

В псевдокоде метод quadraticCurveTo выглядел бы следующим образом:

Сами по себе кривые Безье не особо поразительны, однако при совместном использовании с обычными путями или при их повторном использовании можно получить довольно впечатляющий результат. Они позволяют вам создавать все варианты сложных и великолепных фигур в Сanvas!

Возможно, вам интересно было бы ознакомиться с плагином Ai->Canvas для Adobe Illustrator, который позволяет вам экспортировать ваш фантастическый векторный рисунок в виде кода Сanvas. Он довольно клевый и сэкономит вам уйму времени!


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

В предыдущем руководстве этой серии я подробно описал, как изменить стилевое оформление контуров и заполненных цветом графических объектов и как изменить толщину линии. Одна из проблем, о которой стоит знать, возникающих при изменении этих свойств, - то, что вы должны будете вручную изменить цвета и ширину линии обратно, если хотите снова использовать изначально заданные цвета и ширину линии. К счастью, как всегда, имеется лучший способ это осуществить; он осуществляется при помощи состояния рисования.

Состояние рисования в Сanvas – по сути элемент стека (* структура данных – список, добавление и удаление элементов к которому осуществляется по принципу LIFO (Last In First Out –  "последним пришёл – первым обслужен")), в котором вы можете сохранять текущие стили и затем снова ими воспользоваться позднее.

Это обманчиво простая концепция, однако при полном ее понимании перед вами открываются потрясающие возможности. В действительности, состояние рисования содержит огромный массив визуальной информации о холсте, например о матрице преобразования (* матрица, содержащая коэффициенты уравнений, по которым выполняется преобразование), вырезанной области и следующих свойствах: globalAlpha, globalCompositeOperation, strokeStyle, fillStyle, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, font, textAlign и textBaseline. Большинство из них будут новыми для вас, так что не переживайте. Вы научитесь работать с трансформациями и другими клевыми возможностями вроде добавления теней в следующем руководстве.

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

Использовать состояние рисования чрезвычайно просто, однако для полного понимания этой концепции может потребоваться некоторое время. Замените код, добавленный в предыдущем разделе, следующим:

Это и все, что вам нужно для сохранения состояния рисования, – единственный вызов метода save. Я же говорил, это просто!

Здесь мы изменяем стилевое оформление заполненных цветом графических объектов canvas на синий цвет и затем сохраняем состояние рисования, за счет чего текущее состояние добавляется в стек, о котором я говорил ранее. По умолчанию стек пуст.

Важно помнить, что стек работает прямо как пачка бумаг на вашем столе; первый элемент стека расположен снизу, а наиболее новый – на верхушке. Если вы снова хотите добраться до первого элемента, то вам для начала придется убрать все элементы поверх него. Эта система носит название FILO (* First In Last Out – "первым пришёл - последним обслужен") или LIFO, если взглянуть на нее с другой стороны.

Возобновление состояния рисования

Наличие возможности сохранить состояние рисования – отлично, однако наличие возможности собственно им вновь воспользоваться – по сути более полезно. Для этого мы будем использовать метод restore.

Добавьте следующий код к коду, добавленному выше:

В результате будет нарисован еще один прямоугольник на полотне, однако в этот раз другого цвета (красного):

Пока что все, как обычно, но что если вы хотите вернуться к использованию синего цвета и нарисовать другой прямоугольник? Что ж, вы могли бы задать в качестве значения стилевого оформления заполненных цветом графических объектов вручную синий цвет, однако это было бы утомительно. Давайте попробуем воспользоваться методом restore и посмотрим, что получится.

Добавьте следующий код:

В результате будет нарисован еще один прямоугольник, однако в этот раз изначального цвета:

Насколько это было просто? За счет вызова restore произошло извлечение и удаление последнего состояния рисования, добавленного в стек, и последующее применение его к canvas, в результате чего вы экономите множество времени. Ладно, что ж, возможно в этом примере вы не сэкономили уйму времени, однако сэкономили бы, если бы изменили значения всевозможных свойств и применили трансформации на полотне.

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

Теперь вы знаете, как использовать состояние рисования для единственного случая, но что произойдет при сохранении множества состояний рисования? Внимательные, должно быть, помнят, что я сослался на стек, как на кипу бумаг – последним пришел, первым ушел. Давайте посмотрим, как это реализуется в коде.

Обновите предыдущий код для добавления сохранения состояния рисования после установления в качестве значения стилевого оформления заполненных цветом графических объектов красного цвета:

Несмотря на то что это почти тот же самый код, как и ранее, все будет иначе, поскольку последнее состояние рисования содержит в качестве значения свойства fillStyle красный цвет:

Для того чтобы восстановить изначальное состояние (синий цвет стилевого оформления заполненных цветом графических объектов), вам будет необходимо вызвать метод restore еще раз, так что добавьте следующий код:

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

При помощи использования множества состояний рисования вроде этого вы можете сберечь уйму времени. Это довольно находчиво!


Подведение итогов

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

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

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.