Преобразования
Ранее в этом руководстве мы изучили сетку холста и пространство координат. До сих пор мы использовали только стандартную сетку и изменяли размер всего холста в соответствии с нашими потребностями. Преобразования предоставляют более мощные возможности для перемещения начала координат в другое положение, поворота сетки и даже её масштабирования.
Сохранение и восстановление состояния
Прежде чем перейти к методам преобразования, давайте рассмотрим два других метода, которые незаменимы при создании сложных рисунков.
save()-
Сохраняет всё состояние холста.
restore()-
Восстанавливает последнее сохранённое состояние холста.
Состояния холста хранятся в стеке. Каждый раз, когда вызывается метод save(), текущее состояние рисования помещается в стек. Состояние рисования включает:
- Применённые преобразования (т.е.
translate,rotateиscale— см. ниже). - Текущие значения следующих атрибутов:
- Текущая область обрезки, которую мы рассмотрим в следующей секции.
Вы можете вызывать метод save() сколько угодно раз. Каждый раз, когда вызывается метод restore(), последнее сохранённое состояние извлекается из стека, и все сохранённые настройки восстанавливаются.
Пример сохранения и восстановления состояния холста
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.fillRect(0, 0, 150, 150); // Draw a Black rectangle with default settings
ctx.save(); // Save the original default state
ctx.fillStyle = "#09F"; // Make changes to saved settings
ctx.fillRect(15, 15, 120, 120); // Draw a Blue rectangle with new settings
ctx.save(); // Save the current state
ctx.fillStyle = "#FFF"; // Make changes to saved settings
ctx.globalAlpha = 0.5;
ctx.fillRect(30, 30, 90, 90); // Draw a 50%-White rectangle with newest settings
ctx.restore(); // Restore to previous state
ctx.fillRect(45, 45, 60, 60); // Draw a rectangle with restored Blue setting
ctx.restore(); // Restore to original state
ctx.fillRect(60, 60, 30, 30); // Draw a rectangle with restored Black setting
}
Первый шаг — отрисовка большого прямоугольника с настройками по умолчанию. Затем мы сохраняем это состояние и изменяем цвет заливки. Далее рисуем второй, меньший синий прямоугольник и сохраняем состояние. Затем снова меняем некоторые параметры рисования и рисуем третий полупрозрачный белый прямоугольник.
До сих пор всё довольно похоже на то, что мы делали в предыдущих разделах. Однако, когда вызывается первая команда restore(), верхнее состояние отрисовки удаляется из стека, и настройки восстанавливаются. Если бы мы не сохранили состояние с помощью save(), нам потребовалось бы вручную изменить цвет заливки и прозрачность, чтобы вернуться к предыдущему состоянию. Для двух свойств это просто, но если свойств больше, код станет очень длинным и сложным.
Когда вызывается вторая команда restore(), восстанавливается исходное состояние (то, которое мы задали до первого вызова save), и последний прямоугольник снова рисуется чёрным.
Перемещение
Первый из методов преобразования, который мы рассмотрим, это translate(). Этот метод используется для перемещения холста и его начала координат в другую точку сетки.
translate(x, y)-
Перемещает холст и его начало координат на сетке.
xуказывает горизонтальное расстояние перемещения, аy— вертикальное.
Рекомендуется сохранять состояние холста перед выполнением любых преобразований. В большинстве случаев проще вызвать метод restore вместо выполнения обратного перемещения для возвращения в исходное состояние. Кроме того, если вы выполняете перемещение в цикле и не сохраняете и не восстанавливаете состояние холста, часть рисунка может быть пропущена, так как она была нарисована за пределами границ холста.
Пример перемещения
Этот пример демонстрирует преимущества перемещения начала координат холста. Без метода translate() все прямоугольники были бы нарисованы в одной точке (0,0). Метод translate() также даёт нам свободу размещения прямоугольника в любом месте холста без необходимости ручной корректировки координат в функции fillRect(). Это делает его немного проще для понимания и использования.
В функции draw() мы девять раз вызываем функцию fillRect() с помощью двух циклов for. В каждом цикле холст перемещается, рисуется прямоугольник, а холст возвращается в исходное состояние. Обратите внимание, как вызов fillRect() каждый раз использует одинаковые координаты, полагаясь на translate() для корректировки позиции отрисовки.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
ctx.save();
ctx.fillStyle = `rgb(${51 * i} ${255 - 51 * i} 255)`;
ctx.translate(10 + j * 50, 10 + i * 50);
ctx.fillRect(0, 0, 25, 25);
ctx.restore();
}
}
}
Поворот
Второй метод преобразования — rotate(). Мы используем его для поворота холста вокруг текущего начала координат.
rotate(angle)-
Поворачивает холст по часовой стрелке вокруг текущего начала координат на
angleрадиан.
Точка вращения всегда является началом координат холста. Чтобы изменить точку вращения, нам потребуется переместить холст с помощью метода translate().
Пример поворота
В этом примере мы используем метод rotate() для поворота прямоугольника сначала вокруг начала координат холста, а затем — вокруг центра самого прямоугольника с помощью translate().
Примечание: Углы задаются в радианах, а не в градусах. Для преобразования мы используем: radians = (Math.PI/180)*degrees.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// left rectangles, rotate from canvas origin
ctx.save();
// blue rect
ctx.fillStyle = "#0095DD";
ctx.fillRect(30, 30, 100, 100);
ctx.rotate((Math.PI / 180) * 25);
// grey rect
ctx.fillStyle = "#4D4E53";
ctx.fillRect(30, 30, 100, 100);
ctx.restore();
// right rectangles, rotate from rectangle center
// draw blue rect
ctx.fillStyle = "#0095DD";
ctx.fillRect(150, 30, 100, 100);
ctx.translate(200, 80); // translate to rectangle center
// x = x + 0.5 * width
// y = y + 0.5 * height
ctx.rotate((Math.PI / 180) * 25); // rotate
ctx.translate(-200, -80); // translate back
// draw grey rect
ctx.fillStyle = "#4D4E53";
ctx.fillRect(150, 30, 100, 100);
}
Чтобы повернуть прямоугольник вокруг своего центра, мы перемещаем холст в центр прямоугольника, затем поворачиваем холст, перемещаем его обратно в (0,0) и рисуем прямоугольник.
Масштабирование
Следующий метод преобразования — масштабирование. Мы используем его для увеличения или уменьшения единиц нашей сетки холста. Это можно использовать для рисования масштабированных вниз или вверх фигур и растровых изображений.
scale(x, y)-
Масштабирует единицы холста по оси X горизонтально и по оси Y вертикально. Оба параметра — вещественные числа. Значения меньше 1.0 уменьшают размер единицы, значения больше 1.0 увеличивают размер единицы. Значение 1.0 не меняет размер единицы.
Используя отрицательные числа, можно выполнить отражение относительно осей (например, используя translate(0,canvas.height); scale(1,-1); вы получите хорошо известную Декартову систему координат с началом в левом нижнем углу).
По умолчанию одна единица на холсте соответствует одному пикселю. Если мы применим, например, коэффициент масштабирования 0,5, полученная единица станет 0,5 пикселя, и фигуры будут нарисованы в половину размера. Аналогично, установка коэффициента масштабирования на 2,0 увеличит размер единицы, и одна единица теперь будет равна двум пикселям. В результате фигуры будут нарисованы в два раза больше.
Пример масштабирования
В этом последнем примере мы нарисуем фигуры с различными коэффициентами масштабирования.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// draw a simple rectangle, but scale it.
ctx.save();
ctx.scale(10, 3);
ctx.fillRect(1, 10, 10, 10);
ctx.restore();
// mirror horizontally
ctx.scale(-1, 1);
ctx.font = "48px serif";
ctx.fillText("MDN", -135, 120);
}
Преобразования
Наконец, следующие методы преобразования позволяют вносить изменения непосредственно в матрицу преобразования.
transform(a, b, c, d, e, f)-
Умножает текущую матрицу преобразования на матрицу, заданную ее аргументами. Матрица преобразования описывается следующим образом:
Если какой-либо из аргументов равен
Infinity, то матрица преобразования должна быть помечена как бесконечная, вместо того, чтобы метод выбрасывал исключение.
Параметры этой функции:
a(m11)-
Масштабирование по горизонтали.
b(m12)-
Косвенное искажение по горизонтали.
c(m21)-
Косвенное искажение по вертикали.
d(m22)-
Масштабирование по вертикали.
e(dx)-
Перемещение по горизонтали.
f(dy)-
Перемещение по вертикали.
setTransform(a, b, c, d, e, f)-
Сбрасывает текущее преобразование на единичную матрицу, а затем вызывает метод
transform()с теми же аргументами. Это фактически отменяет текущее преобразование, затем устанавливает указанное преобразование, все в одном шаге. resetTransform()-
Сбрасывает текущее преобразование на единичную матрицу. Это то же самое, что вызов:
ctx.setTransform(1, 0, 0, 1, 0, 0);
Пример для transform и setTransform
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
const sin = Math.sin(Math.PI / 6);
const cos = Math.cos(Math.PI / 6);
ctx.translate(100, 100);
let c = 0;
for (let i = 0; i <= 12; i++) {
c = Math.floor((255 / 12) * i);
ctx.fillStyle = `rgb(${c} ${c} ${c})`;
ctx.fillRect(0, 0, 100, 10);
ctx.transform(cos, sin, -sin, cos, 0, 0);
}
ctx.setTransform(-1, 0, 0, 1, 100, 100);
ctx.fillStyle = "rgb(255 128 255 / 50%)";
ctx.fillRect(0, 50, 100, 100);
}
© 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/Transformations