Spec-Zone.ru › Web APIs

Преобразования

  • Предыдущий
  • Следующий

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

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

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

save()

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

restore()

Восстанавливает последнее сохранённое состояние холста.

Состояния холста хранятся в стеке. Каждый раз, когда вызывается метод save(), текущее состояние рисования помещается в стек. Состояние рисования включает:

  • Применённые преобразования (т.е. translate, rotate и scale — см. ниже).
  • Текущие значения следующих атрибутов:
    • strokeStyle
    • fillStyle
    • globalAlpha
    • lineWidth
    • lineCap
    • lineJoin
    • miterLimit
    • lineDashOffset
    • shadowOffsetX
    • shadowOffsetY
    • shadowBlur
    • shadowColor
    • globalCompositeOperation
    • font
    • textAlign
    • textBaseline
    • direction
    • imageSmoothingEnabled.
  • Текущая область обрезки, которую мы рассмотрим в следующей секции.

Вы можете вызывать метод 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 — вертикальное.

The canvas is pushed down and to the right, or translated, from its origin point on the grid by 'x' units horizontally and 'y' units vertically.

Рекомендуется сохранять состояние холста перед выполнением любых преобразований. В большинстве случаев проще вызвать метод 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 радиан.

The default origin point is at the top left, 0 degrees is horizontal and to the right. The rotation point starts from the origin point and goes clockwise.

Точка вращения всегда является началом координат холста. Чтобы изменить точку вращения, нам потребуется переместить холст с помощью метода 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)

Умножает текущую матрицу преобразования на матрицу, заданную ее аргументами. Матрица преобразования описывается следующим образом:

[acebdf001]\left[ \begin{array}{ccc} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{array} \right]

Если какой-либо из аргументов равен 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API