在梳理了SVG specification和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-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)
Temani Afif的解释方式遵循每次转换所跨越的坐标系。从视口开始,每个连续的坐标系都将派生出来,并位于画布上的不同位置。这些坐标系可能不是笛卡尔坐标系(“拉伸的宇宙”)。它们是从外部在DOM树中构造的,并且在链接到属性中时,从左到右。
但是您可以从内到外从相反的方向想象同样的变换:首先,在其笛卡尔用户空间坐标系中绘制一个矩形,然后通过一系列缩放,旋转等变换它,直到在视口坐标系中绘制时,它会变形为其他形状。