为什么转换顺序很重要?旋转/缩放不会给出与缩放/旋转相同的结果 [英] Why does order of transforms matter? rotate/scale doesn't give the same result as scale/rotate

查看:34
本文介绍了为什么转换顺序很重要?旋转/缩放不会给出与缩放/旋转相同的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在梳理完SVG规范thisthis,我仍在努力理解链式转换的工作原理.

精选相关报价

<块引用>

当您将 transform 属性应用于 SVG 元素时,该元素获取当前正在使用的用户坐标系的副本".

还有:

<块引用>

当转换被链接时,需要注意的最重要的事情就是,就像 HTML 元素转换一样,每个变换应用于坐标系之后的坐标系由之前的转换进行转换.

还有:

<块引用>

例如,如果您要对元素应用旋转,然后是翻译,翻译根据新的坐标系,而不是最初的非旋转坐标系.

还有:

<块引用>

转换的顺序很重要.序列变换函数在变换属性内指定是它们应用于形状的顺序.

代码

第一个矩形的当前坐标系被缩放,然后旋转(注意顺序).第二个矩形的当前坐标系被旋转,然后缩放.

svg {边框:1px纯绿色;}

<svg xmlns="http://www.w3.org/2000/svg"><风格>矩形#s1 {填充:红色;变换:缩放(2, 1)旋转(10度);}</风格><rect id="s1" x="" y="" width="100" height="100"/></svg><svg xmlns="http://www.w3.org/2000/svg"><风格>矩形#s2 {填充:蓝色;变换:旋转(10度)比例(2、1);}</风格><rect id="s2" x="" y="" width="100" height="100"/></svg>

问题

我们知道,当我们链接变换时,会复制用于该元素的当前坐标系,然后按照指定的顺序应用变换.

当我们有一个已经缩放的用户坐标系,并对其应用旋转时,矩形(如所见)有效倾斜(注意改变的角度).如果我们以相反的方式进行两个变换(旋转,然后缩放),则不会发生这种情况.

关于缩放的当前坐标系如何旋转的专家帮助,将不胜感激.我试图从技术(内部工作)角度理解为什么第一个矩形会发生倾斜.

谢谢.

解决方案

为了说明它是如何工作的,让我们考虑一个动画来展示缩放效果如何改变旋转.

.red {宽度:80px;高度:20px;背景:红色;边距:80px;变换原点:左中心;动画:旋转 2s 线性无限;}@关键帧旋转{从{变换:旋转(0)}到{变换:旋转(360度)}}

<div class="红色">

如上所示,旋转正在创建一个完美的圆形.

现在让我们缩放容器并查看不同之处:

.red {宽度:80px;高度:20px;背景:红色;边距:80px;变换原点:左中心;动画:旋转5s线性无限;}@关键帧旋转{从{变换:旋转(0)}到{变换:旋转(360度)}}.容器 {显示:内联块;变换:规模(3,1);变换原点:左中心;}

<div class="红色">

请注意,我们不再有圆形,而是现在是椭圆.就像我们拿了一个圆然后拉伸它,这在我们的矩形内创建了倾斜效果.

<小时>

如果我们做相反的效果,从缩放效果开始,然后应用旋转,我们就不会有任何倾斜.

.red {宽度:80px;高度:20px;背景:红色;边距:80px;动画:旋转 2s 线性无限;}@关键帧旋转{从{转换:规模(1,1)}到{变换:缩放(3,1)}}.容器 {显示:内联块;变换:旋转(30度);变换原点:左中心;}

<div class="红色">

换种方式解释:应用旋转将保持 X 轴和 Y 轴之间的比例相同,因此在稍后进行缩放时您不会看到任何不良影响,但仅缩放一个轴会破坏比例,因此我们的形状看起来很糟糕当我们尝试应用旋转时.

<小时>

如果您想了解有关如何链接变换以及如何计算矩阵的更多详细信息,可以查看此链接:https://www.w3.org/TR/css-transforms-1/#transform-rendering.它是关于 HTML 元素的,但正如 SVG 规范中所说的那样.

以下是相关部分:

<块引用>

转换是累积的.也就是说,元素在其父元素的坐标系内建立它们的局部坐标系.

<块引用>

从用户的角度来看,一个元素有效地累积了其祖先的所有变换属性以及应用于它的任何局部变换

<小时>

让我们做一些数学计算,看看这两种转换之间的区别.让我们考虑矩阵乘法,因为我们正在处理 2D 线性变换,为了简单起见,我们将在 ℝ² 上进行此操作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 轴缩放的.

<小时>

1CSS 也使用仿射变换(如翻译),因此使用 ℝ²(笛卡尔坐标)不足以执行我们的计算,因此我们需要考虑 ℝℙ²(同构坐标).我们之前的计算将是:

<代码> |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(完成乘法)

After combing through the SVG specification, and guides such as this and this, I am still struggling to understand exactly how chaining transforms work.

Selected Relevant Quotes

When you apply the transform attribute to an SVG element, that element gets a "copy" of the current user coordinate system in use.

And:

When transformations are chained, the most important thing to be aware of is that, just like with HTML element transformations, each transformation is applied to the coordinate system after that system is transformed by the previous transformations.

And:

For example, if you’re going to apply a rotation to an element, followed by a translation, the translation happens according to the new coordinate system, not the inital non-rotated one.

And:

The sequence of transformations matter. The sequence the transformation functions are specified inside the transform attribute is the sequence they are applied to the shape.

Code

The first rectangle's current coordinate system is scaled, then rotated (note the order). The second rectangle's current coordinate system is rotated, then scaled.

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>

Question

We know that when we chain transforms, a copy is made of the current coordinate system in use for that element, then the transforms are applied in the order they are specified.

When we have a user coordinate system that is already scaled, and we apply a rotate to it, the rectangle is (as seen) effectively skewed (notice the changed angles). This does not happen if we do the two transforms the other way around (rotate, then scale).

Expert help on exactly how the scaled current coordinate system is rotated, would be deeply appreciated. I am trying to understand, from a technical (inner workings) angle, exactly why the skewing happens in the first rectangle.

Thank you.

解决方案

To illustrate how it works let's consider an animation to show how the scaling effect change the rotation.

.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>

As you can see above, the rotation is creating a perfect circle shape.

Now let's scale the container and see the difference:

.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>

Notice how we no more have a circle but it's an ellipse now. It's like we took the circle and we stertch it which is creating the skew effect inside our rectangle.


If we do the opposite effect and we start by having a scale effect and then we apply a rotation we won't have any skewing.

.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>

To explain it differently: Applying a rotation will keep the same ratio between both X and Y axis so you won't see any bad effect when doing scale later but scaling only one axis will break the ratio thus our shape we look bad when we try to apply a rotation.


You can check this link if you want more details about how transform are chained and how the matrix is caclulated: https://www.w3.org/TR/css-transforms-1/#transform-rendering. It's about HTML element but as said in the SVG specification it's the same.

Here is the relevant parts:

Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent.

From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it


Let's do some math in order to see the difference between both transformations. Let's consider matrix multiplication and since we are dealing with a 2D linear transformation we will do this on ℝ² for simplicity1.

For scale(2, 1) rotate(10deg) we will have

 |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)  |

Now if we apply this matrix to an (Xi,Yi) we will obtain (Xf,Yf) like below:

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

Note how the Xf is having an extra multiplier which is the culprit of creating the skew effect. It's like we changed the behavior or Xf and kept the Yf

Now let's consider 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)  |

And then we will have

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

We can consider the 2*Xi as an Xt and we can say that we rotated the (Xt,Yi) element and this element was initially scaled considering the X-axis.


1CSS uses also affine transformation (like translate) so using ℝ² (Cartesian coordinates) isn't enough to perform our calculation so we need to consider ℝℙ² (Homogeneous coordinates). Our previous calculation will be:

 |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|

Nothing will change in this case because the affine part is null but if we have a translation combined with another transform (ex: scale(2, 1) translate(10px,20px)) we will have the following:

 |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  |

And

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

这篇关于为什么转换顺序很重要?旋转/缩放不会给出与缩放/旋转相同的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆