深入了解 skew() 函数

8
我真的需要了解skew(xdeg)函数是如何工作的,所有的研究似乎都没有解释x角度如何影响其他点并扭曲它,我需要知道是否有数学公式或一种方法来预测使用特定度数时的结果。
注:我已经阅读了大量文档,其中最好的一个是DevDocs,其中说:
这种转换是一种剪切映射(变换),它通过水平和垂直方向上的某个角度扭曲元素中的每个点。每个点的坐标都会按比例修改指定角度和到原点的距离的值;因此,一个点距离原点越远,添加的值就越大。
但是,对于给定的角度如何影响元素中的这些点,没有进一步的说明。
在SVG书中,它通过说将水平或垂直线推移特定值来解释倾斜,但我不明白如何将deg值转换为偏移量。
2个回答

11
为了理解skew是如何工作的,让我们将其与另一种使用角度的变换进行比较。
这里有一个旋转的示例,我们将变换原点设为左上,然后旋转45度

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:rgba(255,0,0,0.5);
  transform-origin:top left;
  transform:rotate(45deg);
}
<div class="box">
  <div></div>
</div>

对于这个例子,找到角度并理解它的工作原理有些微不足道:

enter image description here

现在让我们以同样的例子为基础,将旋转元素的高度减小到一个较小的值:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:3px;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:rotate(45deg);
}
<div class="box">
  <div></div>
</div>

就像我们有一条旋转的线一样。现在让我们用倾斜来替换旋转:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:3px;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:skewY(45deg);
}
<div class="box">
  <div></div>
</div>

如果我们比较两个结果,我们会注意到在两种情况下都存在旋转,但在斜变换的大小方面存在差异:

enter image description here

现在更清楚了,偏斜是如何与角度配合使用的。变换是一种依靠角度来定义这种扭曲的形变。这里有一个更好的说明:

enter image description here

蓝色是我们的初始元素,十字是变换原点,黄色是角度。如果我们进行旋转,我们会得到红线,其中宽度保持不变。如果我们进行倾斜,我们将得到橙色线,其中宽度将会改变,考虑到插图,它将等于W / cos(angle),其中W是我们的初始宽度(在我们之前的情况下,cos(45deg) = 1 / sqrt(2),所以我们将有W * sqrt(2))。


现在我们来看看初始正方形,它在倾斜时会如何表现?

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:skewY(45deg);
}
<div class="box">
  <div></div>
</div>

它将完全按照我们之前描述的逐行的方式运作。如果我们将倾斜应用于另一个方向,我们也将得到相同的结果:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:skewX(45deg);
}
<div class="box">
  <div></div>
</div>

相同的逻辑也适用于垂直线条并考虑高度。值得一提的是,skewX(V)skew(V)相同ref
现在如果我们在两个方向上应用扭曲:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:skew(45deg,10deg);
}
<div class="box">
  <div></div>
</div>

就像我们首先应用skewX来扭曲垂直线条,然后我们将skewY应用于新形状以扭曲水平线条(或相反)。这是一个动画,以说明skew(45deg,45deg)的神奇效果:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform-origin:top left;
  transform:skew(45deg,10deg);
  animation:change 5s infinite alternate linear;
}
@keyframes change {
  from {
    transform:skew(0deg,0deg);
  }
  50% {
    transform:skew(45deg,0deg);
  }
  to {
    transform:skew(45deg,45deg);
  }
}
<div class="box">
  <div></div>
</div>

那么原点呢?对于转换来说,什么也不会改变,只有参考点会改变。换句话说,固定点会移动:

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform-origin:center;
  transform:skew(45deg,10deg);
  animation:change 5s infinite alternate linear;
}
@keyframes change {
  from {
    transform:skew(0deg,0deg);
  }
  50% {
    transform:skew(45deg,0deg);
  }
  to {
    transform:skew(45deg,45deg);
  }
}
<div class="box">
  <div></div>
</div>

我们还可以注意到,如果我们只在一个方向上进行倾斜,则只考虑transform-origin的一个参数。
因此,对于skewX,无论X的值是什么,transform-origin: X Y都将相同。这在某种程度上解释了逐行转换,因为当我们有一行时,我们只有一个维度。

.box {
  margin:50px;
  width:200px;
  height:200px;
  background:blue;
}
.box > div {
  height:100%;
  width:100%;
  background:red;
  transform:skewX(45deg);
  animation:change 5s infinite alternate linear;
}
@keyframes change {
  from {
    transform-origin:0 0;
  }
  50% {
    transform-origin:100% 0;/*nothing will happen between 0 and 50%*/
  }
  to {
    transform-origin:100% 100%;
  }
}
<div class="box">
  <div></div>
</div>

更深入的了解

现在让我们考虑矩阵计算,以了解它的用法以及如何使用tan(angle)

如果我们参考文档,我们有:

enter image description here

这个矩阵用于根据初始元素的每个点的坐标来定义变换后元素的坐标。考虑到这一定义,我们会得到以下方程。

Xf = Xi + Yi * tan(ax)
Yf = Xi * tan(ay) + Yi

如果我们只考虑 skewY ,明显 ax 将为 0 ,因此 tan(0)将为 0 X 不会改变,这就是我们第一个例子中只在Y轴上扭曲的情况(如果我们仅应用skewY,则逻辑相同)。
现在,为什么我们有 Yf = Xi * tan(ay)+ Yi
让我们重新看之前的示例:

enter image description here

绿点是由Xi,Yi定义的初始点,红点是由Xf,Yf定义的变换后的点。显然有Xf=Xi,两点之间的距离为Yf-Yi

从图示中可以清楚地看到tan(ay) = (Yf-Yi)/Xi = (Yf-Yi)/Xf,因此我们有:

Xf = Xi 
Yf = Xi * tan(ay) + Yi

我们会应用相同的逻辑,如果我们在另一个方向上有偏斜。

8

数学运算应用于<angle>是简单的tan(<angle>)。然后将其插入转换矩阵中。

好的,这样并没有深入地讲解skew,也没有解释为什么使用角度而不是数字因子是有意义的。因此,让我们看一下以下ASCII示例,展示了对x轴进行了倾斜。

   skewX(0)            skewX(45deg)
_|         |_         _|         |_ => original box markers
  a o o o o         a o o o o    
  b o o o o           b o o o o
  c o x o o             c o x o o   <-- this line didn't move
  d o o o o               d o o o o
  e o o o z                 e o o o z
 |         |           |         |

因此,如果我们应用 tan(45deg),它会给我们一个skewX系数为1。
这意味着所有水平线都会被它们到变换原点的距离的1倍偏移。
在上面的例子中,变换原点是5*5像素图像的中心 (x) 。
因此,第一条像素线(a o o o o)距离原点负2像素,将向左移动2px。
最后一行(e o o o z)距离原点+2px,将向右移动2px。
位于原点上的中间行(c o x o o)不会受到此变换的影响。
好吧,但这仍然无法解释为什么要使用角度而不是因子...
嗯,角度符号也有道理,因为我们也可以解释我们的示例,即使用它们的中心点作为锚点将每列旋转45度。
即使这只是我个人的猜测,角度还具有额外的好处,即允许skewN(90deg)状态,而这不能用数字因子表示。

@TemaniAfif 嗯... 老实说,我认为你的答案顺序有误 ;-) CSS 变换(像 SVG 和 canvas2D 一样)是从一个 3*3 的变换矩阵中完成的,因此,最终,一切都只是 [scaleX、skewX、skewY、scaleY、translateX、translateY]。这也是 transform 的计算值作为 matrix() 函数返回的原因。所以实际上是 rotate(∂) 被转换为 cos(∂)、sin(∂)、-sin(∂)、cos(∂)、x、y) - Kaiido
@TemaniAfif 在我看来,CSS 语法是从变换矩阵到倾斜函数中制定的,他们决定使用这种用户友好的符号。实际上所做的动作并不是旋转,所以我觉得以此为开始有点误导人。我个人认为,从必须完成的任务到应该按哪个按钮更易于理解。但无论如何,两种观点都很有价值,信息也很准确。我们应该保持现状。 - Kaiido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接