Почему порядок преобразований имеет значение?SVG rotate / scale не дает такой же результат, как масштабирование / поворот - PullRequest
0 голосов
/ 07 декабря 2018

После изучения спецификации SVG и таких руководств, как this и this , я все еще пытаюсь понять, как именно работает преобразование цепочек.

Выбранные релевантные цитаты

Когда вы применяете атрибут transform к элементу SVG, этот элемент получает «копию» текущей используемой пользовательской системы координат.

И:

Когда преобразования объединены в цепочку, самое важное, что нужно знать, это то, что, как и при преобразованиях элементов HTML, каждое преобразование применяется к системе координат.после того, как эта система преобразована предыдущими преобразованиями.

И:

Например, если вы собираетесь применить вращение к элементу с последующим переводомперевод происходит в соответствии с новой системой координат, а не с исходной, без поворота.

И:

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

Код

Текущая система координат первого прямоугольника имеет видмасштабируется, затем поворачивается (обратите внимание на порядок).Текущая система координат второго прямоугольника поворачивается, затем масштабируется.

svg {
  border: 1px solid green;
}
<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    rect#s1 {
      fill: red;
      transform: scale(2, 1) rotate(10deg);
    }
  </style>
  <rect id="s1" x="" y="" width="100" height="100" />
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    rect#s2 {
      fill: blue;
      transform: rotate(10deg) scale(2, 1);
    }
  </style>
  <rect id="s2" x="" y="" width="100" height="100" />
</svg>

Вопрос

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

Когда у нас есть пользовательская система координат, которая уже масштабирована, и мы применяем к ней вращение, прямоугольник (как видно) эффективноперекос (обратите внимание на изменившиеся углы).Этого не произойдет, если мы сделаем два преобразования наоборот (поверните, затем масштабируйте).

Экспертная помощь в том, как именно масштабируется текущая система координат, будет высоко оценена.Я пытаюсь понять с технической (внутренней рабочей) точки зрения, почему именно перекос происходит в первом прямоугольнике.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

То, как Темани Афиф объяснил это, следует системам координат, которые охватывает каждое преобразование.Вы начинаете с области просмотра, и каждая последовательная система координат получается и располагается где-то по-другому на холсте.Эти системы координат могут оказаться не декартовыми («растянутая вселенная»).Они построены в дереве DOM снаружи внутрь, а при сцеплении в атрибуте слева направо.

Но вы можете представить то же преобразование и в противоположном направлении, изнутри наружу: сначала выНарисуйте прямоугольник в его декартовой системе координат пространства пользователя, и затем вы преобразуете его с помощью цепочки шкал, поворотов и т. д., пока при рисовании в системе координат области просмотра он не искажается чем-то другим.

Ноесли вы посмотрите на это вторым способом, цепочечные преобразования в атрибуте должны быть обработаны справа налево: transform: scale(2, 1) rotate(10deg) означает взять прямоугольник, сначала поверните его на 10 градусов, а затем масштабировать повернутый прямоугольник в горизонтальном направлении.

Короче говоря, эти два значения эквивалентны:

  • Если вы рисуете график в преобразованной системе координат построить систему координат, применив преобразования к этим системам координат слева направо .
  • Если выи нарисуйте преобразованный график в исходной системе координат, построите график, применяя преобразования к графику справа налево .
0 голосов
/ 07 декабря 2018

Чтобы проиллюстрировать, как это работает, давайте рассмотрим анимацию, показывающую, как эффект масштабирования изменяет вращение.

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  transform-origin:left center;
  animation: rotate 2s linear infinite;
}
@keyframes rotate {
  from{transform:rotate(0)}
  to{transform:rotate(360deg)}

}
<div class="container">
<div class="red">
</div>
</div>

Как вы можете видеть выше, вращение создает идеальную форму круга.

Теперь давайте масштабируем контейнер и видим разницу:

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  transform-origin:left center;
  animation: rotate 5s linear infinite;
}
@keyframes rotate {
  from{transform:rotate(0)}
  to{transform:rotate(360deg)}

}
.container {
  display:inline-block;
  transform:scale(3,1);
  transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>

Обратите внимание, что у нас больше нет круга, но теперь это эллипс.Это как если бы мы взяли круг и зачеркнули его, создавая эффект перекоса внутри нашего прямоугольника.


Если мы делаем противоположный эффект, и мы начинаем с эффекта масштаба, а затем мы применяем вращение, которое мыне будет никакого перекоса.

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  animation: rotate 2s linear infinite;
}
@keyframes rotate {
  from{transform:scale(1,1)}
  to{transform:scale(3,1)}

}
.container {
  display:inline-block;
  transform:rotate(30deg);
  transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>

Чтобы объяснить это по-разному: применение поворота сохранит одинаковое соотношение между осями X и Y, поэтому вы не увидите никакого плохого эффекта при последующем масштабированиино масштабирование только одной оси нарушит соотношение, поэтому наша фигура выглядит плохо, когда мы пытаемся применить вращение.


Вы можете проверить эту ссылку, если хотите получить более подробную информацию о том, как преобразование связано и какМатрица рассчитана: https://www.w3.org/TR/css-transforms-1/#transform-rendering. Речь идет об элементе HTML, но, как сказано в спецификации SVG, это то же самое.

Вот соответствующие части:

Преобразования являются кумулятивными.То есть элементы устанавливают свою локальную систему координат в системе координат своего родителя.

С точки зрения пользователя, элемент эффективно накапливает все свойства преобразованияего предков, а также любое локальное преобразование, примененное к нему


Давайте сделаем некоторую математику, чтобы увидеть разницу между обоими преобразованиями.Давайте рассмотрим матричное умножение, и, поскольку мы имеем дело с двумерным линейным преобразованием, мы сделаем это на ℝ² для простоты 1 .

Для scale(2, 1) rotate(10deg) у нас будет

 |2 0|   |cos(10deg) -sin(10deg)|   |2*cos(10deg) -2*sin(10deg) |
 |0 1| x |sin(10deg) cos(10deg) | = |1*sin(10deg) 1*cos(10deg)  |

Теперь, если мы применим эту матрицу к (Xi,Yi), мы получим (Xf,Yf), как показано ниже:

 Xf = 2* (Xi*cos(10deg) - Yi*sin(10deg))
 Yf =     Xi*sin(10deg) + Yi*cos(10deg)

Обратите внимание, что Xf имеет дополнительный множитель, который равенвиновник создания эффекта перекоса.Как будто мы изменили поведение или Xf и сохранили Yf

Теперь давайте рассмотрим rotate(10deg) scale(2, 1):

 |cos(10deg) -sin(10deg)|   |2 0|   |2*cos(10deg) -1*sin(10deg) |
 |sin(10deg) cos(10deg) | x |0 1| = |2*sin(10deg) 1*cos(10deg)  |

И тогда у нас будет

 Xf =  2*Xi*cos(10deg) - Yi*sin(10deg)
 Yf =  2*Xi*sin(10deg) + Yi*cos(10deg)

Мы можем рассматривать 2*Xi как Xt, и мы можем сказать, что мы повернули (Xt,Yi) элемент, и этот элемент был первоначально масштабирован с учетом оси X.


1 CSS использует также аффинное преобразование (например, translate), поэтому использование ℝ² (декартовы координаты) недостаточно для выполнения нашего расчета, поэтому мы должны рассмотреть ℝℙ² (однородные координаты).Наш предыдущий расчет будет:

 |2 0 0|   |cos(10deg) -sin(10deg) 0|   |2*cos(10deg) -2*sin(10deg) 0|
 |0 1 0| x |sin(10deg) cos(10deg)  0| = |1*sin(10deg) 1*cos(10deg)  0|
 |0 0 1|   |0          0           1|   |0            0             1|

В этом случае ничего не изменится, потому что аффинная часть равна null , но если у нас есть перевод, объединенный с другим преобразованием (например: scale(2, 1) translate(10px,20px))у нас будет следующее:

 |2 0 0|   |1 0 10px|   |2 0 20px|
 |0 1 0| x |0 1 20px| = |0 1 20px|
 |0 0 1|   |0 0 1   |   |0 0  1  |

и

Xf =  2*Xi + 20px;
Yf =  Yi + 20px;
1  =  1 (to complete the multiplication) 
...