Transformations
===============

* « Предыдущая статья
* Следующая статья »

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

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

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

`save()`

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

`restore()`

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

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

* Трансформации, которые были применены (например, `translate`, `rotate` and `scale` – см. ниже).
* Текущее значение следующих атрибутов: `strokeStyle`, `fillStyle`, `globalAlpha`, `lineWidth`, `lineCap`, `lineJoin`, `miterLimit`, `lineDashOffset`, `shadowOffsetX` (en-US), `shadowOffsetY` (en-US), `shadowBlur` (en-US), `shadowColor` (en-US), `globalCompositeOperation` (en-US), `font`, `textAlign`, `textBaseline`, `direction` (en-US), `imageSmoothingEnabled` (en-US).
* Текущее значение границ вырезанного холста (clipping path), которые будут рассматриваться в следующем разделе.

Вы можете вызывать метод `save()` столько раз, сколько захотите. В то же время, при вызове метода `restore()` последнее сохранённое состояние будет считано из стека, и все сохранённые настройки будут восстановлены.

### Пример сохранения и восстановления состояния холста

Здесь показано, как функционирует сохранение в стек состояния отрисовки на примере последовательной отрисовки набора прямоугольников.


```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  ctx.fillRect(0, 0, 150, 150); // рисуем прямоугольник с настройками по умолчанию
  ctx.save(); // сохраняем состояние

  ctx.fillStyle = "#09F"; // вносим изменения в настройки
  ctx.fillRect(15, 15, 120, 120); // рисуем прямоугольник с новыми настройками
  ctx.save(); // сохраняем состояние

  ctx.fillStyle = "#FFF"; // вносим изменения в настройки
  ctx.globalAlpha = 0.5;
  ctx.fillRect(30, 30, 90, 90); // рисуем прямоугольник с новыми настройками

  ctx.restore(); // возвращаемся к предыдущим настройкам
  ctx.fillRect(45, 45, 60, 60); // рисуем прямоугольник с восстановленными настройками

  ctx.restore(); // возвращаемся к начальным настройкам
  ctx.fillRect(60, 60, 30, 30); // рисуем прямоугольник с изначальными настройками
}
```


```
<canvas id="canvas" width="150" height="150"></canvas>
```


```
draw();
```


Сначала рисуется большой прямоугольник с настройками по умолчанию. Затем мы сохраняем состояние холста, после чего изменяем цвет заливки. Затем рисуем второй синий прямоугольник меньшего размера и опять сохраняем состояние. Снова изменяем какие-то настройки и рисуем третий полупрозрачный белый прямоугольник.

До сих пор наши действия ничем не отличались от тех, что мы делали в предыдущем разделе. Однако, как только мы сделали первый вызов `restore(),` последнее сохранённое состояние отрисовки было восстановлено из стека, вернув все сохранённые настройки. Если бы мы не сохранили предыдущее состояние, используя `save()`, нам бы пришлось менять цвет заливки и настройки прозрачности вручную для возврата к предыдущему состоянию. Для каких-нибудь двух простых свойств это, может быть, сделать не так сложно. Но если таких свойств гораздо больше, это чревато очень быстрым разрастанием кода.

Когда второй вызов `restore()` сделан, изначальное состояние (то самое, которое было сделано перед первым вызовом `save`) восстанавливается и последний нарисованный прямоугольник вновь становится чёрным.

| Screenshot | Live sample |
| --- | --- |
|  |  |

Трансляция (смещение)
---------------------

![](/ru/docs/Web/API/Canvas_API/Tutorial/Transformations/canvas_grid_translate.png)Первый метод для трансформирования холста `translate()`. Он используется для перемещения холста в любую точку нашей сетки.

`translate(x, y)` (en-US)

Перемещение холста на сетке. `x` и `y` - смещение по горизонтали и вертикали соответственно.

Неплохая идея - сохранять `canvas state` перед использованием любых трансформаций. Обычно удобнее использовать метод `restore`, чем выполнять обратные преобразования, чтобы вернуться к начальному состоянию. Кроме того, если вы выполняете преобразования внутри цикла не используя `save` и `restore`, вы рискуете потерять часть рисунка, потому что он был нарисован за пределами края холста.

### Пример использования `translate`

Этот пример демонстрирует некоторые преимущества при использовании смещения холста. Без использования метода `translate()` все прямоугольники будут отрисованы в одинаковой позиции (0,0). Метод `translate()` даёт возможность размещения прямоугольника в любой позиции без изменения параметров функции `fillRect()`. Это может дать некоторое упрощение для понимания и использования.

Внутри функции `draw()` мы вызываем `fillRect()` девять раз, используя два цикла `for`. Каждый раз мы сохраняем состояние холста, смещаем его, рисуем прямоугольник, а затем восстанавливаем исходное состояние. Заметьте, что `fillRect()` всегда использует одни и те же параметры, а изменение позиции фигуры осуществляется с помощью `translate()`.


```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  for (var i = 0; i < 3; i++) {
    for (var 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();
    }
  }
}
```


```
<canvas id="canvas" width="150" height="150"></canvas>
```


```
draw();
```


| Screenshot | Live sample |
| --- | --- |
|  |  |

Поворот
-------

![](/ru/docs/Web/API/Canvas_API/Tutorial/Transformations/canvas_grid_rotate.png)Второй метод трансформации `rotate()`. Он используется для поворота нашего холста.

`rotate(angle)`

Поворачивает наш холст по часовой стрелке вокруг начальной точки на угол `anglе` в радианах.

Центр поворота - всегда начало координат. Для изменения координат центра мы должны сместить холст, используя метод `translate()`.

### Пример использования `rotate`

В этом примере мы сначала используем `rotate()` для поворота прямоугольника относительно начала координат, а затем, используя `translate()` совместно с `rotate()` поворачиваем прямоугольник относительно его центра.

**Примечание:** Углы измеряются в радианах, а не в градусах. Для преобразования единиц используйте следующую формулу: `radians = (Math.PI/180)*degrees`.


```js
function draw() {
  var 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, и наконец рисуем прямоугольник.
```

<canvas id="canvas" width="300" height="200"></canvas>
```


```
draw();
```


| Screenshot | Live sample |
| --- | --- |
|  |  |

Масштабирование
---------------

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

`scale(x, y)` (en-US)

Масштабирует координатную сетку холста по горизонтали и вертикали. Оба параметра - вещественные числа. Значения меньше 1.0 уменьшают, а больше 1.0 увеличивают масштаб сетки. Значение 1.0 не изменяет его.

Используя отрицательные значения вы можете зеркально отразить направление осей. Например, используя `translate(0,canvas.height); scale(1,-1);` вы получите хорошо известную декартову систему координат с началом в нижнем левом углу.

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

### Пример использования `scale`

В этом примере мы нарисуем прямоугольники, используя разные масштабные коэффициенты.


```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // рисуем масштабированный прямоугольник.
  ctx.save();
  ctx.scale(10, 3);
  ctx.fillRect(1, 10, 10, 10);
  ctx.restore();

  // размещаем текст, отражённый по горизонтали
  ctx.scale(-1, 1);
  ctx.font = "48px serif";
  ctx.fillText("MDN", -135, 120);
}
```


```
<canvas id="canvas" width="150" height="150"></canvas>
```


```
draw();
```


| Screenshot | Live sample |
| --- | --- |
|  |  |

Матричное преобразование
------------------------

В заключении рассмотрим метод, который вызывает изменения в соответствии с матрицей преобразования.

`transform(a, b, c, d, e, f)` (en-US)

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


[

a

c

e

b

d

f

0

0

1

]

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


If any of the arguments are `Infinity` the transformation matrix must be marked as infinite instead of the method throwing an exception.

Параметры функции:

`a (m11)`

Horizontal scaling.

*`b (m12)`*

Horizontal skewing.

`c (m21)`

Vertical skewing.

`d (m22)`

Vertical scaling.

`e (dx)`

Horizontal moving.

`f (dy)`

Vertical moving.

`setTransform(a, b, c, d, e, f)` (en-US)

Сбрасывает текущую матрицу преобразования, а затем вызывает `transform()` в соответствии с аргументами.

`resetTransform()` (en-US)

Сбрасывает текущую матрицу преобразования к значению по умолчанию. Аналогично вызову `ctx.setTransform(1, 0, 0, 1, 0, 0);`

### Пример использования `transform` и `setTransform`


```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  var sin = Math.sin(Math.PI / 6);
  var cos = Math.cos(Math.PI / 6);
  ctx.translate(100, 100);
  var c = 0;
  for (var 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 = "rgba(255, 128, 255, 0.5)";
  ctx.fillRect(0, 50, 100, 100);
}
```


```
<canvas id="canvas" width="200" height="250"></canvas>
```


```
draw();
```


| Screenshot | Live sample |
| --- | --- |
|  |  |

* « Предыдущая статья
* Следующая статья »