CSS 3D 变换与 Flex 布局实战:从零打造旋转立方体

30 阅读10分钟

CSS 3D 变换与 Flex 布局实战:从零打造旋转立方体

本文从 CSS 布局基础出发,深入讲解 Flex 弹性布局、inline-block 的隐藏陷阱,再到 CSS 3D 变换的核心属性——带你用纯 CSS 实现一个 3D 旋转立方体,理解前端布局的底层原理。


前言

CSS 不仅是"让页面变好看"的工具,更是前端工程化的核心技能。从 Flex 布局的弹性伸缩,到 CSS 3D 变换的空间旋转——掌握这些技术,你不仅能做出响应式页面,还能打造炫酷的 3D 视觉效果。

本文基于实际代码,系统梳理 CSS 布局的核心概念,并手把手教你实现一个 纯 CSS 3D 旋转立方体


一、HTML 元素的两类本质:行内 vs 块级

1.1 块级元素(Block)

<div>块级元素</div>
<div>独占一行</div>

特性

  • ✅ 可以设置宽高
  • ✅ 独占一行(宽度默认 100%)
  • ✅ 会把兄弟元素"挤下去"

常见块级元素divpullih1~h6sectionarticle

1.2 行内元素(Inline)

<span>行内元素</span>
<span>不会换行</span>

特性

  • ❌ 不可以设置宽高(由内容决定)
  • ❌ 不会把兄弟元素挤下去
  • ✅ 多个行内元素在同一行排列

常见行内元素spanastrongemimg

1.3 display 属性的进化之路

/* 浏览器默认:块级或行内 */
div { display: block; }      /* 块级 */
span { display: inline; }    /* 行内 */

/* 手动切换 */
.display-block { display: block; }
.display-inline { display: inline; }

/* 开启格式化上下文(现代布局) */
.display-flex { display: flex; }           /* 弹性布局 */
.display-inline-block { display: inline-block; }  /* 行内块级 */
.display-grid { display: grid; }           /* 网格布局 */

display 属性演进图

浏览器默认(block/inline)
      │
      ▼
手动切换(display: block/inline)
      │
      ▼
格式化上下文(Formatting Context)
      ├── flex    → 弹性布局
      ├── grid    → 网格布局
      └── inline-block → 行内块级

二、inline-block:行内块级的隐藏陷阱

2.1 什么是 inline-block?

.box {
  display: inline-block;  /* 行内块级 */
  width: 30%;             /* ✅ 可以设置宽高 */
}

特性

  • ✅ 可以设置宽高(像 block)
  • ✅ 不会把兄弟挤下去(像 inline)
  • ⚠️ 默认有个天坑:空格符会占据一定大小

2.2 天坑演示:空格导致布局错位

<div class="box">1</div>
<div class="box">2</div>
<!-- 两个 div 之间有换行/空格 -->
.box {
  display: inline-block;
  width: 50%;   /* 期望:两个各占一半 */
  background: red;
}

问题:两个 50% 宽度的 inline-block 元素不会在同一行

实际效果:
┌────────────┐
│      1     │  ← 第一行(因为空格占用了额外宽度)
└────────────┘
┌────────────┐
│      2     │  ← 第二行
└────────────┘

期望效果:
┌────────┬────────┐
│   1    │   2    │  ← 同一行
└────────┴────────┘

原因:HTML 中的换行和空格会被解析为空白字符,inline-block 元素之间的空白会占据约 4px 的宽度。

2.3 解决方案

/* 方案一:父元素设置 font-size: 0 */
.parent {
  font-size: 0;  /* 消除空白字符 */
}
.parent .box {
  font-size: 16px;  /* 子元素恢复字体大小 */
}

/* 方案二:使用 Flex 布局(推荐) */
.parent {
  display: flex;
}
.box {
  flex: 1;  /* 等分空间 */
}

/* 方案三:注释消除空白 */
<div class="box">1</div><!--
--><div class="box">2</div>

💡 最佳实践:现代项目优先使用 Flex 布局,彻底避免 inline-block 的空格陷阱。


三、Flex 弹性布局:现代前端的首选

3.1 为什么需要 Flex?

传统布局(float/position)的痛点:

  • 垂直居中困难
  • 等高布局复杂
  • 响应式适配繁琐

Flex 布局(Flexible Box)专为一维布局设计,轻松解决这些问题。

3.2 Flex 核心概念

┌─────────────────────────────────────┐
│           Flex 容器                  │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │
│  │ 子项 │ │ 子项 │ │ 子项 │ │ 子项 │  │
│  │  1  │ │  2  │ │  3  │ │  4  │  │
│  └─────┘ └─────┘ └─────┘ └─────┘  │
│                                     │
│  主轴(main axis)→ 水平方向        │
│  次轴(cross axis)↓ 垂直方向       │
└─────────────────────────────────────┘

3.3 容器属性(父元素)

.box {
  display: flex;              /* 开启弹性布局 */
  flex-direction: row;        /* 主轴方向:row(水平)/ column(垂直) */
  justify-content: center;    /* 主轴对齐方式 */
  align-items: center;        /* 次轴对齐方式 */
}

justify-content(主轴对齐)

效果
flex-start左对齐(默认)
center居中对齐
flex-end右对齐
space-between两端对齐,中间等分
space-around每个子项两侧等分
space-evenly所有间距完全相等

align-items(次轴对齐)

效果
stretch拉伸填满(默认)
flex-start顶部对齐
center垂直居中
flex-end底部对齐
baseline基线对齐

3.4 子项属性

.item {
  flex: 1;        /* 等分剩余空间 */
  flex: 2;        /* 占两份空间 */
  flex: 0 0 200px; /* 不伸缩,固定 200px */
}

flex 属性简写

flex: <flex-grow> <flex-shrink> <flex-basis>;

/* 常见写法 */
flex: 1;           /* flex: 1 1 0%  → 等分空间 */
flex: auto;        /* flex: 1 1 auto → 根据内容伸缩 */
flex: none;        /* flex: 0 0 auto → 不伸缩 */

3.5 水平垂直居中终极方案

/* 父容器 */
body {
  height: 100vh;        /* 视口高度 */
  display: flex;
  justify-content: center;  /* 主轴居中 */
  align-items: center;      /* 次轴居中 */
}

/* 子元素自动居中 */
.child {
  /* 无需任何额外样式 */
}

💡 100vh 是什么? vh 是 CSS3 新增的单位,表示 viewport height(视口高度)的百分比。100vh = 视口高度的 100%。同理还有 vw(viewport width),常用于移动端适配。


四、CSS 3D 变换:从 2D 到 3D 的跨越

4.1 CSS 3D vs Canvas 3D

维度CSS 3DCanvas 3D (WebGL)
实现方式CSS 属性JavaScript API
复杂度简单(纯 CSS)复杂(需编程)
性能一般(CPU 渲染)强(GPU 加速)
适用场景简单 3D 效果、卡片翻转复杂 3D 游戏、VR
学习曲线

💡 关键洞察:哪怕是 2D 界面,有时我们也会手动"3D 化"来触发 GPU 硬件加速,提升动画性能。

4.2 3D 变换核心属性

perspective(视距)
.box-wrap {
  perspective: 600px;  /* 观察者与 3D 场景的距离 */
}

原理图解

观察者(眼睛)
    │
    │ 600px(视距)
    │
    ▼
┌─────────────┐
│   3D 场景    │  ← 视距越小,3D 效果越夸张
│             │  ← 视距越大,3D 效果越平缓
└─────────────┘

⚠️ perspective 必须设置在父元素上,而不是 3D 元素本身。

transform-style: preserve-3d
.box {
  transform-style: preserve-3d;  /* 子元素保留 3D 位置 */
}

作用:让子元素在 3D 空间中保持各自的变换,而不是被"压平"到 2D 平面。

without preserve-3d:          with preserve-3d:
┌─────────────┐              ┌─────────────┐
│  ┌───────┐  │              │    ┌───┐    │
│  │ 2D平面 │  │              │   /     \   │
│  │ (扁平) │  │              │  /  3D  \  │
│  └───────┘  │              │ /         \ │
└─────────────┘              └─────────────┘

4.3 3D 变换函数

函数作用示例
translateZ(z)沿 Z 轴移动translateZ(100px) 向前
translate3d(x, y, z)三维移动translate3d(0, 0, 100px)
rotateX(angle)绕 X 轴旋转rotateX(90deg) 俯仰
rotateY(angle)绕 Y 轴旋转rotateY(90deg) 偏航
rotateZ(angle)绕 Z 轴旋转rotateZ(45deg) 翻滚

4.4 布局原则:外层布局 + 内层展示

┌─────────────────────────┐
│      外层盒子(布局)     │  ← 负责定位、视距、动画
│  ┌─────────────────┐    │
│  │   内层盒子(展示) │  ← 负责 3D 变换、颜色、内容
│  │   ┌───┬───┬───┐  │    │
│  │   │ 面│ 面│ 面│  │    │
│  │   └───┴───┴───┘  │    │
│  └─────────────────┘    │
└─────────────────────────┘

五、实战:纯 CSS 3D 旋转立方体

5.1 HTML 结构

<div class="box-wrap">
  <div class="box">
    <div class="face front"></div>
    <div class="face back"></div>
    <div class="face left"></div>
    <div class="face right"></div>
    <div class="face top"></div>
    <div class="face bottom"></div>
  </div>
</div>

结构解析

  • .box-wrap:外层容器,负责 perspective 视距
  • .box:内层容器,负责 3D 变换和动画
  • .face:6 个面,通过 transform 定位到立方体的 6 个方向

5.2 完整 CSS 代码

/* ===== 重置样式 ===== */
* {
  margin: 0;
  padding: 0;
}

/* ===== 页面布局:水平垂直居中 ===== */
html, body {
  height: 100vh;
  display: flex;
  justify-content: center;  /* 主轴居中 */
  align-items: center;      /* 次轴居中 */
}

/* ===== 外层容器:视距 ===== */
.box-wrap {
  width: 200px;
  height: 200px;
  perspective: 600px;  /* 3D 核心:视距 */
}

/* ===== 内层容器:3D 场景 ===== */
.box {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;  /* 保留子元素 3D 位置 */
  animation: rotate 5s linear infinite;  /* 旋转动画 */
}

/* ===== 旋转动画 ===== */
@keyframes rotate {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}

/* ===== 6 个面的公共样式 ===== */
.face {
  width: 200px;
  height: 200px;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 30px;
  color: #fff;
  opacity: 0.8;  /* 透明度,便于观察重叠 */
}

/* ===== 6 个面的定位 ===== */

/* 前面:向前平移 100px */
.front {
  background: #4299e1;
  transform: translateZ(100px);
}

/* 后面:向后平移 100px,再旋转 180° */
.back {
  background: #f56565;
  transform: translateZ(-100px) rotateY(180deg);
}

/* 左面:向左平移 100px,逆时针旋转 90° */
.left {
  background: #48bb78;
  transform: translateX(-100px) rotateY(-90deg);
}

/* 右面:向右平移 100px,顺时针旋转 90° */
.right {
  background: #48bb78;
  transform: translateX(100px) rotateY(90deg);
}

/* 上面:向上平移 100px,顺时针旋转 90° */
.top {
  background: #9f7aea;
  transform: translateY(-100px) rotateX(90deg);
}

/* 下面:向下平移 100px,顺时针旋转 -90° */
.bottom {
  background: #ecc94b;
  transform: translateY(100px) rotateX(-90deg);
}

5.3 6 个面的定位原理

立方体边长 = 200px,所以每个面需要平移 100px(边长的一半)

        top(上)
           │
    left ──┼── right
   (左)   │   (右)
           │
        bottom(下)
           │
        front(前)
           │
        back(后)

前面:translateZ(100px)           → 向观察者方向移动
后面:translateZ(-100px)          → 远离观察者
左面:translateX(-100px)          → 向左移动
右面:translateX(100px)           → 向右移动
上面:translateY(-100px)          → 向上移动
下面:translateY(100px)           → 向下移动

旋转方向约定

旋转轴方向角度
Y 轴180°面向后方
Y 轴-90°(逆时针)面向左侧
Y 轴90°(顺时针)面向右侧
X 轴90°面向顶部
X 轴-90°面向底部

5.4 动画解析

animation: rotate 5s linear infinite;
/*        │      │    │      │
          │      │    │      └─ 无限循环
          │      │    └─ 匀速运动
          │      └─ 持续时间 5 秒
          └─ 动画名称 */

@keyframes rotate {
  0%   { transform: rotateY(0deg); }      /* 起始:正面朝前 */
  100% { transform: rotateY(360deg); }    /* 结束:旋转一圈 */
}

动画属性速查

属性作用示例
animation-name动画名称rotate
animation-duration持续时间5s
animation-timing-function速度曲线linear(匀速)
animation-iteration-count重复次数infinite(无限)
animation-delay延迟开始2s
animation-direction播放方向alternate(往返)

六、知识图谱

CSS 3D 变换与 Flex 布局
├── HTML 元素本质
│   ├── 块级元素(block)
│   │   ├── 可设宽高
│   │   └── 独占一行
│   ├── 行内元素(inline)
│   │   ├── 不可设宽高
│   │   └── 不换行
│   └── display 属性
│       ├── block / inline
│       ├── inline-block(空格陷阱)
│       └── flex / grid
├── Flex 弹性布局
│   ├── 容器属性
│   │   ├── flex-direction(主轴方向)
│   │   ├── justify-content(主轴对齐)
│   │   └── align-items(次轴对齐)
│   ├── 子项属性
│   │   └── flex: grow shrink basis
│   └── 水平垂直居中方案
├── CSS 3D 变换
│   ├── perspective(视距)
│   ├── transform-style: preserve-3d
│   ├── 3D 变换函数
│   │   ├── translateZ / translate3d
│   │   ├── rotateX / rotateY / rotateZ
│   │   └── rotate3d
│   └── 布局原则:外层布局 + 内层展示
└── 实战:3D 旋转立方体
    ├── HTML 结构(wrap + box + 6 faces)
    ├── 6 个面的定位原理
    ├── 旋转动画(@keyframes)
    └── 颜色与透明度

七、总结

本文系统梳理了 CSS 布局与 3D 变换的核心技术:

  1. 行内 vs 块级是 HTML 元素的两类本质,display 属性可以手动切换和开启格式化上下文。
  2. inline-block 虽然灵活,但存在空格陷阱,现代项目优先使用 Flex 布局。
  3. Flex 布局是移动端的首选方案,justify-content 控制主轴,align-items 控制次轴,轻松实现水平垂直居中。
  4. CSS 3D 变换通过 perspectivetransform-style: preserve-3d 开启 3D 空间,translateZrotateX/Y/Z 控制元素在 3D 中的位置和角度。
  5. 3D 立方体的实现遵循"外层布局 + 内层展示"原则,6 个面通过平移和旋转定位到立方体的各个方向。

🚀 学习建议:先掌握 Flex 布局的基础属性,再理解 3D 变换的核心概念(视距、preserve-3d、translate/rotate),最后动手实现一个 3D 立方体。理论 + 实践,才能真正掌握 CSS 3D。


参考资源


📌 标签:#CSS3 #Flex布局 #3D变换 #立方体 #前端布局 #perspective #transform #inline-block

💬 互动:你用 CSS 3D 做过哪些有趣的效果?欢迎在评论区分享!