Применение стилей и цветов
В главе о рисовании фигур мы использовали только стандартные стили линий и заливки. Здесь мы изучим доступные возможности холста, чтобы сделать наши рисунки более привлекательными. Вы узнаете, как добавлять различные цвета, стили линий, градиенты, узоры и тени к своим рисункам.
Примечание: Содержимое холста недоступно для программ чтения с экрана. Если холст предназначен только для декорации, включите role="presentation" в открывающем <canvas> теге. В противном случае включите описательный текст в качестве значения атрибута aria-label непосредственно в элементе холста или включите резервный контент, помещённый в открывающий и закрывающий теги холста. Содержимое холста не является частью DOM, но вложенный резервный контент является.
Цвета
До сих пор мы видели только методы контекста рисования. Если мы хотим применить цвета к фигуре, есть две важные свойства, которые мы можем использовать: fillStyle и strokeStyle.
fillStyle = color-
Устанавливает стиль, используемый при заливке фигур.
strokeStyle = color-
Устанавливает стиль для контуров фигур.
color — это строка, представляющая CSS <color>, объект градиента или объект узора. Мы рассмотрим объекты градиента и узора позже. По умолчанию цвет обводки и заливки установлен на черный (CSS-значение цвета #000000).
Примечание: Когда вы устанавливаете свойство strokeStyle и/или fillStyle, новое значение становится значением по умолчанию для всех фигур, рисуемых с этого момента. Для каждой фигуры, которую вы хотите в другом цвете, вам потребуется повторно назначить свойство fillStyle или strokeStyle.
В соответствии со спецификацией, допустимые строки, которые вы можете ввести, должны быть CSS <color> значениями. Каждый из следующих примеров описывает один и тот же цвет.
// these all set the fillStyle to 'orange' ctx.fillStyle = "orange"; ctx.fillStyle = "#FFA500"; ctx.fillStyle = "rgb(255 165 0)"; ctx.fillStyle = "rgb(255 165 0 / 100%)";
Пример fillStyle
В этом примере мы снова используем два цикла for, чтобы нарисовать сетку прямоугольников, каждый в разных цветах. Полученное изображение должно быть похоже на скриншот. Здесь ничего особо впечатляющего нет. Мы используем две переменные i и j для генерации уникального цвета RGB для каждого квадрата, изменяя только значения красного и зеленого каналов. Синий канал имеет фиксированное значение. Изменяя каналы, вы можете генерировать всевозможные палитры. Увеличивая шаги, вы можете добиться чего-то, похожего на цветовые палитры Photoshop.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 6; i++) {
for (let 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);
}
}
}
Результат выглядит так:
Пример strokeStyle
Этот пример похож на предыдущий, но использует свойство strokeStyle для изменения цветов контуров фигур. Мы используем метод arc() для рисования окружностей вместо квадратов.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 6; i++) {
for (let 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, 2 * Math.PI, true);
ctx.stroke();
}
}
}
Результат выглядит так:
Прозрачность
Помимо рисования непрозрачных фигур на холсте, мы также можем рисовать полупрозрачные (или полупроницаемые) фигуры. Это делается путем установки свойства globalAlpha или путем присвоения полупрозрачного цвета стилю обводки и/или заливки.
globalAlpha = transparencyValue-
Применяет указанное значение прозрачности ко всем будущим фигурам, нарисованным на холсте. Значение должно быть между 0,0 (полностью прозрачное) и 1,0 (полностью непрозрачное). По умолчанию это значение равно 1,0 (полностью непрозрачное).
Свойство globalAlpha может быть полезным, если вы хотите нарисовать много фигур на холсте с подобной прозрачностью, но в остальных случаях обычно полезнее задавать прозрачность отдельных фигур при установке их цветов.
Поскольку свойства strokeStyle и fillStyle принимают CSS значения цвета RGB, мы можем использовать следующую нотацию для назначения прозрачного цвета.
// Assigning transparent colors to stroke and fill style ctx.strokeStyle = "rgb(255 0 0 / 50%)"; ctx.fillStyle = "rgb(255 0 0 / 50%)";
Функция rgb() имеет необязательный дополнительный параметр. Последний параметр устанавливает значение прозрачности этого конкретного цвета. Допустимый диапазон задан как процентное значение между 0% (полностью прозрачное) и 100% (полностью непрозрачное) или как число между 0.0 (эквивалентное 0% ) и 1.0 (эквивалентное 100%).
Пример globalAlpha
В этом примере мы нарисуем фон из четырех прямоугольников разных цветов. Над ними мы нарисуем набор полупрозрачных окружностей. Свойство globalAlpha установлено на 0.2, которое будет использоваться для всех фигур с этого момента. Каждый шаг в цикле for рисует набор окружностей с увеличивающимся радиусом. Конечный результат — радиальный градиент. Накладывая все больше окружностей друг на друга, мы эффективно уменьшаем прозрачность уже нарисованных окружностей. Увеличивая количество шагов и, фактически, рисуя больше окружностей, фон полностью исчезнет из центра изображения.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// draw background
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";
// set transparency value
ctx.globalAlpha = 0.2;
// Draw semi transparent circles
for (let i = 0; i < 7; i++) {
ctx.beginPath();
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
ctx.fill();
}
}
Пример использования rgb() с альфа-прозрачностью
Во втором примере мы делаем нечто подобное предыдущему, но вместо того, чтобы рисовать окружности друг на друга, я нарисовал маленькие прямоугольники с возрастающей непрозрачностью. Использование rgb() даёт вам немного больше контроля и гибкости, потому что мы можем задавать стиль заливки и обводки индивидуально.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Draw background
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);
// Draw semi transparent rectangles
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgb(255 255 255 / ${(i + 1) / 10})`;
for (let 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 единицы. Однако самые левые и все другие линии с шириной толщины, являющейся нечётным числом, не выглядят чёткими из-за расположения траектории.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let 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 . Это добавляет прямоугольник с шириной, равной ширине линии, и высотой, равной половине ширины линии.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// 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";
["butt", "round", "square"].forEach((lineCap, i) => {
ctx.lineWidth = 15;
ctx.lineCap = lineCap;
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 ; результат показан выше.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.lineWidth = 10;
["round", "bevel", "miter"].forEach((lineJoin, i) => {
ctx.lineJoin = lineJoin;
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();
});
}
Демонстрация свойства miterLimit
Как вы видели в предыдущем примере, при соединении двух линий с опцией miter, внешние края двух соединяющихся линий расширяются до точки их пересечения. Для линий, образующих большие углы друг с другом, эта точка находится не далеко от внутренней точки соединения. Однако, по мере уменьшения углов между каждой линией, расстояние (длина среза) между этими точками увеличивается в геометрической прогрессии.
Свойство miterLimit определяет, насколько далеко можно сместить внешнюю точку соединения от внутренней точки соединения. Если две линии превышают это значение, вместо соединения со срезом рисуется соединение с фаской. Обратите внимание, что максимальная длина среза — это произведение ширины линии, измеренной в текущей системе координат, на значение свойства miterLimit (значение по умолчанию — 10.0 в HTML <canvas>), поэтому значение miterLimit можно задавать независимо от текущего масштаба отображения или аффинных преобразований путей: оно влияет только на фактически отрисованную форму краев линий.
Более точно, предел среза — это максимальное допустимое соотношение длины расширения (в HTML-холсте, измеряется между внешним углом соединённых краёв линии и общей конечной точкой соединяющих сегментов, заданных в пути) к половине ширины линии. Его можно эквивалентно определить как максимальное допустимое соотношение расстояния между внутренней и внешней точками соединения краёв к общей ширине линии. Тогда оно равно косекансу половины минимального внутреннего угла соединяющих сегментов, ниже которого не будет отображаться соединение со срезом, а только соединение с фаской:
-
miterLimit= maxmiterLength/lineWidth= 1 / sin ( min θ / 2 ) - Значение предела среза по умолчанию 10.0 отсечёт все срезы для острых углов менее примерно 11 градусов.
- Предел среза, равный √2 ≈ 1,4142136 (округлено) отсечёт срезы для всех острых углов, сохраняя соединения со срезом только для тупых или прямых углов.
- Предел среза, равный 1.0, допустим, но отключит все срезы.
- Значения ниже 1.0 недопустимы для предела среза.
Вот небольшая демонстрация, в которой вы можете динамически задавать miterLimit и видеть, как это влияет на формы на холсте. Синие линии показывают начало и конец каждой линии в зигзагообразном узоре.
Если вы зададите значение miterLimit ниже 4.2 в этой демонстрации, ни один из видимых углов не соединится с расширением среза, а только с небольшой фаской около синих линий; при значении miterLimit выше 10 большинство углов в этой демонстрации должны соединиться с расширением среза, удалённым от синих линий, и высота которого уменьшается между углами слева направо, потому что они соединяются с возрастающими углами; при промежуточных значениях углы с левой стороны будут соединяться только с фаской вблизи синих линий, а углы с правой стороны — с расширением среза (также с уменьшающейся высотой).
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Clear canvas
ctx.clearRect(0, 0, 150, 150);
// Draw guides
ctx.strokeStyle = "#09f";
ctx.lineWidth = 2;
ctx.strokeRect(-5, 50, 160, 50);
// Set line styles
ctx.strokeStyle = "#000";
ctx.lineWidth = 10;
// check input
if (document.getElementById("miterLimit").checkValidity()) {
ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value);
}
// Draw lines
ctx.beginPath();
ctx.moveTo(0, 100);
for (let i = 0; i < 24; i++) {
const dy = i % 2 === 0 ? 25 : -25;
ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
}
ctx.stroke();
return false;
}
Использование штрихов линий
Метод setLineDash и свойство lineDashOffset задают шаблон штрихов для линий. Метод setLineDash принимает список чисел, которые определяют расстояния для чередующегося рисования линии и промежутка, а свойство lineDashOffset устанавливает смещение, с которого следует начинать шаблон.
В этом примере создаётся эффект «муравьиного марша». Это техника анимации, часто встречающаяся в инструментах выделения программ компьютерной графики. Она помогает пользователю различать границу выделения от фонового изображения путём анимации границы. В последующей части этого учебника вы узнаете, как это сделать, и другие основные анимации.
const ctx = document.getElementById("canvas").getContext("2d");
let offset = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setLineDash([4, 2]);
ctx.lineDashOffset = -offset;
ctx.strokeRect(10, 10, 100, 100);
}
function march() {
offset++;
if (offset > 5) {
offset = 0;
}
draw();
setTimeout(march, 20);
}
march();
Градиенты
Так же, как и в любой обычной программе рисования, мы можем заполнять и обводить фигуры с использованием линейных, радиальных и конических градиентов. Мы создаём объект CanvasGradient с помощью одного из следующих методов. Затем мы можем назначить этот объект свойствам fillStyle или strokeStyle.
createLinearGradient(x1, y1, x2, y2)-
Создаёт объект линейного градиента с начальной точкой (
x1,y1) и конечной точкой (x2,y2). createRadialGradient(x1, y1, r1, x2, y2, r2)-
Создаёт объект радиального градиента. Параметры представляют собой два круга, один с центром в (
x1,y1) и радиусомr1, а другой с центром в (x2,y2) и радиусомr2. createConicGradient(angle, x, y)-
Создаёт объект конического градиента с начальным углом
angleв радианах в позиции (x,y).
Например:
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150); const radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
После создания объекта CanvasGradient мы можем назначить цвета с помощью метода addColorStop().
gradient.addColorStop(position, color)-
Создаёт новую точку цвета для объекта
gradient. Значениеposition— число от 0.0 до 1.0, определяющее относительное положение цвета в градиенте, а аргументcolorдолжен быть строкой, представляющей CSS<color>, указывающей цвет, который градиент должен достичь в этой точке перехода.
Вы можете добавлять к градиенту столько точек цвета, сколько вам нужно. Ниже представлен очень простой линейный градиент от белого к чёрному.
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150); lineargradient.addColorStop(0, "white"); lineargradient.addColorStop(1, "black");
Пример createLinearGradient
В этом примере мы создадим два разных градиента. Как вы видите, и свойства strokeStyle и fillStyle могут принимать объект canvasGradient в качестве допустимого ввода.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const linGrad = ctx.createLinearGradient(0, 0, 0, 150);
linGrad.addColorStop(0, "#00ABEB");
linGrad.addColorStop(0.5, "#fff");
linGrad.addColorStop(0.5, "#26C000");
linGrad.addColorStop(1, "#fff");
const linGrad2 = ctx.createLinearGradient(0, 50, 0, 95);
linGrad2.addColorStop(0.5, "#000");
linGrad2.addColorStop(1, "rgb(0 0 0 / 0%)");
// assign gradients to fill and stroke styles
ctx.fillStyle = linGrad;
ctx.strokeStyle = linGrad2;
// draw shapes
ctx.fillRect(10, 10, 130, 130);
ctx.strokeRect(50, 50, 50, 50);
}
Первый — градиент фона. Как вы видите, мы назначили два цвета в одной и той же позиции. Вы делаете это, чтобы создать очень резкие цветовые переходы — в данном случае от белого к зеленому. Обычно порядок определения точек цвета не имеет значения, но в этом специальном случае это имеет значение. Если вы сохраняете назначения в нужном порядке, это не проблема.
Во втором градиенте мы не задали начальный цвет (в позиции 0.0), так как это не было строго необходимо, потому что он автоматически примет цвет следующей точки цвета. Таким образом, назначение чёрного цвета в позиции 0.5 автоматически делает градиент от начала до этой точки чёрным.
Пример createRadialGradient
В этом примере мы определим четыре разных радиальных градиента. Так как у нас есть контроль над начальной и конечной точками градиента, мы можем добиться более сложных эффектов, чем обычно в «классических» радиальных градиентах, которые мы видим, например, в Photoshop (то есть градиент с единственной центральной точкой, где градиент расширяется наружу по кругу).
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const radGrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
radGrad.addColorStop(0, "#A7D30C");
radGrad.addColorStop(0.9, "#019F62");
radGrad.addColorStop(1, "rgb(1 159 98 / 0%)");
const radGrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
radGrad2.addColorStop(0, "#FF5F98");
radGrad2.addColorStop(0.75, "#FF0188");
radGrad2.addColorStop(1, "rgb(255 1 136 / 0%)");
const radGrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
radGrad3.addColorStop(0, "#00C9FF");
radGrad3.addColorStop(0.8, "#00B5E2");
radGrad3.addColorStop(1, "rgb(0 201 255 / 0%)");
const radGrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
radGrad4.addColorStop(0, "#F4F201");
radGrad4.addColorStop(0.8, "#E4C700");
radGrad4.addColorStop(1, "rgb(228 199 0 / 0%)");
// draw shapes
ctx.fillStyle = radGrad4;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad3;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad2;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad;
ctx.fillRect(0, 0, 150, 150);
}
В этом случае мы немного сместили начальную точку от конечной, чтобы добиться сферического 3D-эффекта. Лучше всего стараться избегать перекрытия внутренних и внешних окружностей, так как это приводит к странным эффектам, которые трудно предсказать.
Последняя точка цвета в каждом из четырёх градиентов использует полностью прозрачный цвет. Если вы хотите плавный переход от этого к предыдущей точке цвета, оба цвета должны быть равны. Это не очень очевидно из кода, так как он использует два разных метода цвета CSS для демонстрации, но в первом градиенте #019F62 = rgb(1 159 98 / 100%).
Пример createConicGradient
В этом примере мы определим два разных конических градиента. Конический градиент отличается от радиального тем, что, вместо создания кругов, он вращается вокруг точки.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const conicGrad1 = ctx.createConicGradient(2, 62, 75);
conicGrad1.addColorStop(0, "#A7D30C");
conicGrad1.addColorStop(1, "#fff");
const conicGrad2 = ctx.createConicGradient(0, 187, 75);
// we multiply our values by Math.PI/180 to convert degrees to radians
conicGrad2.addColorStop(0, "black");
conicGrad2.addColorStop(0.25, "black");
conicGrad2.addColorStop(0.25, "white");
conicGrad2.addColorStop(0.5, "white");
conicGrad2.addColorStop(0.5, "black");
conicGrad2.addColorStop(0.75, "black");
conicGrad2.addColorStop(0.75, "white");
conicGrad2.addColorStop(1, "white");
// draw shapes
ctx.fillStyle = conicGrad1;
ctx.fillRect(12, 25, 100, 100);
ctx.fillStyle = conicGrad2;
ctx.fillRect(137, 25, 100, 100);
}
Первый градиент расположен в центре первого прямоугольника и переводит зелёную точку цвета в начальную точку в белую в конечную. Угол начинается с 2 радиан, что заметно из-за начальной/конечной линии, указывающей на юго-восток.
Второй градиент также расположен в центре своего второго прямоугольника. У него несколько точек цвета, чередующихся от чёрного к белому на каждой четверти поворота. Это даёт нам шахматный эффект.
Узоры
В одном из примеров на предыдущей странице мы использовали циклы для создания узора из изображений. Однако существует гораздо более простой метод: метод createPattern().
createPattern(image, type)-
Создаёт и возвращает новый объект узора холста.
imageявляется источником изображения (то есть, этоHTMLImageElement,SVGImageElement, другойHTMLCanvasElementилиOffscreenCanvas,HTMLVideoElementилиVideoFrame, илиImageBitmap).type— строка, указывающая, как использовать изображение.
Тип определяет, как использовать изображение для создания узора и должен быть одним из следующих строковых значений:
repeat-
Разворачивает изображение по вертикали и горизонтали.
repeat-x-
Разворачивает изображение по горизонтали, но не по вертикали.
repeat-y-
Разворачивает изображение по вертикали, но не по горизонтали.
no-repeat-
Не разворачивает изображение. Оно используется только один раз.
Мы используем этот метод для создания объекта CanvasPattern, который очень похож на методы градиента, которые мы видели выше. После создания узора мы можем назначить его свойствам fillStyle или strokeStyle. Например:
const img = new Image(); img.src = "some-image.png"; const pattern = ctx.createPattern(img, "repeat");
Примечание: Как и в методе drawImage(), необходимо убедиться, что изображение загружено перед вызовом этого метода, иначе узор может быть нарисован неправильно.
Пример использования createPattern
В этом последнем примере мы создадим узор, чтобы назначить его свойству fillStyle. Единственное, что стоит отметить, это использование обработчика изображения onload. Это необходимо, чтобы убедиться, что изображение загружено перед его назначением узору.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// create new image object to use as pattern
const img = new Image();
img.src = "canvas_create_pattern.png";
img.onload = () => {
// create pattern
const pattern = ctx.createPattern(img, "repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 150, 150);
};
}
Тени
Для использования теней необходимо всего четыре свойства:
shadowOffsetX = float-
Указывает горизонтальное расстояние, на которое тень должна сместиться от объекта. Это значение не изменяется матрицей преобразований. По умолчанию равно 0.
shadowOffsetY = float-
Указывает вертикальное расстояние, на которое тень должна сместиться от объекта. Это значение не изменяется матрицей преобразований. По умолчанию равно 0.
shadowBlur = float-
Указывает размер эффекта размытия; это значение не соответствует числу пикселей и не изменяется текущей матрицей преобразований. Значение по умолчанию — 0.
shadowColor = color-
Стандартное значение CSS цвета, указывающее цвет эффекта тени; по умолчанию — полностью прозрачный чёрный.
Свойства shadowOffsetX и shadowOffsetY указывают, на сколько тень должна сместиться от объекта по осям X и Y; эти значения не изменяются текущей матрицей преобразований. Используйте отрицательные значения, чтобы сместить тень вверх или влево, и положительные значения, чтобы сместить тень вниз или вправо. По умолчанию оба свойства равны 0.
Свойство shadowBlur указывает размер эффекта размытия; это значение не соответствует числу пикселей и не изменяется текущей матрицей преобразований. Значение по умолчанию — 0.
Свойство shadowColor — стандартное значение CSS цвета, указывающее цвет эффекта тени; по умолчанию — полностью прозрачный чёрный.
Примечание: Тени рисуются только для source-over операций композиции.
Пример с тенью текста
В этом примере рисуется строка текста с эффектом тени.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgb(0 0 0 / 50%)";
ctx.font = "20px Times New Roman";
ctx.fillStyle = "Black";
ctx.fillText("Sample String", 5, 30);
}
Мы рассмотрим свойство font и метод fillText в следующей главе о рисовании текста.
Правила заполнения холста
При использовании fill (или clip и isPointInPath) вы можете указать алгоритм правила заполнения, по которому определяется, находится ли точка внутри или вне пути, и, таким образом, заполняется ли она или нет. Это полезно, когда путь пересекает себя или вложен.
Возможны два значения:
nonzero-
Правило «не равно нулю» (правило о витках), которое является правилом по умолчанию.
evenodd
В этом примере используется правило evenodd.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.beginPath();
ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
ctx.fill("evenodd");
}
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors