暗而不明郁而不发

发布于
CSS 矩阵变换的实践

内圣外王之道,暗而不明,郁而不发,天下之人各为其所欲焉以自为方。


之前用过的 css transform 形变,只会熟练的使用 旋转 rotate 缩放 scale 斜切 skew 位移 translate 这些基础的属性。

近来想尝试一下高端的 矩阵变换,虽然开篇就是专业的坐标系云云,险些劝退,但是不要紧,弄清楚这几个值就还好。

定义

transform: matrix(a, b, c, d, tx, ty)

这里的 a b 等就是基础的变换的组合,相当于是

matrix(scaleX(), skewY(), skewX(), scaleY(), translateX(), translateY())

然后就是填空了,把原来的基础变换放到对应的位置。

缩放和斜切是比例,位移是距离,默认情况,缩放比例是 1,斜切比例是 0,位移距离是 0。所以不变换的 matrix 就是

{
    transform: matrix(1, 0, 0, 1, 0, 0);
}

实践

假设有一个正方形盒子,想将其变换为一个伪 3d 的效果

el
el1
el2
el3

首先将其拆解为三个面,由于要将其拼接成标准的 六边形,故而每一个都要是一个四边等长的菱形,然后其中的钝角是 120° 用来三分圆周。

先变换出第一个面,可以正方形的 x轴 压缩,然后在 y轴 方向向右下斜切。

el1
1 1 30° cos(30) sin(30)

因为边长相等,变换后差集部分的三角形锐角为 120° - 90° ,通过三角函数计算 x轴 缩放的比例为

{
    transform:  scaleX(calc( cos(30deg) / 1));
}

然后 y 轴斜切的比例为

{
    transform:  skewY(calc( sin(30deg) / 1));
}

组合起来

.el1 {
    transform: matrix(cos(30deg), sin(30deg), 0, 1, 0, 0);
}

第二个面相对于第一个,只需要向右上斜切即可得到对应的形状,然后做一下位移

el1
el2
el3
el2
.el2 {
    transform: matrix(
        cos(30deg),
        sin(-30deg),
        0,
        1,
        calc(cos(30deg) * 100),
        calc(sin(30deg) * 100)
    );
}

第三个面可以理解为,将第一个面旋转 -60度。注意一下,css 变换的 顺序 是从后往前。

所以要组合的话,就要先矩阵变换,然后再 z 轴旋转

el1
el2
el3
el3
.el3 {
    transform: rotateZ(-60deg) matrix(cos(30deg), sin(30deg), 0, 1, 0, 0);
}

逆推

如果现在拿到了一个格式化了的 3D 盒子,想要将其顶面( el3 )变换回来标准的平面:

直接作用在组上变换:

  • 先旋转 60度,使得顶面左边竖直
  • 将盒子平移
  • 将盒子 x轴 整体放大为原来的 1 / cos(30deg)
  • 在放大的基础上再来斜切
el1
el2
el3
el1
el2
el3
.group-transform {
    transform-origin: 0 0;
    transform: matrix(
            calc(1 / cos(30deg)),
            calc(1 / cos(30deg) * sin(-30deg)),
            0,
            1,
            100,
            0
        )
        rotateZ(60deg);
}

致谢

  • 感谢三角函数 为手动计算提供了数学基础;
  • 感谢绘图软件和勤劳的处理器,在可视化操作的背后默默的进行着复杂的计算;