【前端(九)】CSS Transform 2D/3D 变换笔记:分清两个原点,搞懂多重变换

文章目录

  • [1. 永远要分清的两个"原点"](#1. 永远要分清的两个“原点”)
  • [2. 2D 变换](#2. 2D 变换)
  • [3. 3D 变换](#3. 3D 变换)
    • [3.1 3D 变换的准备工作](#3.1 3D 变换的准备工作)
      • [3.1.1 开启 3D 空间 (transform-style)](#3.1.1 开启 3D 空间 (transform-style))
      • [3.1.2 设置景深 (perspective)](#3.1.2 设置景深 (perspective))
      • [3.1.3 透视点位置 (perspective-origin)](#3.1.3 透视点位置 (perspective-origin))
    • [3.2 3D 位移](#3.2 3D 位移)
    • [3.3 3D 缩放](#3.3 3D 缩放)
    • [3.4 3D 旋转](#3.4 3D 旋转)
    • [3.5 变换原点](#3.5 变换原点)
    • [3.6 背部可见性](#3.6 背部可见性)
    • [3.7 多重变换](#3.7 多重变换)
  • [4. 常见疑问解答与总结](#4. 常见疑问解答与总结)

开启本小节学习之前,请大家带上富有想象力的立体思维~本小节的核心就是变形 transform


1. 永远要分清的两个"原点"

在踏入 2D 和 3D 变换前,有一个极其重要的基础概念------坐标系原点变换中心点是两个完全不同的东西。

名称 是什么 默认位置 由什么决定
当前坐标系的坐标原点 测量所有坐标 (0,0) 的零点,位移的参考基准 元素的左上角 之前应用的变换(位移/旋转/缩放都会移动原点)
旋转/缩放的中心点 元素绕哪个点旋转、从哪个点向外缩放 元素的几何中心(50% 50%) transform-origin 属性

默认情况下,这两个点绝对不是同一个点:一个是左上角,一个是中心。

如下图所示二维平面:橙色代表元素盒子,蓝色坐标系是位移的参考系,绿色点是旋转和缩放的原点。

三维类似,多一个Z轴。

接下来的所有示例中,请时刻关注这两个"参考点",这是理解变换效果(尤其是复合变换)的绝对前提。


2. 2D 变换

2.1 2D 位移

给元素设置 transform 属性,再用以下函数指定位移:

函数 参数 含义 示例
translateX 长度值(px、%等) 仅水平位移 translateX(50px) 右移50px;translateX(50%) 右移自身宽度的50%
translateY 长度值 仅垂直位移 translateY(20px) 下移20px;translateY(-20%) 上移自身高度的20%
translate 1个值:水平 2个值:水平 垂直 复合位移;1个值只设水平(垂直为0) translate(30px) 右移30px;translate(20px, 40px) 右移20px+下移40px

百分比 :参考的是元素自身 的宽/高,而非父元素。
注意 :位移对行内元素 (如普通 <span>)无效。

示例

css 复制代码
transform: translate(150px,150px)


示例

css 复制代码
transform: translate(50%,50%) ;

对比:2D 位移 vs. 相对定位

2D 位移(translate) 相对定位(position:relative + top/left)
是否脱离文档流 ❌ 不脱离 ❌ 不脱离
对其他元素布局的影响
百分比参照物 自身的宽/高 父元素的宽/高
能否作为定位包含块(子绝父相) ❌ 不能 ✅ 可以
典型用途 动画、元素自身居中 微调位置、创建"定位上下文"

浏览器针对位移有优化,与定位相比,浏览器处理位移的效率更高。所以如果考虑是相同效果的场景下可以先考虑使用位移。

经典居中技巧 :让子元素先用定位 left:50%; top:50%;(移到父元素中心),再用 transform: translate(-50%, -50%);(回移自身的一半),即可精确居中。

css 复制代码
.box {
	position : absolute ;
	left : 50%;
	top : 50%;
	transform : translate(-50%, -50%);
}

2.2 2D 缩放

函数 参数 含义 示例
scaleX 数字 仅水平缩放,1为原大 scaleX(1.5) 水平扩大到1.5倍
scaleY 数字 仅垂直缩放 scaleY(0.5) 垂直缩小一半
scale 1个数字:宽高同时缩放 2个数字:水平 垂直 统一缩放或分别控制 scale(0.5) 整体缩小一半;scale(2, 0.5) 水平拉长两倍、垂直压扁一半

注意translate只写一个值默认水平,scale只写一个值默认宽高同时缩放。

  • 缩放只接受无单位数字,1 表示原大小,大于 1 放大,小于 1 缩小。
  • 同样对行内元素无效。
  • 缩放的值会影响坐标系的刻度,导致后续位移的距离也跟着缩放(后文详述)。

示例

css 复制代码
transform: scale(0.5) ;

技巧:如何实现比浏览器最小字号还小的文字?

浏览器最小字体通常为 12px,但你可以用 transform: scale(0.5) 将已设置 font-size: 12px 的元素缩小一半,视觉上变成 6px。


2.3 2D 旋转

函数 参数 含义 示例
rotate 角度(deg) 绕 Z 轴旋转;正值顺时针,负值逆时针 rotate(45deg) 顺时针45°;rotate(-90deg) 逆时针90°

注意rotate() 等同于 rotateZ(),本质是绕Z轴(从屏幕射向你的那条轴)旋转,并非绕 X 轴。2D 平面就是 XY 平面,旋转轴垂直该平面即为 Z 轴。


2.4 变换原点

使用 transform-origin 可改变旋转或缩放的中心点

  • 该属性不影响位移的参考坐标系(位移始终基于坐标系原点移动)。所以变换原点对旋转和缩放有影响对位移没有影响。
  • 取值参考的是元素自身的宽高。

取值规则详解

transform-origin 可以接受 1 个、2 个或 3 个值(3D 场景才用第三个 Z 轴值)。这里我们重点讲 1~2 个值的情况。
① 两个值:第一个是横坐标,第二个是纵坐标。

每个值都可以是长度值(像素等)、百分比、或方向关键字left``center``right``top``bottom)。

② 一个值:要分两种情况。

一个值的情况 另一个轴的默认值 示例
值是长度或百分比 纵坐标默认为 50%(即垂直居中) transform-origin: 0; → 原点在 (0, 50%),左边缘中点
值是方向关键字(指代上下左右时) 另一个轴取 50% transform-origin: top; → 原点在 (50%, 0),上边缘中点

为了让规则更好记,更好理解,可以看下面这张速查表:

你的写法 实际等价于 原点到底在哪
0% 0% 0% 0% 左上角
0 0 0% 0% 左上角
200px 100px 200px 100px 距左 200px,距上 100px
right bottom 100% 100% 右下角
0 0% 50% 左边缘,垂直居中
left 0% 50% 左边缘,垂直居中
top 50% 0% 上边缘,水平居中
100px 100px 50% 距左 100px,垂直居中

示例

css 复制代码
transform: rotate(30deg);
transform-origin: 0% 0%;	 /* 左上角 */

2.5 多重变换与坐标系的变化(核心原理)

当多个变换写在同一个 transform 中时,每步变换都可能会改变后续步骤的坐标系(原点位置、方向与刻度)

想象你手里拿着一张透明塑料片(元素),上面印着图案。初始坐标系原点在塑料片左上角 ,旋转/缩放中心在塑料片中心

  • 位移:捏住左上角移动,原点跟着移动,中心点也一起平移(但相对塑料片的位置不变)。
  • 旋转 :用针钉住中心点,转动塑料片,左上角(原点)绕中心画弧,坐标系方向改变
  • 缩放 :用针钉住中心点,拉伸或压缩塑料片,坐标系刻度被缩放,左上角(原点)也会移动。

核心结论:旋转和缩放以 transform-origin 为中心,并会移动坐标系原点;位移则带着整个坐标系(包括中心点)一起走,不会改变旋转/缩放中心相对于塑料片的位置。


通过四个对比案例来彻底理解一下:

案例1:先平移,再缩放

css 复制代码
transform: translate(150px, 150px) scale(0.5);

初始状态

  • 坐标系原点:左上角 (0, 0)
  • 旋转/缩放中心:几何中心 (150, 150)

第一步:平移 (150, 150)

平移移动的是整个坐标系,包括坐标原点和中心点。

  • 坐标系原点从 (0, 0) → (150, 150)
  • 中心点也同步平移:原 (150, 150) → (300, 300)

第二步:缩放 0.5 倍

缩放操作以当前中心点 (300, 300) 为基准,向该点收缩。

最终结果


案例2:先缩放,再平移

css 复制代码
transform: scale(0.5) translate(150px, 150px);

初始状态

  • 坐标系原点:(0, 0)
  • 中心点:(150, 150)

第一步:缩放 0.5 倍

以中心点 (150, 150) 为基准收缩。

  • 中心点不变:(150, 150)
  • 坐标原点向中心点靠近:从 (0,0) 变为 (150 - 150×0.5, 150 - 150×0.5) = (75, 75)
  • 坐标系刻度变为 1 单位 = 0.5px

第二步:平移 (150, 150)

注意这时是在已被缩放的坐标系 中移动。

平移 150 单位,实际屏幕移动距离 = 150 × 0.5 = 75px。

  • 坐标系原点从 (75, 75) → (150, 150)
  • 中心点同步平移:从 (150, 150) → (225, 225)

最终结果

对比案例1和案例2,最终位置完全不同,根本原因就在于平移发生的时刻不同,所使用的坐标系刻度不一样


案例3:先平移,再旋转

css 复制代码
transform: translate(150px, 150px) rotate(30deg);

初始状态

  • 原点:(0,0),中心点:(150,150)

第一步:平移 (150, 150)

  • 原点 → (150, 150),中心点 → (300, 300)

第二步:旋转 30°

旋转以当前中心点 (300, 300) 为轴进行。

  • 中心点不动
  • 坐标系原点绕中心点顺时针旋转 30°,位置发生弧线偏移
  • 坐标系的方向也随之旋转 30°

最终结果


案例4:先旋转,再平移

css 复制代码
transform: rotate(30deg) translate(150px, 150px);

初始状态

  • 原点:(0,0),中心点:(150,150)

第一步:旋转 30°

以中心点 (150, 150) 为轴旋转。

  • 中心点位置不变
  • 坐标原点绕 (150, 150) 顺时针旋转 30°,移到了一个新位置
  • 坐标系方向倾斜 30°

第二步:平移 (150, 150)

此时移动是沿已旋转坐标系的 X、Y 轴方向进行。

  • 视觉上的位移方向不是纯水平/垂直,而是斜着移动
  • 原点再沿新轴移动 150 单位,中心点也同步平移

最终结果

注意,这个最终状态下,当你用开发者工具查看旋转角度时,会发现元素好像围绕着一个原点旋转(150px,150px)为什么?因为你先设置的旋转,旋转开始的时候参考点就是(150px,150px),最后位移。这个最终结果是位移之后的,但是当时旋转参考的是初始状态。


实践建议:

  • 尽量把 translate 放在变换列表的最前面,这样位移参考系不受缩放和旋转影响,推理简单。

示例

css 复制代码
transform:translate(150px,150px) scale(0.5) rotate(30deg) ;
  • 如果必须先旋转或缩放再移动,时刻记住坐标系已经被扭曲,移动的方向和距离都不再是屏幕像素上的直观数值。

这个参考系追踪的方法就是打通多重变换的最核心思维,所有 3D 复合变换也完全遵循同一套规则。


3. 3D 变换

3.1 3D 变换的准备工作

所有以下属性都要设置在父元素上!

在 2D 空间中,所有元素都是平面上的纸片。要让它们在三维空间中立起来、产生立体穿插,需要先搭建一个"3D 舞台"。这需要三步:开启 3D 空间设置景深(可选)调整透视点


3.1.1 开启 3D 空间 (transform-style)

它是什么?

控制子元素是否位于三维空间中。默认情况下,即使子元素做了 3D 变换,它们也会被"拍扁"在父元素的 2D 平面上。

为什么需要它?

如果不开启,多个子元素即使设置了 translateZrotateY,看起来也还是像一张张摞在一起的纸片,无法实现真正的立体穿插效果。开启后可让子元素真正分布在 Z 轴不同深度。

怎么设置?

css 复制代码
父元素 {
  transform-style: preserve-3d;  /* 开启 3D 空间 */
}
可选值 含义
flat 默认值。子元素被强制压扁在父元素的 2D 平面内,无视 Z 轴层级。
preserve-3d 子元素保留各自的三维变换,真正位于 Z 轴不同深度。

注意事项

  • 必须设置在直接父元素上,中间层级中断则 3D 失效。
  • transform-style 只影响子元素,不影响自身。

3.1.2 设置景深 (perspective)

它是什么?

景深(透视距离)是观察者(你)到 屏幕(z=0 平面) 的距离。可以想象成你离窗户有多远。

如图理解景深,白线为 z=0 平面,perspective 即为观察者到该平面的距离。

为什么需要它?

没有景深时,3D 旋转就像看一张纸片转动,无近大远小;设置了景深后,元素旋转时近的部分变大、远的部分变小,产生逼真的立体透视感。

如图所示,设置景深后,立体感明显增强:

怎么设置?

css 复制代码
父元素 {
  perspective: 600px;   /* 推荐起始值 */
}
景深值 效果 类比
很小(如 100px) 透视极度夸张,旋转时有"扑面而来"感 贴着窗户玻璃看
适中(600px~1200px) 自然的立体透视 正常视角
很大(如 5000px) 透视很弱,近乎平行投影 拿望远镜看
none(默认) 无透视,完全等距投影 ---

注意事项

  • 景深不宜太小,若小于元素尺寸的一半,旋转时元素会感觉"贴脸",失真。通常设置大于元素宽高的一半,再微调。
  • 默认值为 none不是 0 ,0 无意义,none 就是没有透视。
  • 景深加在父元素上,让所有子元素共享同一透视效果。

3.1.3 透视点位置 (perspective-origin)

它是什么?

透视点就是观察者的眼睛站在哪里看perspective-origin 设定了观察点在屏幕平面上的投影位置,相当于人站在哪个角度去看舞台。

为什么需要它?

默认观察点在父元素正中心,对于大多数居中交互是合适的。但有时你需要模拟人从侧面或上方往下看的效果,此时调整透视点即可改变立体感的偏向方向。

怎么设置?

css 复制代码
父元素 {
  perspective: 600px;
  perspective-origin: 50% 50%;   /* 默认值,中心点 */
}

属性的坐标系统与父元素的 XY 轴一致:

位置 示例
正中心(默认) 50% 50%center center
从右侧看 100% 50%
从上方看 50% 0%

默认透视点在中心:

调整透视点,相当于人移动到不同位置观察,景深就是我们和平面的距离,xy透视点位置属性的坐标系统与父元素的 XY 轴 一致,所以说设定了观察点在屏幕平面上的投影位置。

注意事项

  • 透视点必须与 perspective 配合使用,单独设置无效。
  • 通常情况下,无需修改,默认中心点就很好。

这三个属性构成了 3D 变换的"舞台":transform-style 搭建立体容器,perspective 制造近大远小,perspective-origin 决定从哪个角度观看。搭建好后,子元素就能自由地进行 3D 位移、旋转和缩放了。


3.2 3D 位移

在 2D 位移的基础上增加了 Z 轴。

函数 参数 含义 示例
translateZ 长度值(❌不支持百分比) 正值向屏幕外(靠近你),负值向屏幕里 translateZ(50px)
translate3d 三个长度值:X, Y, Z(三个参数缺一不可 同时设置三维空间中的位移。不移动的轴写 0 translate3d(10px, 20px, 30px)

为什么不支持百分比? Z 轴位移沿视线方向,没有"自身高度"这样的参照物可计算百分比。

z位移如果超过景深,就无法看到物体


3.3 3D 缩放

函数 参数 含义 示例
scaleZ 数字 沿 Z 轴缩放,影响后续旋转/透视时的厚度 scaleZ(1.5)
scale3d (x, y, z) 三个数字,缺一不可 三维缩放 scale3d(1, 1, 1.5) 仅拉伸 Z 轴

虽然单纯缩放 Z 轴在静态画面中看不出厚度变化,但它改变了坐标系 Z 轴刻度。当元素后续绕 X/Y 轴旋转时,"厚度"变大,旋转出的立体感会更强。当你旋转一个薄的事物和一个厚的事物的时候是不一样的,厚的事物会更贴近我们。有的博主说影响景深我猜测也是这个道理。

对比如下(均有旋转45°):

  • 无 scaleZ 额外拉伸:
  • scaleZ(1.5):元素旋转后显得更"厚",更靠近观察者。

3.4 3D 旋转

在 2D 旋转的基础上增加了 沿x 轴和y 轴旋转。

函数 参数 含义 示例
rotateX 角度(deg) X 轴 旋转。 面向 X 轴正方向(即从屏幕右侧看): 正值,使元素顺时针旋转(向上方向外翻); 负值,使元素逆时针旋转 rotateX(45deg)
rotateY 角度(deg) Y 轴 旋转。 面向 Y 轴正方向(即从屏幕下方往上看):正值使元素顺时针旋转(右半部向屏幕内转); 负值,使元素逆时针旋转 rotateY(60deg)
rotate3d (x, y, z, 角度) 4 个参数,缺一不可 绕自定义方向向量旋转,前三个数字表示旋转轴方向(自动归一化) rotate3d(1,1,1, 30deg) 沿对角线转

X 轴和 Y 轴的旋转轴都穿过元素的 transform-origin(默认中心),不是从屏幕边缘开始转。如图所示为绕x轴旋转的顺时针方向,重点展示理解正直旋转顺时针方向。


3.5 变换原点

transform-origin 在 3D 空间同样控制旋转与缩放的中心

  • 可以设置三个值:X 偏移、Y 偏移、Z 偏移(Z 偏移为长度值,如 transform-origin: 50px 50px 20px;)。
  • transform-origin 只平移变换中心,不改变坐标轴方向。Z 偏移让旋转中心变远变近,元素像绕着更远(或更近)的枢轴转动,从而产生不同的翻转视觉效果。
  • 原点偏移方向 ≠ 旋转轴方向时,才能改变视觉上的变换效果

❌ 无效:Z轴偏移 + rotateZ

css 复制代码
transform: rotateZ(45deg);
transform-origin: 0 0 -110px;
  • 旋转轴是 Z,偏移也是 Z → 同方向,轴沿自身元素滑动,画面不变

✅ 有效:Z轴偏移 + rotateY

css 复制代码
transform: rotateY(45deg);
transform-origin: 0  0 -110px;
  • 旋转轴是Y,偏移是Z→ Z 偏移与 Y 轴垂直 → 旋转轴在屏幕深处 110px,元素像绕着背后的柱子翻转,视觉效果变换,3D 感极强。

核心结论

transform-origin 的偏移只有在垂直于旋转轴 的方向上,才能改变视觉上的变换效果。

偏移量平行于旋转轴时,只是轴在自身方向上的滑动,不会产生新的视觉变化。
与 2D 相同,transform-origin 不影响位移的参考系。


3.6 背部可见性

backface-visibility 决定元素背面朝向观察者 时是否可见,加在变换元素自身上

  • visible(默认):可见,会显示正面的镜像。
  • hidden:隐藏,常用于翻转卡片效果,避免背面透出。

当元素绕 Y 轴转 180° 时,背面朝向屏幕,hidden 值可使其消失。


3.7 多重变换

3D 场景中的复合变换与 2D 原理完全一致:每步可能改变坐标系。实际开发中 3D 复合变换相对少见,更依赖单轴的精细控制,此处不再赘述。


4. 常见疑问解答与总结

Q1:为什么 transform 对行内元素无效?

CSS 规范规定,可变换元素 是指所有由盒模型控制布局的元素,但排除了非替换的行内框 (如 <span><a> 默认状态)、表格列、表格列组。因此行内元素无法变换,所有变换函数(位移、缩放、旋转)都一样。
解决办法 :设置 display: inline-blockblock 即可。

Q2:什么时候用 translate,什么时候用 relative 定位?

  • 动画/过渡:优先用 translate,因为它会交给 GPU 合成,性能更好,不触发重排。
  • 纯粹微调位置但需要保留原本的包含块功能:使用相对定位。
  • 居中定位:translate 配合百分比可基于自身尺寸精准居中,简单高效。

Q3:缩放会影响后续位移,到底怎么理解?

缩放会改变坐标系的刻度尺 。假设元素缩放 0.5 倍,坐标系上"1 个单位"就只对应屏幕上的 0.5px。如果在这之后再位移 100px,实际屏幕上的移动就只有 50px。这就是为什么位移放前面可以避免距离被缩放

2D / 3D 变换速查总表

类型 2D 函数 3D 扩展函数 关键差异
位移 translateX/Y translate translateZ translate3d 3D 增加 Z 轴,不支持百分比;需父元素 设置3D 变换前提属性
缩放 scaleX/Y scale scaleZ scale3d 3D 增加 Z 轴厚度,影响旋转后的视觉厚度;需父元素 设置3D 变换前提属性
旋转 rotate (绕Z轴) rotateX/Y rotate3d 3D 可绕 X/Y 轴或任意轴翻转,需透视才能看到效果;需父元素 设置3D 变换前提属性
中心点 transform-origin transform-origin (可设Z偏移) 3D 支持 Z 方向调整旋转轴位置;需父元素 设置3D 变换前提属性

小结:理解两个原点(坐标系原点 vs 变换中心点)和坐标系动态变化,是彻底掌握 CSS Transform 的钥匙。2D 与 3D 变换本质上共享同一套规则,只是 3D 多了一个维度、需要父元素开启景深与 3D 空间。

以上为个人学习总结,旨在梳理个人理解。如有疏漏或不当之处,欢迎指正与交流。

相关推荐
林恒smileZAZ2 小时前
CSS终于支持渐变色的过渡了[特殊字符]
css·html·css3
|晴 天|10 小时前
Vue 3 + TypeScript + Element Plus 博客系统开发总结与思考
前端·vue.js·typescript
猫32811 小时前
v-cloak
前端·javascript·vue.js
旷世奇才李先生11 小时前
Vue 3\+Vite\+Pinia实战:企业级前端项目架构设计
前端·javascript·vue.js
handler0112 小时前
从零实现自动化构建:Linux Makefile 完全指南
linux·c++·笔记·学习·自动化
SoaringHeart13 小时前
Flutter进阶:用OverlayEntry 实现所有弹窗效果
前端·flutter
Hello_Embed13 小时前
嵌入式上位机开发入门(二十六):将 MQTT 测试程序加入 APP 任务
网络·笔记·网络协议·tcp/ip·嵌入式
不会编程的懒洋洋13 小时前
C# Task async/await CancellationToken
笔记·c#·线程·面向对象·task·同步异步
IT_陈寒15 小时前
Vite静态资源加载把我坑惨了
前端·人工智能·后端