各位靓仔靓女,早上好!今天我们来聊聊CSS transform
里的 matrix()
和 matrix3d()
这两个看起来有点吓人的函数。别怕,其实它们就是把我们常用的变换操作,比如平移、旋转、缩放、倾斜,一股脑儿打包成一个矩阵而已。理解了矩阵的本质,你就掌握了操控网页元素的“变形金刚”的钥匙!
开场白:矩阵的魅力
你可能在数学课上见过矩阵,一堆数字排列成方阵。当时你可能觉得它跟你的生活八竿子打不着,但是,在图形学里,矩阵可是个宝贝。它可以表示各种变换,而且最酷的是,多个变换可以合并成一个矩阵,一次性应用到元素上。是不是有点像“乾坤大挪移”?
第一部分:2D 矩阵 matrix()
matrix(a, b, c, d, tx, ty)
是 CSS 中 2D 变换的矩阵表示。这六个参数代表着一个 3×3 的矩阵(虽然实际上你只需要写 6 个数字):
[ a c tx ]
[ b d ty ]
[ 0 0 1 ]
这个矩阵会作用于元素的每个像素点 (x, y),计算公式如下:
- x’ = a*x + c*y + tx
- y’ = b*x + d*y + ty
其中 (x, y) 是原始坐标,(x’, y’) 是变换后的坐标。
- a: 水平缩放。 当 a=1,scaleX(1);
- b: 垂直倾斜。
- c: 水平倾斜。
- d: 垂直缩放。当 d=1,scaleY(1);
- tx: 水平平移。
- ty: 垂直平移。
1.1 常用变换的矩阵表示
我们来看看常用的变换,是如何用 matrix()
表示的:
-
平移:
translate(tx, ty)
matrix(1, 0, 0, 1, tx, ty)
- a = 1
- b = 0
- c = 0
- d = 1
- tx = 水平平移距离
- ty = 垂直平移距离
例如:
translate(10px, 20px)
等价于matrix(1, 0, 0, 1, 10, 20)
.element { transform: matrix(1, 0, 0, 1, 10, 20); /* 平移 10px, 20px */ }
-
缩放:
scale(sx, sy)
matrix(sx, 0, 0, sy, 0, 0)
- a = sx (水平缩放比例)
- b = 0
- c = 0
- d = sy (垂直缩放比例)
- tx = 0
- ty = 0
例如:
scale(2, 0.5)
等价于matrix(2, 0, 0, 0.5, 0, 0)
.element { transform: matrix(2, 0, 0, 0.5, 0, 0); /* 水平放大 2 倍,垂直缩小 0.5 倍 */ }
-
旋转:
rotate(angle)
matrix(cos(angle), sin(angle), -sin(angle), cos(angle), 0, 0)
- a = cos(angle)
- b = sin(angle)
- c = -sin(angle)
- d = cos(angle)
- tx = 0
- ty = 0
例如:
rotate(45deg)
等价于matrix(cos(45deg), sin(45deg), -sin(45deg), cos(45deg), 0, 0)
≈matrix(0.707, 0.707, -0.707, 0.707, 0, 0)
.element { transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0); /* 旋转 45 度 */ }
-
倾斜:
skew(ax, ay)
matrix(1, tan(ay), tan(ax), 1, 0, 0)
- a = 1
- b = tan(ay) (垂直倾斜角度的tan值)
- c = tan(ax) (水平倾斜角度的tan值)
- d = 1
- tx = 0
- ty = 0
例如:
skew(30deg, 60deg)
等价于matrix(1, tan(60deg), tan(30deg), 1, 0, 0)
≈matrix(1, 1.732, 0.577, 1, 0, 0)
.element { transform: matrix(1, 1.732, 0.577, 1, 0, 0); /* 水平倾斜 30 度,垂直倾斜 60 度 */ }
1.2 手写 matrix()
的乐趣
现在,让我们尝试手写一个稍微复杂一点的 matrix()
,比如先旋转 30 度,然后平移 (50px, 100px):
- 旋转 30 度:
matrix(cos(30deg), sin(30deg), -sin(30deg), cos(30deg), 0, 0)
≈matrix(0.866, 0.5, -0.5, 0.866, 0, 0)
- 平移 (50px, 100px):
matrix(1, 0, 0, 1, 50, 100)
现在,问题来了,怎么把这两个矩阵合并成一个? 答案是:矩阵乘法!
1.3 矩阵乘法:合并变换
矩阵乘法的规则是:第一个矩阵的每一行,依次乘以第二个矩阵的每一列,对应位置的元素相乘后求和,得到新矩阵对应位置的元素。
假设我们要把矩阵 A 和矩阵 B 相乘,得到矩阵 C:
A = [ a11 a12 a13 ] B = [ b11 b12 b13 ] C = [ c11 c12 c13 ]
[ a21 a22 a23 ] [ b21 b22 b23 ] [ c21 c22 c23 ]
[ a31 a32 a33 ] [ b31 b32 b33 ] [ c31 c32 c33 ]
那么:
- c11 = a11*b11 + a12*b21 + a13*b31
- c12 = a11*b12 + a12*b22 + a13*b32
- c13 = a11*b13 + a12*b23 + a13*b33
- c21 = a21*b11 + a22*b21 + a23*b31
- c22 = a21*b12 + a22*b22 + a23*b32
- c23 = a21*b13 + a22*b23 + a23*b33
- c31 = a31*b11 + a32*b21 + a33*b31
- c32 = a31*b12 + a32*b22 + a33*b32
- c33 = a31*b13 + a32*b23 + a33*b33
记住这个规则,我们就可以把旋转和平移的矩阵相乘了。 注意矩阵乘法不满足交换律,即A*B != B*A
先旋转后平移,意味着平移矩阵乘以旋转矩阵。
- 平移矩阵 T =
[1 0 50]
[0 1 100]
[0 0 1]
- 旋转矩阵 R =
[0.866 0.5 0]
[-0.5 0.866 0]
[0 0 1]
所以 T * R 结果如下:
[ 0.866 0.5 50 ]
[ -0.5 0.866 100 ]
[ 0 0 1 ]
那么 CSS 代码就是:
.element {
transform: matrix(0.866, -0.5, 0.5, 0.866, 50, 100); /* 先旋转 30 度,然后平移 (50px, 100px) */
}
一个小技巧: 如果你觉得手动计算矩阵乘法太麻烦,可以使用在线矩阵计算器,或者自己写一个 JavaScript 函数来完成。
第二部分:3D 矩阵 matrix3d()
matrix3d()
是 3D 变换的矩阵表示,它使用一个 4×4 的矩阵:
[ a1 a2 a3 a4 ]
[ a5 a6 a7 a8 ]
[ a9 a10 a11 a12 ]
[ a13 a14 a15 a16]
matrix3d(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
这个矩阵会作用于元素的每个 3D 坐标点 (x, y, z, 1),计算公式如下:
- x’ = a1*x + a5*y + a9*z + a13
- y’ = a2*x + a6*y + a10*z + a14
- z’ = a3*x + a7*y + a11*z + a15
- w’ = a4*x + a8*y + a12*z + a16
最后的 w’ 用于透视投影,通常情况下我们不用关心它。 最终的坐标需要除以 w’ x’ = x’ / w’; y’ = y’ / w’; z’ = z’ / w’;
2.1 常用 3D 变换的矩阵表示
-
3D 平移:
translate3d(tx, ty, tz)
[ 1 0 0 0 ] [ 0 1 0 0 ] [ 0 0 1 0 ] [ tx ty tz 1 ]
matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1)
-
3D 缩放:
scale3d(sx, sy, sz)
[ sx 0 0 0 ] [ 0 sy 0 0 ] [ 0 0 sz 0 ] [ 0 0 0 1 ]
matrix3d(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1)
-
绕 X 轴旋转:
rotateX(angle)
[ 1 0 0 0 ] [ 0 cos(a) -sin(a) 0 ] [ 0 sin(a) cos(a) 0 ] [ 0 0 0 1 ]
matrix3d(1, 0, 0, 0, 0, cos(angle), sin(angle), 0, 0, -sin(angle), cos(angle), 0, 0, 0, 0, 1)
-
绕 Y 轴旋转:
rotateY(angle)
[ cos(a) 0 sin(a) 0 ] [ 0 1 0 0 ] [ -sin(a) 0 cos(a) 0 ] [ 0 0 0 1 ]
matrix3d(cos(angle), 0, -sin(angle), 0, 0, 1, 0, 0, sin(angle), 0, cos(angle), 0, 0, 0, 0, 1)
-
绕 Z 轴旋转:
rotateZ(angle)
[ cos(a) -sin(a) 0 0 ] [ sin(a) cos(a) 0 0 ] [ 0 0 1 0 ] [ 0 0 0 1 ]
matrix3d(cos(angle), sin(angle), 0, 0, -sin(angle), cos(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
(这个和2D的rotate很相似)
2.2 3D 矩阵的组合
和 2D 矩阵一样,3D 矩阵也可以通过矩阵乘法进行组合。 比如我们想先绕 X 轴旋转 45 度,然后沿 Z 轴平移 100px,就需要将对应的矩阵相乘。
2.3 透视 (Perspective)
3D 变换中一个很重要的概念是透视。透视会使远处的物体看起来比近处的物体小,从而产生 3D 的视觉效果。 在 CSS 中,我们可以使用 perspective
属性来设置透视距离。
perspective: distance;
这个属性应该设置在变换元素的父元素上。 distance
表示观察者到 z=0 平面的距离。 数值越小,透视效果越强烈。
有了 perspective
,我们才能看到 3D 旋转的效果。
第三部分:实战演练
让我们用一个简单的例子来演示 matrix()
和 matrix3d()
的用法。
3.1 2D 变换:卡片翻转效果
<div class="container">
<div class="card">
<div class="front">Front</div>
<div class="back">Back</div>
</div>
</div>
<style>
.container {
width: 200px;
height: 150px;
perspective: 800px; /* 设置透视距离 */
}
.card {
width: 100%;
height: 100%;
position: relative;
transition: transform 1s; /* 添加过渡效果 */
transform-style: preserve-3d; /* 保持 3D 变换 */
}
.card:hover {
transform: rotateY(180deg); /* 鼠标悬停时绕 Y 轴旋转 180 度 */
}
.front, .back {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
backface-visibility: hidden; /* 隐藏背面 */
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
}
.front {
background-color: lightblue;
}
.back {
background-color: lightcoral;
transform: rotateY(180deg); /* 初始时将背面旋转 180 度 */
}
</style>
在这个例子中,我们使用了 rotateY(180deg)
来实现卡片的翻转效果。 我们可以将 rotateY(180deg)
替换成 matrix3d()
的形式:
.card:hover {
transform: matrix3d(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1); /* 绕 Y 轴旋转 180 度 */
}
.back {
transform: matrix3d(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1); /* 初始时将背面旋转 180 度 */
}
3.2 3D 变换:立方体
<div class="container">
<div class="cube">
<div class="face front">1</div>
<div class="face back">2</div>
<div class="face right">3</div>
<div class="face left">4</div>
<div class="face top">5</div>
<div class="face bottom">6</div>
</div>
</div>
<style>
.container {
width: 200px;
height: 200px;
perspective: 800px;
margin: 100px auto;
}
.cube {
width: 200px;
height: 200px;
position: relative;
transform-style: preserve-3d;
transform: rotateX(-30deg) rotateY(30deg); /* 初始旋转角度 */
}
.face {
width: 200px;
height: 200px;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 48px;
background-color: rgba(0, 0, 255, 0.5);
border: 1px solid black;
}
.front { transform: translateZ(100px); }
.back { transform: rotateY(180deg) translateZ(100px); }
.right { transform: rotateY(90deg) translateZ(100px); }
.left { transform: rotateY(-90deg) translateZ(100px); }
.top { transform: rotateX(90deg) translateZ(100px); }
.bottom{ transform: rotateX(-90deg) translateZ(100px); }
</style>
在这个例子中,我们创建了一个简单的立方体。 每个面都通过 transform
属性进行平移和旋转,从而形成立方体的形状。
总结
matrix()
和 matrix3d()
虽然看起来有点复杂,但它们是 CSS transform
的底层实现。 掌握了矩阵变换,你就能更加灵活地控制网页元素的变形,创造出各种炫酷的效果。
记住,多练习、多尝试,你也能成为“变形金刚”的 Master! 今天就到这里, 谢谢大家! 下课!