CSS 3D变换矩阵(Matrix3d):手动计算变换参数实现复杂空间效果

CSS 3D 变换矩阵 (Matrix3d): 手动计算变换参数实现复杂空间效果

大家好,今天我们来深入探讨 CSS 3D 变换矩阵 matrix3d(),以及如何手动计算其参数,从而实现复杂的空间效果。matrix3d() 提供了最底层、最灵活的 3D 变换控制,理解它的运作机制对于精通 CSS 3D 至关重要。

1. matrix3d() 的结构

matrix3d() 接受 16 个参数,这些参数按行排列,构成一个 4×4 的变换矩阵。这个矩阵描述了 3D 空间中的线性变换,包括平移、旋转、缩放和倾斜。

transform: matrix3d(a1, b1, c1, d1,
                    a2, b2, c2, d2,
                    a3, b3, c3, d3,
                    a4, b4, c4, d4);

这个矩阵与一个齐次坐标向量相乘,得到变换后的坐标向量。齐次坐标是一种在 3D 图形学中常用的坐标表示方法,它将 3D 坐标 (x, y, z) 扩展为 (x, y, z, w),其中 w 通常为 1。

2. 变换矩阵的意义

矩阵的每一行和每一列都有其特定的含义。为了更清晰地理解,我们将其与标准的变换操作对应起来:

x y z w
行 1 scaleX skewY skewZ translateX
行 2 skewX scaleY skewZ translateY
行 3 skewX skewY scaleZ translateZ
行 4 perspectiveX perspectiveY perspectiveZ scaleW (通常为1)
  • scaleX, scaleY, scaleZ: 沿 X, Y, Z 轴的缩放。
  • skewX, skewY, skewZ: 沿 X, Y, Z 轴的倾斜 (也称为 shear)。
  • translateX, translateY, translateZ: 沿 X, Y, Z 轴的平移。
  • perspective: 透视效果。 影响最终的3D视觉深度感。
  • scaleW: 通常保持为 1,用于齐次坐标系统的标准化。

3. 基础变换的矩阵表示

我们先来看看一些基础变换对应的 matrix3d() 表示:

  • 单位矩阵 (无变换):

    transform: matrix3d(1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1);

    单位矩阵不进行任何变换,相当于 transform: none;

  • 沿 X 轴平移 100px:

    transform: matrix3d(1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        100, 0, 0, 1);

    matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1) 等价于 translateX(100px)

  • 沿 Y 轴缩放 0.5 倍:

    transform: matrix3d(1, 0, 0, 0,
                        0, 0.5, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1);

    matrix3d(1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) 等价于 scaleY(0.5)

  • 绕 Z 轴旋转 45 度:

    transform: matrix3d(0.707, 0.707, 0, 0,
                        -0.707, 0.707, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1);

    这里的 0.707 是 cos(45°) 和 sin(45°) 的近似值。 绕Z轴旋转的矩阵构建需要一些三角函数的知识,我们会在后面详细讨论。

4. 旋转矩阵的推导

旋转是 3D 变换中最复杂的部分,需要用到三角函数。我们以绕 Z 轴旋转为例进行推导。

假设我们要将点 (x, y, z) 绕 Z 轴旋转 θ 角度,得到新的点 (x’, y’, z’)。 在二维平面上,绕原点旋转的公式是:

  • x’ = x cos(θ) – y sin(θ)
  • y’ = x sin(θ) + y cos(θ)
  • z’ = z (因为绕 Z 轴旋转,Z 坐标不变)

将这个公式转换为矩阵形式:

| x' |   | cos(θ)  -sin(θ)  0 |   | x |
| y' | = | sin(θ)   cos(θ)  0 | * | y |
| z' |   | 0        0       1 |   | z |

将其扩展到 4×4 矩阵,并加入齐次坐标:

| x' |   | cos(θ)  -sin(θ)  0  0 |   | x |
| y' | = | sin(θ)   cos(θ)  0  0 | * | y |
| z' |   | 0        0       1  0 |   | z |
| 1  |   | 0        0       0  1 |   | 1 |

因此,绕 Z 轴旋转 θ 度的 matrix3d() 值为:

transform: matrix3d(cos(θ), sin(θ), 0, 0,
                    -sin(θ), cos(θ), 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1);

同理,可以推导出绕 X 轴和 Y 轴旋转的矩阵。

  • 绕 X 轴旋转 θ 度的矩阵:

    transform: matrix3d(1, 0, 0, 0,
                        0, cos(θ), sin(θ), 0,
                        0, -sin(θ), cos(θ), 0,
                        0, 0, 0, 1);
  • 绕 Y 轴旋转 θ 度的矩阵:

    transform: matrix3d(cos(θ), 0, -sin(θ), 0,
                        0, 1, 0, 0,
                        sin(θ), 0, cos(θ), 0,
                        0, 0, 0, 1);

代码示例:

<!DOCTYPE html>
<html>
<head>
<title>3D Rotation Example</title>
<style>
.cube {
  width: 100px;
  height: 100px;
  background-color: red;
  margin: 100px;
  transform-origin: center center; /* 旋转中心 */
  transition: transform 2s ease-in-out;
}

.cube:hover {
  /* 绕 Z 轴旋转 180 度 */
  transform: matrix3d(
    Math.cos(Math.PI), Math.sin(Math.PI), 0, 0,
    -Math.sin(Math.PI), Math.cos(Math.PI), 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
  );
}
</style>
</head>
<body>

<div class="cube"></div>

</body>
</html>

在这个例子中,当鼠标悬停在红色立方体上时,它会绕 Z 轴旋转 180 度。 Math.PI 代表 180 度。

5. 矩阵的组合

matrix3d() 的强大之处在于它可以将多个变换组合成一个矩阵。组合变换的顺序很重要,不同的顺序会产生不同的结果。 矩阵的组合是通过矩阵乘法实现的。 例如,先绕 X 轴旋转,再沿 Y 轴平移,对应的矩阵是:

T = TranslationMatrix * RotationMatrix

注意矩阵乘法不满足交换律,A * B 通常不等于 B * A

JavaScript 代码示例:

function multiplyMatrices(a, b) {
  let result = [
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0
  ];

  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      for (let k = 0; k < 4; k++) {
        result[i * 4 + j] += a[i * 4 + k] * b[k * 4 + j];
      }
    }
  }
  return result;
}

// 绕 X 轴旋转 45 度的矩阵
const rotateX = [
  1, 0, 0, 0,
  0, Math.cos(Math.PI / 4), Math.sin(Math.PI / 4), 0,
  0, -Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), 0,
  0, 0, 0, 1
];

// 沿 Y 轴平移 50px 的矩阵
const translateY = [
  1, 0, 0, 0,
  0, 1, 0, 0,
  0, 0, 1, 0,
  0, 50, 0, 1
];

// 组合变换
const combinedMatrix = multiplyMatrices(translateY, rotateX);

console.log(combinedMatrix);

这个例子演示了如何使用 JavaScript 将绕 X 轴旋转和平移 Y 轴的变换组合成一个 matrix3d()。 需要注意的是,CSS 中的 matrix3d() 参数是按行排列的,而这里我们使用的是一维数组,需要根据实际情况进行调整。

6. 透视 (Perspective)

透视是 3D 场景中模拟深度感的重要因素。在 CSS 中,透视可以通过 perspective 属性或 perspective() 函数来设置。 matrix3d() 也允许直接控制透视效果。

透视矩阵的形式如下:

| 1  0  0  0 |
| 0  1  0  0 |
| 0  0  1  -1/d |
| 0  0  0  1 |

其中 d 是观察者到屏幕的距离。 perspective属性实际上影响的是 matrix3d 矩阵的第三行最后一列的值。

代码示例:

<!DOCTYPE html>
<html>
<head>
<title>Perspective Example</title>
<style>
.container {
  width: 200px;
  height: 200px;
  margin: 100px;
  perspective: 500px; /* 设置透视距离 */
}

.cube {
  width: 100px;
  height: 100px;
  background-color: blue;
  transform: rotateY(45deg); /* 绕 Y 轴旋转 */
  transform-style: preserve-3d; /* 关键:保留 3D 变换 */
}
</style>
</head>
<body>

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

</body>
</html>

在这个例子中,perspective: 500px; 设置了透视距离。 transform-style: preserve-3d; 确保了子元素保留 3D 变换,否则旋转效果将失效。

7. transform-origin 的影响

transform-origin 属性定义了变换的中心点。 默认情况下,变换中心点是元素的中心。 当使用 matrix3d() 进行旋转时,变换中心点会显著影响旋转效果。 如果需要绕元素的一个角旋转,需要先使用 transform-origin 将旋转中心点移动到该角。

8. 实际应用: 创建一个简单的 3D 旋转木马

我们可以利用 matrix3d() 创建一个简单的 3D 旋转木马效果。

<!DOCTYPE html>
<html>
<head>
<title>3D Carousel Example</title>
<style>
.carousel-container {
  width: 300px;
  height: 200px;
  margin: 100px auto;
  perspective: 800px;
}

.carousel {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 1s ease-in-out;
}

.carousel-item {
  position: absolute;
  width: 200px;
  height: 150px;
  background-color: lightblue;
  border: 1px solid black;
  text-align: center;
  line-height: 150px;
  font-size: 20px;
}
</style>
</head>
<body>

<div class="carousel-container">
  <div class="carousel" id="carousel">
    <div class="carousel-item" style="transform: rotateY(0deg) translateZ(300px);">1</div>
    <div class="carousel-item" style="transform: rotateY(60deg) translateZ(300px);">2</div>
    <div class="carousel-item" style="transform: rotateY(120deg) translateZ(300px);">3</div>
    <div class="carousel-item" style="transform: rotateY(180deg) translateZ(300px);">4</div>
    <div class="carousel-item" style="transform: rotateY(240deg) translateZ(300px);">5</div>
    <div class="carousel-item" style="transform: rotateY(300deg) translateZ(300px);">6</div>
  </div>
</div>

<script>
  const carousel = document.getElementById('carousel');
  let rotationAngle = 0;

  function rotateCarousel() {
    rotationAngle += 60;
    carousel.style.transform = `rotateY(${rotationAngle}deg)`;
  }

  setInterval(rotateCarousel, 3000);
</script>

</body>
</html>

这个例子创建了一个简单的旋转木马,其中每个 carousel-item 都绕 Y 轴旋转一定角度并沿 Z 轴平移,形成一个圆形排列。 JavaScript 代码用于定期更新旋转角度,使旋转木马旋转起来。 这里我们使用了 rotateYtranslateZ 组合来实现,如果需要更精细的控制,可以将这些变换合并到一个 matrix3d 中。

9. matrix3d 的性能考量

虽然 matrix3d 提供了强大的控制能力,但过度使用可能会影响性能。 复杂的 3D 变换需要大量的计算,尤其是在移动设备上。 在实际应用中,需要权衡效果和性能,尽量使用简单的变换,并避免不必要的重绘。 使用硬件加速(通常浏览器会自动处理)可以提高性能。

10. Debugging matrix3d

调试 matrix3d 变换可能很困难,因为它涉及到 16 个参数。 一些有用的调试技巧包括:

  • 逐步分解变换: 将复杂的变换分解为简单的步骤,逐个测试每个步骤的 matrix3d 值。
  • 使用浏览器的开发者工具: 开发者工具可以显示元素的 transform 属性,并允许你修改 matrix3d 的值,实时查看效果。
  • 使用可视化工具: 有一些在线工具可以可视化 matrix3d 变换,帮助你理解每个参数的含义。

11. 总结: 掌握 CSS 3D变换的核心工具

matrix3d() 是 CSS 3D 变换的核心工具,它提供了最底层、最灵活的控制。理解 matrix3d() 的结构和原理,以及如何手动计算其参数,对于实现复杂的空间效果至关重要。 矩阵组合,透视,变换中心点都是需要掌握的关键点。虽然matrix3d使用起来比较复杂,但只要掌握了其原理,就能创造出令人惊艳的 3D 效果。

更多IT精英技术系列讲座,到智猿学院

发表回复

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