为什么转换顺序很重要? SVG旋转/缩放的效果与缩放/旋转不同

时间:2018-12-07 14:57:04

标签: css css3 svg css-transforms

在梳理SVG specification以及诸如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>

问题

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

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

非常感谢您对准确缩放比例的当前坐标系如何旋转的专家帮助。我试图从技术(内部工作)的角度来理解偏斜发生在第一个矩形中的确切原因。

谢谢。

2 个答案:

答案 0 :(得分:2)

为说明其工作原理,让我们考虑一个动画,以显示缩放效果如何改变旋转。

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

现在,如果我们将此矩阵应用于(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还使用仿射变换(例如平移),因此使用ℝ²(笛卡尔坐标)不足以执行我们的计算,因此我们需要考虑ℝℙ²(同质坐标)。我们之前的计算将是:

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

答案 1 :(得分:1)

Temani Afif的解释方式遵循每次转换所跨越的坐标系。从视口开始,每个连续的坐标系都将派生出来,并位于画布上的不同位置。这些坐标系可能不是笛卡尔坐标系(“拉伸的宇宙”)。它们是从DOM树的外部开始构造的,当链接到属性中时,它们是从左到右。

但是您可以从内到外在相反的方向上想象相同的变换:首先,在其笛卡尔用户空间坐标系中绘制一个矩形,然后通过一系列缩放,旋转等变换它,直到在视口坐标系中绘制时,它会变形为其他形状。

但是,如果以第二种方式看待它,则属性中的链式转换需要从右到左处理:transform: scale(2, 1) rotate(10deg)表示取一个矩形, first 将其旋转10度,然后然后在水平方向上缩放旋转的矩形。

简而言之,这两个是等效的:

  • 如果您在变换的坐标系中绘制图形,请通过从左至右对这些坐标系进行变换来构造坐标系。
  • 如果您在原始坐标系中绘制了一个已变换的,请通过将变换应用于从右到左来构造该grafic。