HTML5 Canvas - трансформации и прозрачность

184

Трансформации

Трансформация — это прием рисования, позволяющий перемещать систему координат холста. Допустим, нам нужно нарисовать один и тот же квадрат в трех местах холста. Это можно сделать, вызвав метод fillRect() три раза, каждый раз с другими координатами (модернизируем пример из предыдущей статьи, где задавались переменные canvas и context):

// Рисуем квадрат размером 30x30 пикселов в трех разных местах
context.rect(0,0,30,30);
context.rect(50,50,30,30);
context.rect(100,100,30,30);
context.stroke();

Тот же квадрат в трех разных местах можно нарисовать, используя те же его координаты, но сдвигая систему координат таким образом:

 // Рисуем квадрат с левым верхним углом в (0,0) 
context.rect(0,0,30,30);

// Сдвигаем систему координат вниз и вправо на 50 пикселов
context.translate(50,50);
context.rect(0,0,30,30);

// Сдвигаем систему координат вниз и вправо ещё на 50 пикселов.
// Преобразования суммируются, поэтому точка (0,0) 
// в действительности находится в точке (100, 100)
context.translate(50,50);
context.rect(0,0,30,30);
context.stroke();

Оба варианта кода дают одинаковый результат: рисуют три квадрата в трех разных местах холста.

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

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

Существует несколько разных типов трансформаций. В предыдущем примере мы использовали трансляцию, или перенос (translation transform), чтобы переместить начальную точку системы координат, т.е. точку (0,0) в левом верхнем углу холста.

Кроме трансляции, существуют такие трансформации, как масштабирование (scale transform), которая позволяет рисовать в увеличенном или уменьшенном масштабе; вращение (rotate transform), позволяющая вращать систему координат; и матричная трансформация (matrix transform), позволяющая растягивать и сгибать систему координат практически как угодно при условии, что вы понимаете сложную матричную математику в основе требуемых визуальных эффектов.

Трансформации обладают эффектом аккумулирования (сложения). В следующем примере начало координат (т.е. точка (0,0)) перемещается в точку (100,100) посредством трансляции, после чего система координат поворачивается несколько раз на определенный угол вокруг этой точки. При каждом вращении рисуется новый квадрат, создавая интересную фигуру:

// Перемещаем точку (0,0) . Это важный шаг, 
// т.к. вращение выполняется вокруг этой точки
context.translate(100, 100);

// Рисуем 9 квадратов
var copies = 10;
for (var i=1; i<copies; i++) {
    // Прежде чем рисовать следующий квадрат, вращаем систему координат. 
	// Угол полного оборота составляет 2*Math.PI. Этот код вращает систему 
	// координат для каждого нового квадрата лишь на долю этого угла, 
	// выполняя полный оборот при рисовании последнего квадрата,
    context.rotate(2 * Math.PI * 1/(copies-1));

    // Рисуем квадрат
    context.rect(0, 0, 60, 60);
  }
context.strokeStyle = "#109bfc";
context.stroke();
Трансформации Canvas

С помощью метода контекста save() можно сохранить текущее состояние системы координат и восстановить его позже посредством метода restored(). Сохраняйте состояние контекста перед тем, как применять трансформацию, чтобы в случае ошибки можно было восстановить систему координат. А при рисовании сложных фигур, требующих длинной последовательности шагов, разумно сохранять состояние как можно чаще. Этот список сохраненных состояний действует подобно истории посещенных веб-страниц. При каждом вызове метода restore() система возвращается в непосредственно предшествующее состояние.

Прозрачность

До сих пор мы имели дело с чистыми цветами. Но холст также позволяет применять частичную прозрачность, чтобы накладывать одну фигуру поверх другой. Существуют два способа использования прозрачности. Первый позволяет установить цвет, присвоив значения свойству fillStyle или strokeStyle и используя функцию rgba(), а не более распространенную функцию rgb().

Функция rgba() принимает три параметра — значения красной, зеленой и синей цветовых составляющих (от 0 до 255) и дополнительное значение альфа-канала (от 0 до 1), которое указывает прозрачность цвета. Значение альфа, равное 1, соответствует полной непрозрачности, а значение, равное 0 — полной прозрачности. Установив значение альфа между этими двумя пределами (например, 0.5), мы получим частично прозрачный цвет, позволяющий просматривать любое находящееся под ним содержимое.

Местонахождение содержимого снизу или сверху — определяется порядком операций рисования. Например, если в одном и том же месте сначала нарисовать круг, а потом квадрат, квадрат будет наложен на круг.

В следующем листинге приведен код для рисования круга и треугольника. Обе фигуры закрашиваются одним и тем же цветом, только для треугольника значение альфа-канала устанавливается равным 0.5, делая его на 50% прозрачным:

// Устанавливаем цвета заливки и контура
context.fillStyle = "rgb(100,150,185)";
context.lineWidth = 10;
context.strokeStyle = "red";

// Рисуем круг
context.arc(110, 120, 100, 0, 2*Math.PI);
context.fill();
context.stroke();

// Не забудьте вызвать метод beginPath() перед тем,
// как рисовать новую фигуру
context.beginPath();

// Рисуем треугольник
context.moveTo(215,50);
context.lineTo(15,250);
context.lineTo(315,250);
context.closePath();

// Заполняем треугольник прозрачным цветом
context.fillStyle = "rgba(100,150,185,0.5)";

context.fill();
context.stroke();
Полупрозрачные фигуры на Canvas

Другой способ использования прозрачности — установить значение свойства контекста globalAlpha, как показано в следующем коде:

context.globalAlpha = 0.5;
// Теперь этому цвету автоматически присваивается 
// значение альфа-канала, равное 0.5
context.fillStyle = "rgb(100,150,185)";

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

Какой из этих подходов лучше? Если требуется один прозрачный цвет, используйте функцию rgba(). А если требуется нарисовать несколько фигур разного цвета, но с одинаковым уровнем прозрачности, используйте свойство globalAlpha. Свойство globalAlpha также полезно в том случае, когда нужно рисовать полупрозрачные изображения на холсте.

Пройди тесты
Лучший чат для C# программистов