在梳理了SVG规范以及诸如this和this之类的指南之后,我仍然很难准确理解链接转换是如何工作的。
选定的相关报价
将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>
问题
我们知道,当我们链接变换时,会复制该元素使用的当前坐标系,然后按指定的顺序应用变换。
当我们有一个已经缩放的用户坐标系,并对其应用旋转时,矩形(如图所示)实际上是倾斜的(注意角度的变化)。如果我们以另一种方式进行两个变换(旋转,然后缩放),则不会发生这种情况。
如果您能提供有关缩放后的当前坐标系如何旋转的专家帮助,我们将不胜感激。我正试图从技术(内部工作)的角度来理解为什么倾斜会发生在第一个矩形中。
谢谢。
为了说明它是如何工作的,让我们考虑一个动画来显示缩放效果如何改变旋转。
.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-渲染。它是关于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 (to complete the multiplication)
Temani Afif解释它的方式遵循每个变换所跨越的坐标系。从视口开始,每个连续的坐标系都会派生出来,并位于画布上不同的位置。这些坐标系可能不是笛卡尔坐标系(一个"拉伸的宇宙")。它们在DOM树中从外向内构建,当链接在属性中时,从左到右。
但你可以想象,同样的变换也会朝着相反的方向,从内到外:首先,你在笛卡尔用户空间坐标系中绘制一个矩形,然后通过一系列缩放、旋转等方式进行变换,直到在视口坐标系中绘图时,它被扭曲成其他东西。
但如果你从第二个角度来看,属性中的链式转换需要从右到左进行处理:transform: scale(2, 1) rotate(10deg)
意味着取一个矩形,首先将其旋转10度,然后在水平方向上缩放旋转的矩形。
简而言之,这两者是等价的:
- 如果在变换后的坐标系中绘制图形,请通过从左到右对这些坐标系应用变换来构建坐标系
- 如果在原始坐标系中绘制变换的图形,请通过从右到左对图形应用变换来构造图形