首页 > 解决方案 > 为什么转换的顺序很重要?旋转/缩放与缩放/旋转的结果不同

问题描述

在梳理了SVG 规范以及诸如thisthis之类的指南之后,我仍然在努力理解链式转换是如何工作的。

精选相关行情

当您将 transform 属性应用于 SVG 元素时,该元素会获得当前用户坐标系的“副本”。

和:

当转换被链接时,需要注意的最重要的事情是,就像 HTML 元素转换一样,每个转换都会在坐标系统被之前的转换转换之后应用到坐标系统。

和:

例如,如果您要对元素应用旋转,然后进行平移,则平移会根据新的坐标系进行,而不是根据初始未旋转的坐标系进行。

和:

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

代码

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

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>

问题

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

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

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

谢谢你。

标签: csssvgcss-transforms

解决方案


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

.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 规范中所说,它是相同的。

以下是相关部分:

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

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


让我们做一些数学运算,看看两种转换之间的区别。让我们考虑矩阵乘法,因为我们正在处理 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)  |

现在,如果我们将此矩阵应用于 a,(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视为 anXt并且可以说我们旋转了 ( Xt,Yi) 元素,并且该元素最初是根据 X 轴缩放的。


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

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

在这种情况下什么都不会改变,因为仿射部分是的,但是如果我们有一个翻译与另一个变换(例如:)相结合,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) 

推荐阅读