3. 2D变换与3D变换:熟练使用transform进行平移、旋转、缩放、倾斜,理解3D空间与透视原理

说到CSS的transform,我脑子里第一个蹦出来的词就是「魔法」。你想想看,一个静态的div,加上几行代码,就能在屏幕上飞来飞去、旋转跳跃,这感觉确实挺爽的。

但说实话,我刚入行那会儿,对transform的理解也就停留在「能转能移」的层面。直到有一次做H5活动页,需要做一个3D翻转的卡片效果,我才发现——嗯,这东西远比我以为的深。

3.1 2D变换:四个基本功

2D变换说白了就四种操作:平移、旋转、缩放、倾斜。咱们一个一个来。

3.1.1 平移(translate)

平移是最直观的。让元素在X轴或Y轴上移动,就这么简单。

/* 向右移动50px,向下移动100px */
.element {
  transform: translate(50px, 100px);
}

/* 只移动X轴 */
.element-x {
  transform: translateX(50px);
}

/* 只移动Y轴 */
.element-y {
  transform: translateY(100px);
}

我个人习惯用百分比来做居中,比如:

.center-box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

这个技巧我用了快十年了,百试百灵。它比margin负值好使,因为不用知道元素本身的宽高。

3.1.2 旋转(rotate)

旋转的单位是deg(度)。一圈是360deg。

.rotate-45 {
  transform: rotate(45deg);
}

.rotate-half {
  transform: rotate(180deg);
}
小提示:旋转是绕着元素的中心点(默认是50% 50%)转的。如果你想换个旋转中心,可以用transform-origin来调整。

3.1.3 缩放(scale)

缩放的值是倍数。1是原大小,2是两倍,0.5是一半。

.scale-up {
  transform: scale(1.5);  /* 放大1.5倍 */
}

.scale-down {
  transform: scale(0.8);  /* 缩小到80% */
}

/* 单独控制X轴和Y轴 */
.scale-xy {
  transform: scale(1.2, 0.8);  /* 宽放大,高缩小 */
}

我曾经在项目中遇到过一个问题:用scale做hover放大效果时,旁边的元素会被挤开。后来发现,用scale其实不会改变元素的布局尺寸,只是视觉上变了。但如果你用了transform: scale,记得加上will-change来优化性能。

3.1.4 倾斜(skew)

倾斜这个操作,说实话用得不多。但做平行四边形按钮或者一些特殊字体效果时,它很管用。

.skew-x {
  transform: skewX(10deg);  /* X轴倾斜10度 */
}

.skew-y {
  transform: skewY(10deg);  /* Y轴倾斜10度 */
}

.skew-both {
  transform: skew(10deg, 5deg);  /* 两个方向一起 */
}
注意:倾斜会让里面的文字也跟着歪。如果你只想让背景歪,文字保持正常,可以用伪元素来做倾斜,内容放在主元素上。

3.2 组合变换:顺序很重要

多个变换可以写在一起,但顺序会影响结果。为什么?因为CSS的变换是从右往左执行的。

/* 先平移,再旋转 */
.a {
  transform: translate(100px, 0) rotate(45deg);
}

/* 先旋转,再平移 */
.b {
  transform: rotate(45deg) translate(100px, 0);
}

这两个结果完全不一样。第一个是向右平移100px,然后原地旋转。第二个是先旋转45度,然后沿着旋转后的方向平移。说白了,坐标系变了。

我记得有次做loading动画,死活调不对位置,最后发现就是顺序写反了。从那以后,我每次写组合变换都会在脑子里过一遍执行顺序。

3.3 3D变换:进入立体世界

2D玩熟了,就该上3D了。3D变换比2D多了个Z轴,也就是深度。

3.3.1 开启3D空间

想让元素在3D空间里变换,得先告诉浏览器:「我要用3D了」。这个开关就是perspective。

.scene {
  perspective: 800px;  /* 视距,相当于眼睛到屏幕的距离 */
}

.card {
  transform: rotateY(45deg);
}

perspective可以加在父元素上,也可以直接加在子元素上。我个人建议加在父元素上,这样所有子元素共享同一个3D空间,效果更统一。

3.3.2 3D平移、旋转、缩放

3D的平移多了个Z轴:

.move-3d {
  transform: translate3d(50px, 100px, 200px);
  /* X轴50px,Y轴100px,Z轴200px(朝你飞来) */
}

3D旋转也多了两个方向:

.rotate-x {
  transform: rotateX(45deg);  /* 绕X轴转,像翻书 */
}

.rotate-y {
  transform: rotateY(45deg);  /* 绕Y轴转,像转门 */
}

.rotate-z {
  transform: rotateZ(45deg);  /* 绕Z轴转,和2D的rotate一样 */
}

3D缩放同样可以控制Z轴:

.scale-3d {
  transform: scale3d(1.5, 1.5, 2);
  /* Z轴放大2倍,会让元素看起来更「厚」 */
}

3.3.3 透视原理:为什么近大远小?

你想想看,为什么3D变换能产生立体感?说白了就是透视。perspective的值越小,透视效果越强,变形越夸张。值越大,效果越平缓。

perspective值 效果 适用场景
200px - 400px 强烈透视,变形明显 特写、强调效果
600px - 1000px 适中透视,自然立体 卡片翻转、3D轮播
1200px以上 弱透视,接近平面 大场景、远距离视角
核心要点:perspective是3D变换的灵魂。没有它,rotateY(45deg)看起来只是宽度变窄了,而不是真的在旋转。

3.4 实战:3D翻转卡片

说了这么多,来写个实际例子。3D翻转卡片是面试常考题,也是我当年第一个3D项目。

<div class="scene">
  <div class="card">
    <div class="front">正面</div>
    <div class="back">背面</div>
  </div>
</div>

<style>
.scene {
  width: 200px;
  height: 300px;
  perspective: 800px;
}

.card {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.6s;
}

.card:hover {
  transform: rotateY(180deg);
}

.front, .back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
}

.front {
  background: #4A90D9;
}

.back {
  background: #E74C3C;
  transform: rotateY(180deg);
}
</style>

这里有几个关键点:

  • transform-style: preserve-3d:让子元素在3D空间里保持位置关系。不加的话,子元素会被压平。
  • backface-visibility: hidden:翻转时隐藏背面。不然你会看到卡片的「反面」。
  • 两个面都要设position: absolute:让它们叠在一起。
避坑指南:我曾经在Safari上遇到过3D卡片不翻转的问题。后来发现是缺少-webkit-前缀。现在大部分浏览器都支持无前缀写法了,但如果你要兼容老设备,记得加上。

3.5 性能优化:别让动画卡成PPT

3D变换比2D更耗性能。为什么?因为浏览器要计算深度、透视、遮挡关系。我总结了几条经验:

  • 用transform代替left/top:transform不会触发重排,性能更好。
  • 加will-change:告诉浏览器这个元素要变了,让它提前准备。
  • 别滥用3D:不是所有动画都需要3D。2D能搞定的,就别上3D。
.optimized-card {
  will-change: transform;
  /* 或者更具体:will-change: transform, opacity; */
}
注意:will-change别加太多。加多了反而会占用内存,适得其反。一般一个页面控制在3-5个以内。

好了,2D和3D变换的核心内容就这些。说白了,2D是基础,3D是进阶。先把2D的平移旋转缩放练熟,再上手3D,你会发现——嗯,其实也没那么难。

下一章我们聊聊过渡和动画,看看怎么让这些变换动起来。到时候我会分享一个我做过的翻页动画案例,踩过的坑可不少。