z-index仅控制2D层叠顺序,不改变Z轴空间位置;必须配合translateZ()与perspective或preserve-3d开启3D上下文,才能实现真实景深效果。

单纯靠 z-index 无法实现真正的 3D 深度感;必须配合 transform: translateZ() 和开启 3D 上下文(transform-style: preserve-3d 或父容器 perspective),否则 translateZ() 会被忽略或退化为 2D 位移。
为什么 z-index 单独用没用
z-index 只在同一个层叠上下文(stacking context)内生效,且仅控制“绘制顺序”,不改变元素在 Z 轴上的实际空间位置。它本质上是 2D 层叠规则,和透视、深度感知无关。如果你只调 z-index 却没开 3D 渲染,卡片永远是平铺的——哪怕 DOM 顺序不同,视觉上也看不出前后纵深。
常见错误现象:translateZ(50px) 写了但毫无立体效果,浏览器开发者工具里看到元素坐标没变,甚至报 transform 被降级为 2D 的 warning。
- 确保父容器设置了
perspective(如perspective: 1000px),这是触发 3D 视角的关键 - 避免在父级用了
transform但没设transform-style: preserve-3d,否则子元素的translateZ会相对于父级自身平面计算,而非共享 3D 空间 -
z-index在开启了preserve-3d的容器里基本失效——此时应完全依赖translateZ控制前后关系
translateZ() 的单位与取值逻辑
translateZ() 的数值不是像素距离“屏幕”的绝对值,而是相对于当前 3D 坐标系原点的偏移量。它的视觉缩放/遮挡效果强弱,直接受父级 perspective 值影响:perspective 越小,Z 方向变化越夸张;越大则越接近平面效果。
立即学习“前端免费学习笔记(深入)”;
使用场景:做卡片堆叠时,通常让最前一张 translateZ(0),后面的依次递减(如 -20px、-40px),这样它们会自然后退并轻微缩小,模拟真实景深。
- 负值 = 向观察者后方移动(看起来更小、略暗);正值 = 向观察者前方突出(可能遮挡其他元素,需谨慎)
- 不要混用
px和rem等相对单位——translateZ只接受长度单位(px、em、vh等),不能用百分比 - 若卡片有阴影,记得同步调整
box-shadow的blur和spread,越靠后的卡片阴影越柔和、范围越大才自然
必须设置的两个 CSS 属性
缺一不可,否则 3D 效果不会渲染:
- 父容器加
perspective: 800px(值建议 500–1500px,太小易畸变,太大无感) - 卡片容器(即直接包裹卡片的 wrapper)设
transform-style: preserve-3d,否则子卡片的translateZ会塌陷到父级平面
注意:perspective 必须写在能包含所有卡片的共同祖先上(比如 .card-stack),而不是每张卡片自己加——那样每个卡片都独立视角,无法形成统一空间感。
示例结构:
.card-stack {
perspective: 1000px;
}
.card-stack > .card-wrapper {
transform-style: preserve-3d;
}
.card-wrapper > .card {
transform: translateZ(-40px); /* 后续卡片依次减小 */
}移动端与性能注意事项
3D transform 在 iOS Safari 和部分安卓 WebView 中可能触发硬件加速异常,导致闪烁、错位或卡顿,尤其在滚动区域中嵌套 3D 卡片时。
- 避免对
:hover或scroll高频触发的卡片做大幅translateZ动画——改用scale+opacity模拟更稳妥 - 慎用
will-change: transform,它可能在低端设备上引发内存暴涨;优先靠transform自身触发 GPU 加速 - 如果卡片内容含文字或 SVG,注意
translateZ过大会导致字体发虚(因纹理缩放),建议限制 Z 偏移在 ±60px 以内
真正难的不是写出那几行 transform,而是判断什么时候该用 3D,什么时候该退回到 2D 视觉模拟——后者往往更稳定、更可控。


















