优先选matrix()处理纯2D变换,涉及Z轴移动、任意轴旋转、透视或空间曲线时必须用matrix3d();其参数按列主序排列,a33恒为1,透视值需谨慎设置,Z平移须通过矩阵复合实现。

matrix() 与 matrix3d() 到底该选哪个
2D 路径动画(如斜向滑入、带倾斜的缩放位移)用 matrix() 就够了;只要涉及 Z 轴移动、绕任意轴旋转、透视投影或真实空间曲线(比如弧线飞入、螺旋上升),必须上 matrix3d()。别试图用多个 translate3d() + rotateY() 堆叠来模拟——顺序一错,结果就偏;而 matrix3d() 把所有变换“烘焙”进一个矩阵,顺序问题直接消失。
手动写 matrix3d 参数时最常踩的三个坑
浏览器按列主序读取 matrix3d(a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33),不是行主序。新手照着数学教材抄 4×4 矩阵,直接贴进去会全乱。
-
a33必须是1,否则坐标齐次化失效,元素可能消失或缩成一个点 -
a30–a32(第4行前3列)控制透视,但值不能为 0;设成0.001比0.01更安全,太大容易导致远端变形夸张 - Z 轴平移不写在
a03/a13/a23,而要通过复合矩阵推导:直接填translateZ(50)对应的矩阵后,再乘到你已有的旋转/缩放矩阵左边(不是右边)
用 JavaScript 动态生成 matrix3d 实现路径插值
纯 CSS 关键帧里硬写几十个 matrix3d() 值既难维护又无法响应数据。实际项目中,路径动画几乎都靠 JS 计算每帧矩阵,再写入 style.transform。
例如实现“从左下角沿贝塞尔曲线飞入中心并轻微翻转”:
立即学习“前端免费学习笔记(深入)”;
const path = (t) => {
// t ∈ [0,1],三次贝塞尔:P(t) = (1−t)³P₀ + 3(1−t)²tP₁ + 3(1−t)t²P₂ + t³P₃
const x = Math.pow(1-t,3)*-200 + 3*Math.pow(1-t,2)*t*0 + 3*(1-t)*t*t*0 + t*t*t*0;
const y = Math.pow(1-t,3)*200 + 3*Math.pow(1-t,2)*t*100 + 3*(1-t)*t*t*50 + t*t*t*0;
const rotZ = t * 15; // 末尾顺时针转15度
const scale = 0.8 + t * 0.4;
<p>// 组合:平移 → Z旋转 → 缩放 → 透视增强
const m = multiplyMatrices(
translationMatrix(x, y, 0),
rotationZMatrix(rotZ),
scaleMatrix(scale, scale, 1),
perspectiveMatrix(0.001)
);
return <code>matrix3d(${m.join(',')})</code>;
};关键点:multiplyMatrices 必须按“后写的变换先执行”顺序连乘(即矩阵右乘),否则旋转中心会漂移;perspectiveMatrix 只影响后续变换,不能放在最前面。
动画性能和调试的现实约束
matrix3d() 动画本身不触发重排,但频繁 setAttribute 或 style 写入仍会卡顿。Chrome DevTools 的 “Rendering” 面板里打开“Paint flashing”,能立刻看出是否意外触发了层合成(layer promotion)失败。
- 确保目标元素有
will-change: transform,但别滥用——加在父容器上反而可能让子元素失去独立合成层 - 不要用
%或vw单位参与 matrix 计算;所有tx/ty/tz必须是像素值,否则 JS 计算时需反复 getComputedStyle,开销陡增 - 真要调试矩阵效果?删掉所有其他 transform,只留
transform: matrix3d(...),再用 DevTools 的 computed 标签页点开 transform 查看“Decomposed matrix”——它会把当前矩阵反解成 rotate/scale/translate 等语义化值,比肉眼数 16 个数字靠谱得多
真正难的从来不是写出第一个正确矩阵,而是当路径变复杂、多元素耦合、还要兼容 Safari 时,如何让 16 个数字在不同设备上保持数值稳定——这时候手算不如信工具链,但得知道工具输出的每个数到底压在哪一行哪一列。


















