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

标签 css svg css-transforms

经过梳理SVG specification和指南,例如 thisthis ,我仍在努力理解链接转换的确切工作原理。

精选相关语录

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

和:

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

和:

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

和:

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

代码

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

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>

问题

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

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

请专家帮助准确说明缩放后的当前坐标系是如何旋转的,将不胜感激。我试图从技术(内部工作) Angular 理解第一个矩形中发生倾斜的确切原因。

谢谢。

最佳答案

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

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

请注意我们不再有圆圈,而是现在的椭圆形。就像我们拿了一个圆圈,然后对它进行拉伸(stretch),这在我们的矩形内部产生了倾斜效果。


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

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

相关部分如下:

Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent.

From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it


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

关于css - 为什么转换顺序很重要?旋转/缩放不会给出与缩放/旋转相同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58701186/

相关文章:

javascript - foreignObject 中的背景图像隐藏了 Firefox 上的其他 svg 元素

css - 同时使用多个 css 过滤器?

animation - Safari SVG 变换-原点缩放动画

html - 'transform3d' 不适用于位置 : fixed children

html - 如何在 ReactJS 中使用相对路径加载图像?

css - 使用 CSS Grid 指定从右侧开始的列

javascript - 悬停时的 Svg 使组可见

javascript - 如何访问 SVG 文档中的 <path>

html - Bootstrap 网站加载在 iphone 上放大

css - Angular2 查询生成器模块删除按钮布局