为什么变换的顺序很重要?旋转/缩放不会给出与缩放/旋转相同的结果

Mag*_*nus 2 css svg css-transforms

在梳理了SVG 规范以及诸如此类的指南之后仍然在努力理解链接变换的工作原理。

\n\n

精选相关报价

\n\n
\n

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

\n
\n\n

And:

\n\n
\n

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

\n
\n\n

And:

\n\n
\n

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

\n
\n\n

And:

\n\n
\n

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

\n
\n\n

Code

\n\n

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

\n\n

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

\n\n

问题

\n\n

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

\n\n

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

\n\n

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

\n\n

谢谢。

\n

Tem*_*fif 7

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

\n\n

\r\n
\r\n
.red {\r\n  width:80px;\r\n  height:20px;\r\n  background:red;\r\n  margin:80px;\r\n  transform-origin:left center;\r\n  animation: rotate 2s linear infinite;\r\n}\r\n@keyframes rotate {\r\n  from{transform:rotate(0)}\r\n  to{transform:rotate(360deg)}\r\n\r\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<div class="container">\r\n<div class="red">\r\n</div>\r\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

正如您在上面看到的,旋转正在创建一个完美的圆形。

\n\n

现在让我们缩放容器并查看差异:

\n\n

\r\n
\r\n
.red {\r\n  width:80px;\r\n  height:20px;\r\n  background:red;\r\n  margin:80px;\r\n  transform-origin:left center;\r\n  animation: rotate 5s linear infinite;\r\n}\r\n@keyframes rotate {\r\n  from{transform:rotate(0)}\r\n  to{transform:rotate(360deg)}\r\n\r\n}\r\n.container {\r\n  display:inline-block;\r\n  transform:scale(3,1);\r\n  transform-origin:left center;\r\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<div class="container">\r\n<div class="red">\r\n</div>\r\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

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

\n\n
\n\n

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

\n\n

\r\n
\r\n
.red {\r\n  width:80px;\r\n  height:20px;\r\n  background:red;\r\n  margin:80px;\r\n  animation: rotate 2s linear infinite;\r\n}\r\n@keyframes rotate {\r\n  from{transform:scale(1,1)}\r\n  to{transform:scale(3,1)}\r\n\r\n}\r\n.container {\r\n  display:inline-block;\r\n  transform:rotate(30deg);\r\n  transform-origin:left center;\r\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<div class="container">\r\n<div class="red">\r\n</div>\r\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

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

\n\n
\n\n

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

\n\n

这是相关部分:

\n\n
\n

转变是累积的。也就是说,元素在其父元素的坐标系内建立其局部坐标系。

\n
\n\n

\n\n

\n

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

\n
\n\n

\n\n


\n\n

让我们做一些数学计算,看看两种转换之间的差异。让我们考虑矩阵乘法,由于我们正在处理 2D 线性变换,因此为了简单起见,我们将在 \xe2\x84\x9d\xc2\xb2 上执行此操作1

\n\n

因为scale(2, 1) rotate(10deg)我们将会有

\n\n
 |2 0|   |cos(10deg) -sin(10deg)|   |2*cos(10deg) -2*sin(10deg) |\n |0 1| x |sin(10deg) cos(10deg) | = |1*sin(10deg) 1*cos(10deg)  |\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,如果我们将此矩阵应用到 ,(Xi,Yi)我们将得到(Xf,Yf)如下所示的结果:

\n\n
 Xf = 2* (Xi*cos(10deg) - Yi*sin(10deg))\n Yf =     Xi*sin(10deg) + Yi*cos(10deg)\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,Xf额外的乘数是造成倾斜效应的罪魁祸首。就像我们改变了行为或Xf保留了Yf

\n\n

现在让我们考虑一下rotate(10deg) scale(2, 1)

\n\n
 |cos(10deg) -sin(10deg)|   |2 0|   |2*cos(10deg) -1*sin(10deg) |\n |sin(10deg) cos(10deg) | x |0 1| = |2*sin(10deg) 1*cos(10deg)  |\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我们就会有

\n\n
 Xf =  2*Xi*cos(10deg) - Yi*sin(10deg)\n Yf =  2*Xi*sin(10deg) + Yi*cos(10deg)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们可以将 视为2*Xian Xt,我们可以说我们旋转了 ( Xt,Yi) 元素,并且该元素最初是根据 X 轴进行缩放的。

\n\n
\n\n

1 CSS 还使用仿射变换(如平移),因此使用 \xe2\x84\x9d\xc2\xb2 (笛卡尔坐标)不足以执行我们的计算,因此我们需要考虑 \xe2\x84\x9d\xe2\x84 \x99\xc2\xb2(齐次坐标)。我们之前的计算是:

\n\n
 |2 0 0|   |cos(10deg) -sin(10deg) 0|   |2*cos(10deg) -2*sin(10deg) 0|\n |0 1 0| x |sin(10deg) cos(10deg)  0| = |1*sin(10deg) 1*cos(10deg)  0|\n |0 0 1|   |0          0           1|   |0            0             1|\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,什么都不会改变,因为仿射部分为,但如果我们将翻译与另一个变换(例如:)相结合,scale(2, 1) translate(10px,20px)我们将得到以下结果:

\n\n
 |2 0 0|   |1 0 10px|   |2 0 20px|\n |0 1 0| x |0 1 20px| = |0 1 20px|\n |0 0 1|   |0 0 1   |   |0 0  1  |\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
Xf =  2*Xi + 20px;\nYf =  Yi + 20px;\n1  =  1 (to complete the multiplication) \n
Run Code Online (Sandbox Code Playgroud)\n

  • 确切地。另一种说法是,矩形在拉伸的“宇宙”中旋转。从矩形的角度来看,它正在绕一圈旋转。但从我们(宇宙之外)的角度来看,这个圆圈被拉长了。使圆形看起来像椭圆形,使矩形看起来倾斜。 (2认同)