Тег CANVAS: рисование на веб-странице (ч.2)
Продолжаем рассматривать канву HTML 5 - средство для программного рисования прямо на веб-странице.
8. Рисование сложных фигур
Но чтобы нарисовать более-менее сложную картинку, одних только прямоугольников недостаточно. Поэтому канва предоставляет нам средства для рисования сложных фигур, контур которых состоит из произвольного количества сегментов - прямых и кривых линий, соединённых друг с другом. Сейчас мы узнаем, как пользоваться этими средствами.
Вообще, все сложные фигуры рисуются в три этапа:
- Веб-браузер ставится в известность, что сейчас мы начнём рисовать контур сложной фигуры.
- Рисуются отдельные сегменты контура этой фигуры.
- Веб-браузер ставится в известность, что рисование контура закончено, и теперь фигура должна быть выведена на канву, возможно, с заливкой.
Рассмотрим все эти три этапа более подробно.
8.1. Начало рисования сложной фигуры
Сначала, как уже говорилось, нам следует поставить веб-барузер в известность о том, что сейчас мы начнём рисовать контур сложной фигуры. Для этого достаточно вызвать метод beginPath объекта CanvasRenderingContext2D. Этот метод не принимает параметров и не возвращает результата.
ctx1.beginPath();
Теперь мы можем начинать рисование контура сложной фигуры.
8.2. Перо. Перемещение пера
При рисовании контуров сложных фигур используется концепция пера. Перо - это воображаемый инструмент, с помощью которого и рисуются отдельные сегменты контура.
- Изначально перо находится в точке с координатами [0, 0], то есть в начале координат канвы (в её верхнем левом углу).
- Рисование любого сегмента начинается в точке, где в текущий момент находится перо.
- По окончании рисования сегмента перо окажется в его конечной точке. Из этой точки мы можем начать рисование следующего сегмента контура. (Из этого правла есть исключения, о которых мы обязательно поговорим.)
- Мы можем перемещать перо в произвольную точку канвы.
Чтобы переместить перо в другую точку канвы, следует вызвать метод moveTo объекта CanvasRenderingContext2D. Вот формат его вызова:
контекст рисования.moveTo(
горизонтальная координата,
вертикальная координата>
);
ата,
вертикальная координата>
);[/code]
Параметры этого метода указывают координаты точки, в которую должно быть помещено перо. Результата этот метод не возвращает.
[code]ctx1.moveTo(20, 20);[/code]
8.3. Рисование сегментов различной формы
Теперь рассмотрим, как рисуются разнообразные сегменты, составляющие контур сложной фигуры. К таким сегментам относятся прямые, дуги, кривые Безье различного типа и прямоугольники.
8.3.1. Рисование прямых линий
Прямые линии рисовать проще всего. Для этого используется метод lineTo объекта CanvasRenderingContext2D. Формат его вызова выглядит так:
[code]контекст рисования.lineTo(
горизонтальная координата,
вертикальная координата>
);[/code]
Прямая, что мы нарисуем, начнётся в точке, где сейчас располагается перо, и закончится в точке, чьи координаты указаны в параметрах этого метода. Результата он не возвращает.
[code]ctx1.moveTo(20, 20);
ctx1.lineTo(380, 280);[/code]
8.3.2. Рисование дуг
Дуги также рисуются довольно просто. Для этого применяется метод arc объекта CanvasRenderingContext2D. Вот формат его вызова:
[code]контекст рисования.arc(
горизонтальная координата центра,
вертикальная координата центра,
радиус,
начальный угол,
конечный угол,
направление рисования
);[/code]
- Первые два параметра указывают координаты центра дуги.
- Третий параметр указывает радиус дуги в виде числа в пикселах.
- Четвёртый и пятый параметры указывают начальный и конечный углы дуги в виде чисел в радианах. Эти углы отсчитываются от горизонтальной оси.
Чтобы пересчитать градусы в радианы, следует использовать выражение вида:
[code]величина в радианах = (Math.PI / 180) * величина в градусах;[/code]
Свойство PI объекта Math хранит значение числа "пи".
- Последний параметр указывает направление рисования души. Значение true указывает веб-браузеру нарисовать дугу против часовой стрелки, а значение false - по часовой стрелке.
Результата метод arc не возвращает.
Начальная точка рисуемой окружности будет находиться там, где окажется конечная точка отрезка, чья длина совпадает с радиусом дуги, повёрнутого на начальный угол по часовой стрелке относительно горизонтальной оси координат. Соответственно, конечная её точка будет находиться там, где окажется конечная точка отрезка, чья длина совпадает с радиусом дуги, повёрнутого уже на конечный угол также по часовой стрелке относительно горизонтальной оси координат.
Если перед рисованием дуги уже был нарисован хотя бы один сегмент, то веб-барузер сам соединит прямой линией точку, где в данный момент находится перо (то есть где закончилось рисование предыдущего сегмента), и начальную точку дуги.
[code]ctx1.arc(200, 150, 100, 0, Math.PI, false);[/code]
Это выражение рисует дугу, представляющую собой половину окружности.
[code]ctx1.arc(200, 150, 100, 0, 2 * Math.PI, false);[/code]
А это выражение рисует целую окружность.
8.3.3. Рисование кривых Безье
Кривые Безье - это линии особой формы, описываемые тремя или четырьмя точками: начальной, конечной и одной или двумя контрольными. Начальная и конечная точки, как и в случае прямой линии, задают начало и конец кривой Безье, а контрольные точки формируют касательные, определяющие форму этой кривой.
Рис. 1. Кривая Безье с двумя контрольными точками
Кривая Безье с двумя контрольными точками представлена на рис. 1. Сама она показана в виде толстой линии. Её начальная и конечная точки обозначены кружками, а контрольные точки - квадратиками. Через каждую контрольную точку и начальную и конечную точку кривой Безье проведены касательные (тонкие прямые линии) — они определяют форму кривой. Если мы мысленно переместим какую-либо из контрольных точек, то форма проведённой через неё касательной изменится, и, следовательно, кривая Безье примет другую форму.
Существует и другой вид кривых Безье — с одной контрольной точкой (рис. 2).
Рис. 2. Кривая Безье с одной контрольной точкой
Для рисования кривых Безье с двумя контрольными точками служит метод bezierCurveTo объекта CanvasRenderingContext2D. Формат его вызова выглядит так:
[code]контекст рисования.bezierCurveTo(
горизонтальная координата первой контрольной точки,
вертикальная координата первой контрольной точки,
горизонтальная координата второй контрольной точки,
вертикальная координата второй контрольной точки,
горизонтальная координата конечной точки,
вертикальная координата конечной точки
);[/code]
Назначение параметров этого метода понятно из их описания. Метод bezierCurveTo не возвращает результат.
Рисование кривой Безье начинается в той точке, где в данный момент находится перо. После рисования кривой перо устанавливается в её конечную точку.
[code]ctx1.moveTo(100, 100);
ctx1.bezierCurveTo(120, 80, 260, 100, 60, 220);[/code]
Рисование кривых Безье с одной контрольной точкой выполняет метод quadraticCurveTo объекта CanvasRenderingContext2D. Формат его вызова таков:
[code]контекст рисования.quadraticCurveTo(
горизонтальная координата контрольной точки,
вертикальная координата контрольной точки,
горизонтальная координата конечной точки,
вертикальная координата конечной точки
);[/code]
Назначение параметров этого метода понятно из их описания. Метод не возвращает результата.
И здесь рисование кривой Безье начинается в той точке, где в данный момент установлено перо. После рисования кривой перо устанавливается в её конечную точку.
[code]ctx1.moveTo(100, 100);
ctx1.quadraticCurveTo(250, 50, 200, 200);[/code]
8.3.4. Рисование прямоугольников
Чтобы нарисовать прямоугольник в качестве сегмента контура сложной фигуры, следует использовать метод rect объекта CanvasRenderingContext2D.
[code]контекст рисования.rect(
горизонтальная координата,
вертикальная координата,
ширина,
высота
);[/code]
Первые два параметра задают, соответственно, горизонтальную и вертикальную координаты верхнего левого угла рисуемого прямоугольника, а третий и четвёртый - его ширину и высоту. Результата этот метод не возвращает.
Метод rect стоит несколько особняком. Дело в том, что он не соединяет только что созданный прямоугольник с нарисованными ранее сегментами. То есть прямоугольник, что мы создали с помощью этого метода, скажем так, "повисает в пространстве".
После рисования прямоугольника перо будет установлено в его верхнюю левую точку.
[code]ctxCanvas.rect(50, 50, 100, 150);[/code]
8.3.5. Автоматические замыкание контура
Если мы собираемся нарисовать сложную фигуру с заливкой, то должны будем закрыть её контур, то есть соединить его конечную точку с начальной какой-либо линией. Мы можем сделать это сами, а можем "попросить" сделать это за нас веб-браузер.
Вызов метода closePath объекта CanvasRenderingContext2D автоматически закрывает контур прямой линией. Этот метод не принимает параметров и не возвращает результата.
[code]ctx1.closePath();[/code]
8.4. Завершение рисования сложной фигуры
Нарисовав контур сложной фигуры и, возможно, закрыв его, вручную или вызовом метода closePath, мы должны указать веб-браузеру, что рисование, собственно, закончено, и готовую фигуру следует вывести на канву. Для этого применяются два метода, которые мы сейчас рассмотрим.
Метод stroke объекта CanvasRenderingContext2D просто завершает рисование контура сложной фигуры. Он не принимает параметров и не возвращает результата.
[code]ctx1.stroke();[/code]
Метод fill объекта CanvasRenderingContext2D завершает рисование контура сложной фигуры и создаёт для неё заливку. Он также не принимает параметров и не возвращает результата.
[code]ctx1.fill();[/code]
8.5. Несколько примеров кода, рисующего сложные фигуры
Завершая разговор о принципах рисования на канве сложных фигур, давайте рассмотрим несколько примеров кода, создающих такие фигуры.
[code]ctx1.beginPath();
ctx1.moveTo(200, 20);
ctx1.lineTo(20, 280);
ctx1.lineTo(380, 280);
ctx1.closePath();
ctx1.stroke();[/code]
Этот код создаёт контур равностороннего треугольника. Отметим, что для его закрытия мы используем метод closePath - так проще.
[code]ctx1.beginPath();
ctx1.arc(200, 150, 150, 0, Math.PI * 2, false);
ctx1.moveTo(300, 150);
ctx1.arc(200, 150, 100, 0, Math.PI * 2, false);
ctx1.moveTo(250, 150);
ctx1.arc(200, 150, 50, 0, Math.PI * 2, false);
ctx1.stroke();[/code]
Этот код рисует три концентрические окружности без заливки.
Здесь у нас возникает проблема. Дело в том, что метод arc при рисовании дуги (а окружность - та же дуга) соединяет её начальную точку с точкой, где в данный момент находится перо, прямой линией. А перо после рисования предыдущей дуги окажется в её конечной точке. О начальных и конечных точках дуг см. в параграфе 8.3.2. В результате мы увидим в окне веб-браузера прямые линии, соединяющие начальные точки всех трёх окружностей.
Чтобы избежать этого, мы принудительно устанавливаем перо в то место на канве, где будут находиться начальные точки второй и третьей рисуемых нами окружностей; это делают вызовы метода moveTo в приведённом выше коде. Горизонтальная координата этих точек в нашем случае (то есть при нулевом начальном угле) будет равна сумме горизонтальной координаты центра окружности и её радиуса, а вертикальная координата будет равна вертикальной координате центра окружности.
[code]ctx1.beginPath();
ctx1.moveTo(100, 100);
ctx1.quadraticCurveTo(300, 100, 300, 300);
ctx1.lineTo(100, 300);
ctx1.lineTo(100, 100);
ctx1.fill();[/code]
Этот код рисует сектор окружности с заливкой, формируя его из кривой Безье с одной контрольной точкой и двух прямых линий.
[code]ctx1.beginPath();
ctx1.moveTo(20, 0);
ctx1.lineTo(280, 0);
ctx1.quadraticCurveTo(300, 0, 300, 20);
ctx1.lineTo(300, 180);
ctx1.quadraticCurveTo(300, 200, 280, 200);
ctx1.lineTo(20, 200);
ctx1.quadraticCurveTo(0, 200, 0, 180);
ctx1.lineTo(0, 20);
ctx1.quadraticCurveTo(0, 0, 20, 0);
ctx1.stroke();[/code]
А этот код рисует прямоугольник со скруглёнными углами без заливки. Для создания скруглённых углов используются кривые Безье с одной контрольной точкой.
Продолжение следует...
Февраль 2011