CSS `Transform Functions` `matrix3d()` `perspective()` 的数学原理与手写

好的,各位观众老爷们,今天咱们聊聊CSS Transform Functions 里的两位重量级选手:matrix3d()perspective()。 保证让大家听完之后,不仅知其然,还能知其所以然,甚至还能手撸一个简易版的。 准备好了吗? Let’s roll!

开场白: 三维世界的敲门砖

在二维的网页世界里摸爬滚打久了,是不是偶尔也想搞点花样,让元素们跳出平面,来个立体的翻滚跳跃? CSS transform 属性就是那把钥匙,而matrix3d()perspective(),则是通往三维世界的大门。 别害怕,它们并没有想象中那么可怕,只要掌握了背后的数学原理,就能轻松驾驭。

第一幕: matrix3d() 的身世之谜

matrix3d(), 顾名思义,就是一个 3D 变换矩阵。 啥是矩阵? 别慌,简单来说,你可以把它看成一个数字表格,通过特定的规则,可以对坐标进行变换。 在3D世界里,我们需要一个 4×4 的矩阵来描述所有可能的变换,包括平移、旋转、缩放、倾斜,甚至透视。

matrix3d(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)

别被这一堆字母吓到, 咱们把它拆开来看:

X Y Z W
X’ a b c d
Y’ e f g h
Z’ i j k l
W’ m n o p
  • X, Y, Z: 代表原始坐标
  • W: 齐次坐标, 暂时可以理解为1, 后面会解释它的作用
  • X’, Y’, Z’: 变换后的坐标

那么,原始坐标 (x, y, z, 1) 如何通过这个矩阵变换成新的坐标 (x’, y’, z’, w’) 呢? 答案就是矩阵乘法:

  • x’ = a*x + b*y + c*z + d*1
  • y’ = e*x + f*y + g*z + h*1
  • z’ = i*x + j*y + k*z + l*1
  • w’ = m*x + n*y + o*z + p*1

最终,我们得到的坐标是 (x’/w’, y’/w’, z’/w’) 。 注意,这里除以 w’ 的操作非常重要, 它可以实现透视效果。 如果 w’ 始终为 1, 那么就相当于没有透视。

常用变换的矩阵表示

说了这么多, 还是有点抽象。 让我们来看看几种常用变换对应的矩阵:

  1. 单位矩阵 (Identity Matrix): 不做任何变换。

    matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)

    相当于 transform: none;

  2. 平移 (Translate): 将元素沿 X, Y, Z 轴移动。

    假设我们要沿 X 轴移动 tx,沿 Y 轴移动 ty,沿 Z 轴移动 tz。

    matrix3d(1, 0, 0, tx, 0, 1, 0, ty, 0, 0, 1, tz, 0, 0, 0, 1)

    举个例子, matrix3d(1, 0, 0, 100, 0, 1, 0, 50, 0, 0, 1, 20, 0, 0, 0, 1) 表示将元素沿 X 轴移动 100px,沿 Y 轴移动 50px,沿 Z 轴移动 20px。

  3. 缩放 (Scale): 将元素沿 X, Y, Z 轴缩放。

    假设我们要沿 X 轴缩放 sx,沿 Y 轴缩放 sy,沿 Z 轴缩放 sz。

    matrix3d(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1)

    例如,matrix3d(2, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) 表示将元素沿 X 轴放大 2 倍,沿 Y 轴缩小 0.5 倍,Z 轴不变。

  4. 旋转 (Rotate): 这个比较复杂,涉及到旋转轴和旋转角度。 我们先简单介绍绕 Z 轴旋转的矩阵:

    假设我们要绕 Z 轴旋转 θ 角度 (弧度制)。

    matrix3d(cos(θ), sin(θ), 0, 0, -sin(θ), cos(θ), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)

    绕 X 轴和 Y 轴旋转的矩阵类似,只是 cos 和 sin 的位置不同。

代码示例: 用 matrix3d() 实现平移和旋转

<!DOCTYPE html>
<html>
<head>
<title>matrix3d Example</title>
<style>
.box {
  width: 100px;
  height: 100px;
  background-color: red;
  transition: transform 1s; /* 添加过渡效果 */
}

.box:hover {
  /* 平移 100px, 50px, 20px */
  /* 绕 Z 轴旋转 45 度 (0.785 radians) */
  transform: matrix3d(0.707, 0.707, 0, 0, -0.707, 0.707, 0, 0, 0, 0, 1, 0, 100, 50, 20, 1);
}
</style>
</head>
<body>

<div class="box"></div>

</body>
</html>

在这个例子中,当鼠标悬停在红色方块上时,它会同时进行平移和旋转。 注意, 我们使用了 transition 属性来添加过渡效果,让变换过程更平滑。 旋转角度 45 度 换算成弧度约为 0.785。

手写 matrix3d() 的挑战

现在,让我们挑战一下,手写一个简单的函数,来模拟 matrix3d() 的平移效果:

function translate3d(x, y, z, element) {
  const matrix = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    x, y, z, 1
  ];

  const matrixString = `matrix3d(${matrix.join(',')})`;
  element.style.transform = matrixString;
}

// 使用示例
const box = document.querySelector('.box');
translate3d(50, 25, 10, box); // 将方块平移 50px, 25px, 10px

这段代码虽然简单,但足以说明 matrix3d() 的基本原理。 当然,真正的 matrix3d() 实现要复杂得多,需要处理各种旋转、缩放、倾斜等变换。

第二幕: perspective() 的魔法

perspective() 函数用于设置观察者的视角,创造近大远小的透视效果。 它的语法很简单:

perspective(length)

length 表示观察者到 z=0 平面的距离。 这个值越大,透视效果越弱,反之则越强。 可以把它想象成照相机的焦距: 焦距越短,广角效果越明显,透视畸变也越大。

perspective() 的数学原理

perspective() 实际上修改了 matrix3d() 矩阵中的一个值,即第四行第三列 (o)。 具体来说, perspective(d) 相当于设置了 o = -1/d。 这个值会影响到前面提到的 w’ 的计算:

w’ = m*x + n*y + o*z + p*1 = 0*x + 0*y + (-1/d)*z + 1 = 1 – z/d

如果 z 接近 d,那么 w’ 就会接近 0, 导致 x’/w’ 和 y’/w’ 变得非常大,从而产生强烈的透视效果。 如果 z 远小于 d,那么 w’ 接近 1,透视效果不明显。

代码示例: perspective() 的应用

<!DOCTYPE html>
<html>
<head>
<title>Perspective Example</title>
<style>
.container {
  width: 200px;
  height: 200px;
  perspective: 400px; /* 设置透视距离 */
  border: 1px solid black;
  margin: 50px;
}

.box {
  width: 100px;
  height: 100px;
  background-color: blue;
  transform: rotateX(45deg); /* 绕 X 轴旋转 */
}
</style>
</head>
<body>

<div class="container">
  <div class="box"></div>
</div>

</body>
</html>

在这个例子中, 我们将 perspective 属性设置在父元素 .container 上, 并让子元素 .box 绕 X 轴旋转 45 度。 你会看到,由于透视效果的存在,方块的上半部分看起来比下半部分小, 从而产生了立体的感觉。

perspective-origin : 视点的调整

除了 perspective 之外, 还有一个相关的属性 perspective-origin, 用于设置透视的中心点, 也就是观察者的位置。 默认情况下,透视中心位于元素的中心点 (50% 50%)。 你可以通过 perspective-origin: x y; 来调整中心点的位置。 xy 可以是像素值、百分比或关键字 (left, right, top, bottom, center)。

第三幕: matrix3d()perspective() 的联袂演出

matrix3d()perspective() 通常一起使用, 才能创造出更复杂、更逼真的 3D 效果。 perspective() 负责设置整体的透视, matrix3d() 负责进行各种变换, 例如旋转、平移、缩放等。

代码示例: 结合 matrix3d()perspective()

<!DOCTYPE html>
<html>
<head>
<title>Combined Example</title>
<style>
.container {
  width: 200px;
  height: 200px;
  perspective: 400px;
  border: 1px solid black;
  margin: 50px;
}

.box {
  width: 100px;
  height: 100px;
  background-color: green;
  transition: transform 1s;
}

.box:hover {
  /* 先绕 Y 轴旋转 60 度, 再沿 Z 轴平移 50px */
  transform: matrix3d(0.5, 0, 0.866, 0, 0, 1, 0, 0, -0.866, 0, 0.5, 0, 0, 0, 50, 1);
}
</style>
</head>
<body>

<div class="container">
  <div class="box"></div>
</div>

</body>
</html>

在这个例子中, 当鼠标悬停在绿色方块上时, 它会先绕 Y 轴旋转 60 度 (约 1.047 弧度), 然后沿 Z 轴平移 50px。 由于 perspective 属性的存在, 旋转和平移后的方块看起来更具立体感。

总结: 掌握 3D 变换的钥匙

matrix3d()perspective() 是 CSS 3D 变换中最重要的两个函数。 matrix3d() 提供了强大的矩阵变换能力, 可以实现各种复杂的 3D 效果。 perspective() 则负责设置观察者的视角, 创造逼真的透视效果。 掌握了这两个函数, 你就掌握了通往 3D 网页世界的一把钥匙。 多加练习, 你也能创造出令人惊艳的 3D 动画和视觉效果。

进阶之路:

  • 深入理解矩阵数学: 了解矩阵乘法、矩阵求逆等基本概念, 可以更好地理解 3D 变换的原理。
  • 学习 WebGL: WebGL 是一个更底层的 3D 图形 API, 可以实现更复杂、更高效的 3D 渲染。
  • 使用 3D 库: Three.js 是一个流行的 JavaScript 3D 库, 它封装了 WebGL 的底层细节, 提供了更易用的 API。

希望今天的讲座对大家有所帮助。 记住, 学习是一个循序渐进的过程, 不要害怕困难, 勇敢地去探索和实践, 你一定能成为 3D 网页开发的专家! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注