文章目录
- [一、Canvas --- HTML5 的"画布"](#一、Canvas — HTML5 的"画布")
- [二、CSS 3D --- 用 CSS 属性触发 GPU 加速](#二、CSS 3D — 用 CSS 属性触发 GPU 加速)
-
- [2.1 不止是"做 3D"](#2.1 不止是"做 3D")
- [2.2 核心 CSS 属性速览](#2.2 核心 CSS 属性速览)
- [三、布局 Layout --- 3D 效果的前置基础](#三、布局 Layout — 3D 效果的前置基础)
-
- [3.1 外层盒子 ------ 3D 的"舞台"](#3.1 外层盒子 —— 3D 的"舞台")
- [3.2 内层盒子 ------ 3D 的"演员"](#3.2 内层盒子 —— 3D 的"演员")
- [四、水平垂直居中 --- 移动端适配的基石](#四、水平垂直居中 — 移动端适配的基石)
-
- [4.1 认识视口单位](#4.1 认识视口单位)
- [4.2 Flexbox 一步到位居中](#4.2 Flexbox 一步到位居中)
- [五、行内元素与块级元素 --- HTML 布局的基石](#五、行内元素与块级元素 — HTML 布局的基石)
-
- [5.1 两大类元素](#5.1 两大类元素)
- [5.2 块级元素 --- 独占一行的盒子](#5.2 块级元素 — 独占一行的盒子)
- [5.3 行内元素 --- 排排坐的文本流](#5.3 行内元素 — 排排坐的文本流)
- [5.4 `display` 属性 --- 手动切换元素类型](#5.4
display属性 — 手动切换元素类型) - [5.5 `inline-block` --- 鱼和熊掌兼得](#5.5
inline-block— 鱼和熊掌兼得)
- [六、定位 --- 摆放 3D 面片的核心机制](#六、定位 — 摆放 3D 面片的核心机制)
-
- [6.1 `position: relative` --- 相对定位](#6.1
position: relative— 相对定位) - [6.2 `position: absolute` --- 绝对定位](#6.2
position: absolute— 绝对定位) - [6.3 定位在 3D 立方体中的应用](#6.3 定位在 3D 立方体中的应用)
- [6.1 `position: relative` --- 相对定位](#6.1
- [七、实战:完整的 3D 旋转立方体](#七、实战:完整的 3D 旋转立方体)
-
- [7.1 HTML 结构](#7.1 HTML 结构)
- [7.2 CSS 完整代码(带逐行注释)](#7.2 CSS 完整代码(带逐行注释))
- [7.3 立方体 6 个面的空间定位原理](#7.3 立方体 6 个面的空间定位原理)
- [7.4 效果预览](#7.4 效果预览)
- [八、Flexbox 布局实战补充](#八、Flexbox 布局实战补充)
本文将带你从 HTML5 Canvas 出发,深入到 CSS 3D 的核心原理与实战,手把手实现一个纯 CSS 驱动的 3D 旋转立方体。同时系统梳理行内/块级元素、弹性布局、定位等前端布局基石,适合正在夯实 CSS 基础、对 3D 效果感兴趣的开发者。
一、Canvas --- HTML5 的"画布"
在聊 CSS 3D 之前,有必要提一下 HTML5 另一个做图形的方向:Canvas。
<canvas> 是 HTML5 新增的标签,它本身只是一块"画布",真正绘制图形需要靠 JavaScript API。你可以用 JS 在 Canvas 上画 2D 图形,也能结合 WebGL 实现复杂的 3D 渲染(比如 Three.js 就是建立在 Canvas/WebGL 之上的)。
Canvas 和 CSS 3D 的本质区别:
| 对比维度 | Canvas | CSS 3D |
|---|---|---|
| 驱动方式 | JavaScript API 逐像素绘制 | CSS 属性声明式触发 |
| 性能消耗 | 大量 JS 计算,CPU 密集 | 浏览器底层/GPU 加速 |
| 交互能力 | 需要通过坐标计算 | 原生 DOM 事件(点击、hover) |
| 学习成本 | 高(需要掌握绘制 API) | 中(熟悉 CSS 属性即可) |
| 适用场景 | 游戏、数据可视化、图像处理 | 页面 UI 动效、产品展示、Banner |
一句话总结:Canvas 适合"画"复杂图形,CSS 3D 适合"摆"界面元素。
二、CSS 3D --- 用 CSS 属性触发 GPU 加速
2.1 不止是"做 3D"
CSS 3D 的核心能力远不止"搞个立体效果"这么简单。当你使用 transform 相关属性时,浏览器会把该元素提升到独立的合成层(Composite Layer) ,交给 GPU 负责渲染,而非走传统的 CPU 重绘流程。
这意味着:
- ✅ 动画更流畅(60fps)
- ✅ 不触发重排(reflow),只触发合成(composite)
- ✅ 主线程压力小,页面交互不卡顿
实战技巧 :哪怕你做的只是一个 2D 的淡入淡出,有时我们也会手动加一句
transform: translateZ(0)------ 这就是俗称的 "手动 3D 化",目的就是骗浏览器开启 GPU 加速。
css
/* 2D 元素也可以偷偷吃 GPU 加速 */
.smooth-card {
transform: translateZ(0); /* 提升到合成层,动画更丝滑 */
transition: opacity 0.3s;
}
2.2 核心 CSS 属性速览
在动手写 3D 效果之前,先把关键属性认全:
| 属性 | 作用 | 常见值 |
|---|---|---|
perspective |
视距(3D 空间的"深度感"来源) | 600px~1200px,值越小透视感越强 |
transform-style |
是否保留子元素的 3D 空间 | preserve-3d(保留)/ flat(默认,拍扁) |
transform |
执行变换:位移、旋转、缩放等 | translateZ() rotateX() rotateY() 等 |
backface-visibility |
元素背面是否可见 | visible / hidden |
perspective-origin |
视点位置(观察者站哪看) | center center(默认) |
三、布局 Layout --- 3D 效果的前置基础
在做 3D 之前,你必须先把元素"摆对位置"。布局不稳,3D 全是歪的。
3.1 外层盒子 ------ 3D 的"舞台"
3D 效果需要一个视距容器来提供深度感。我们通常用一个外层 wrapper 包裹 3D 内容:
css
.box-wrap {
width: 200px;
height: 200px;
perspective: 600px; /* 👈 3D 核心!定义观察者与屏幕的距离 */
}
perspective 的作用直观理解:
想象你站在一条马路上看远方------路越远越小,两条平行的路边最终"汇聚"成一个点。perspective 就是你站的位置与"屏幕"的距离:
perspective: 300px→ 你离屏幕很近 → 透视感极强,元素变形剧烈perspective: 1200px→ 你离屏幕很远 → 透视感弱,接近正交投影perspective: 600px→ 常用中间值,效果自然
css
/* 对比效果 */
.box-wrap-near { perspective: 300px; } /* 强烈透视 */
.box-wrap-far { perspective: 1200px; } /* 微弱透视 */
3.2 内层盒子 ------ 3D 的"演员"
内层盒子需要两个关键属性来配合:
css
.box {
width: 100%;
height: 100%;
position: relative; /* 为子元素的绝对定位提供锚点 */
transform-style: preserve-3d; /* 👈 保留子元素的 3D 空间 */
}
transform-style: preserve-3d 有多重要?
- 默认值
flat会把所有子元素"拍扁"在父元素的平面上------你做的 3D 变换看起来还是 2D - 设为
preserve-3d后,子元素的 Z 轴位置才真正生效,立体感马上就出来了
四、水平垂直居中 --- 移动端适配的基石
在做 3D 效果之前,我们通常需要把内容放到屏幕正中央。这涉及到一个前端面试高频考点:水平垂直居中。
4.1 认识视口单位
CSS3 引入了四个与视口(viewport)相关的单位,特别适合移动端适配:
| 单位 | 含义 | 示例 |
|---|---|---|
vh |
viewport-height,视口高度的 1% | 100vh = 满屏高度 |
vw |
viewport-width,视口宽度的 1% | 100vw = 满屏宽度 |
vmin |
vh 和 vw 中较小的那个 |
竖屏时 = vw |
vmax |
vh 和 vw 中较大的那个 |
竖屏时 = vh |
css
/* 移动端适配利器 */
html, body {
height: 100vh; /* 占满整个屏幕高度,100 等份中的 100 份 */
}
与
100%的区别 :height: 100%依赖父元素有明确高度,嵌套深了容易"塌陷"。100vh直接取视口高度,不依赖任何父元素。
4.2 Flexbox 一步到位居中
css
html, body {
height: 100vh;
display: flex; /* 开启弹性布局 */
flex-direction: row; /* 主轴方向:row(默认水平)/ column(垂直) */
justify-content: center; /* 主轴对齐:居中 */
align-items: center; /* 次轴对齐:居中 */
}
Flexbox 主轴 / 次轴图解:
flex-direction |
主轴方向 | 次轴方向 | justify-content 控制 |
align-items 控制 |
|---|---|---|---|---|
row(默认) |
水平 → | 垂直 ↓ | 水平居中 | 垂直居中 |
column |
垂直 ↓ | 水平 → | 垂直居中 | 水平居中 |
这就是为什么 4 行 CSS 就能同时搞定水平和垂直居中------不需要 margin 负值,不需要 transform 偏移,移动端视窗大小多变时尤其好用。
五、行内元素与块级元素 --- HTML 布局的基石
在深入定位之前,必须搞清楚 HTML 元素的两大分类------这是所有 CSS 布局的底层逻辑。
5.1 两大类元素
HTML 元素天生分为两类:
| 类型 | 默认 display | 典型元素 | 特点 |
|---|---|---|---|
| 块级(block) | block |
div p h1~h6 ul li section |
① 可以设置宽高 ② 独占一行 |
| 行内(inline) | inline |
span a strong em i |
① 不能设置宽高 ② 不会挤走兄弟 |
5.2 块级元素 --- 独占一行的盒子
html
<div class="block-demo">我是块级盒子1</div>
<div class="block-demo">我是块级盒子2</div>
css
.block-demo {
width: 300px; /* ✅ 可以设置宽高 */
height: 80px;
background: #4299e1;
/* 默认独占一行,下一个元素会换行 */
}
5.3 行内元素 --- 排排坐的文本流
html
<span class="inline-demo">行内1</span>
<span class="inline-demo">行内2</span>
css
.inline-demo {
/* width: 200px; ❌ 设置宽高无效! */
background: #f5656f;
padding: 5px 10px;
/* 不会独占一行,兄弟元素跟在后面 */
}
5.4 display 属性 --- 手动切换元素类型
浏览器给了每个 HTML 标签一个默认的 display 值,但我们可以通过 display 手动切换,进而开启不同的格式化上下文(Formatting Context):
css
/* 块级 ↔ 行内自由切换 */
.box { display: block; } /* 变成块级 */
.box { display: inline; } /* 变成行内 */
三种重要的格式化上下文:
| 值 | 格式化上下文 | 特点 |
|---|---|---|
flex |
弹性格式化上下文 | 一维布局,子元素自动沿主轴排列 |
inline-block |
行内块级 | 兼具行内和块级的优点 |
grid |
网格格式化上下文 | 二维布局,行 + 列同时控制 |
5.5 inline-block --- 鱼和熊掌兼得
css
.item {
display: inline-block;
width: 50%; /* ✅ 可以设置宽高! */
/* ✅ 也不会把兄弟元素挤到下一行! */
}
inline-block 是行内元素和块级元素的"混血儿":
- 对外像行内元素 ------ 不换行,跟兄弟元素排排坐
- 对内像块级元素 ------ 可以设置宽高、margin、padding
⚠️ 天坑预警:inline-block 的空白间距
html
<!-- 注意:两个 div 之间有个换行符 -->
<div class="box">1</div>
<div class="box">2</div>
css
.box {
display: inline-block;
width: 50%;
background: red;
}

你会发现两个 50% 的盒子没有并排,第二个被挤到下一行了------为什么?
原因 :HTML 中标签之间的换行符(\n、\r)会被解析为一个空格字符 。这个空格在 inline-block 元素之间会占据一定的宽度,导致 50% + 空格 + 50% > 100%,第二个盒子只能换行。
解决方案:
html
<!-- 方案1:去掉换行(不优雅) -->
<div class="box">1</div><div class="box">2</div>
<!-- 方案2:父元素 font-size: 0(推荐) -->
<style>
.parent { font-size: 0; } /* 让空格宽度变为 0 */
.box { font-size: 16px; } /* 子元素单独设回字号 */
</style>
<!-- 方案3:用 flex 替代 inline-block(推荐) -->
<style>
.parent { display: flex; }
.box { flex: 1; }
</style>

六、定位 --- 摆放 3D 面片的核心机制
在 3D 立方体中,6 个面都需要绝对定位 叠加在同一个位置,再由 transform 各自位移旋转。这就要用到 CSS 的定位体系。
6.1 position: relative --- 相对定位
css
.parent {
position: relative; /* 建立定位参考系 */
}
- 元素仍占据原来的文档流位置
- 为子元素的
absolute提供定位锚点 - 自身可以通过
top/left/right/bottom微调位置
6.2 position: absolute --- 绝对定位
css
.child {
position: absolute;
top: 0;
left: 0;
}
- 元素脱离文档流(不占位,后面的元素会顶上来)
- 相对于最近的已定位祖先 (
position非static)定位 - 如果找不到已定位祖先,则相对于
<body>定位
黄金组合 :
父 relative + 子 absolute,这是 CSS 布局中最经典的搭配。
6.3 定位在 3D 立方体中的应用
css
.box {
position: relative; /* 父:建立坐标参考 */
}
.face {
position: absolute; /* 子:6 个面都叠在 (0,0) 原点 */
width: 200px;
height: 200px;
top: 0;
left: 0;
}
6 个面先全部叠在同一位置,再用 transform 分别推到不同方向------这就是 CSS 3D 立方体的核心思路:
css
.front { transform: translateZ(100px); } /* 向前 100px */
.back { transform: translateZ(-100px); } /* 向后 100px */
.left { transform: translateX(-100px) rotateY(-90deg); } /* 左移 + 旋转 */
.right { transform: translateX(100px) rotateY(90deg); } /* 右移 + 旋转 */
.top { transform: translateY(-100px) rotateX(90deg); } /* 上移 + 旋转 */
.bottom { transform: translateY(100px) rotateX(-90deg); } /* 下移 + 旋转 */
七、实战:完整的 3D 旋转立方体
把上面所有知识点串起来,下面是完整代码。
7.1 HTML 结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS 3D 旋转立方体</title>
<link rel="stylesheet" href="./common.css">
</head>
<body>
<!-- 外层:视距容器(3D 舞台) -->
<div class="box-wrap">
<!-- 内层:3D 空间容器 + 动画主体 -->
<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>
</body>
</html>
7.2 CSS 完整代码(带逐行注释)
css
/* ===== 全局重置 ===== */
* {
margin: 0;
padding: 0;
}
/* ===== 父容器:将立方体居中于屏幕 ===== */
html, body {
height: 100vh; /* 占满整个视口高度 */
display: flex; /* 开启弹性布局,移动端适配首选 */
flex-direction: row; /* 主轴方向(默认值,可省略) */
justify-content: center; /* 主轴居中对齐 */
align-items: center; /* 次轴居中对齐 */
}
/* ===== 外层盒子:3D 舞台(提供视距) ===== */
.box-wrap {
width: 200px;
height: 200px;
perspective: 600px; /* 👈 3D 核心属性:观察者到屏幕的距离 */
/* 值越小 → 透视感越强 → 近大远小效果越夸张 */
}
/* ===== 内层盒子:3D 空间容器 + 动画主体 ===== */
.box {
width: 100%;
height: 100%;
position: relative; /* 为子元素绝对定位提供锚点 */
transform-style: preserve-3d; /* 👈 保留子元素的 3D 空间(默认 flat 会把子元素拍扁) */
animation: rotate 5s linear infinite; /* 绑定旋转动画 */
}
/* ===== 关键帧动画 ===== */
@keyframes rotate {
100% {
/* 同时绕 X 轴和 Y 轴旋转 360 度 */
transform: rotateX(360deg) rotateY(360deg);
}
}
/* ===== 6 个面的公共样式 ===== */
.face {
width: 200px;
height: 200px;
position: absolute; /* 脱离文档流,6 个面叠在同一位置 */
top: 0;
left: 0;
/* 面上的文字居中 */
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
color: #fff;
opacity: 0.8; /* 半透明,方便看到多个面 */
}
/* ===== 6 个面各自的颜色 + 3D 位移与旋转 ===== */
/* 正面:沿 Z 轴向屏幕外推 100px(200px 边长的一半) */
.front {
background-color: #4299e1;
transform: translateZ(100px);
}
/* 背面:沿 Z 轴向屏幕内推 100px */
.back {
background-color: #f5656f;
transform: translateZ(-100px);
}
/* 左面:先左移 100px,再绕 Y 轴逆时针转 90° */
.left {
background-color: #99c266;
transform: translateX(-100px) rotateY(-90deg);
}
/* 右面:先右移 100px,再绕 Y 轴顺时针转 90° */
.right {
background-color: #6699cc;
transform: translateX(100px) rotateY(90deg);
}
/* 上面:先上移 100px,再绕 X 轴转 90° */
.top {
background-color: #65f59f;
transform: translateY(-100px) rotateX(90deg);
}
/* 下面:先下移 100px,再绕 X 轴反向转 90° */
.bottom {
background-color: #f5659f;
transform: translateY(100px) rotateX(-90deg);
}
7.3 立方体 6 个面的空间定位原理
↑ Y 轴(上)
|
┌───────┼───────┐
│ top │ │
│ │ │
├───────┼───────┤
←─── left ── │ front │ right ── → X 轴(右)
│ │ │
├───────┼───────┤
│ bottom│ │
│ │ │
└───────┼───────┘
│
back(Z 轴负方向,屏幕内)
每个面都是 200×200px
立方体中心到每个面的距离 = 100px(边长的一半)
关键理解:
- 6 个面初始位置完全相同(
absolute后都叠在原点) translateZ(100px)把前面推到离观察者近 100px 的位置translateX(-100px) rotateY(-90deg)把左面推到左边,然后旋转让它"朝向"外面- 其他面同理------先位移到对应位置,再旋转到正确的朝向
transform 的顺序很重要 :
translateX()后再rotateY()的效果,和先旋转再位移是完全不同的!CSS 的 transform 按从左到右的顺序执行。
7.4 效果预览
保存代码后在浏览器打开,你会看到一个半透明、自动旋转的 3D 立方体 ,6 个面颜色各异,平滑旋转无卡顿------这就是 GPU 加速带来的丝滑体验。

八、Flexbox 布局实战补充
上面的 3.html 演示了 Flexbox 的核心机制:
html
<div class="box">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
css
.box {
display: flex;
/* flex-direction: column; 切换主轴方向 */
}
.item {
flex: 1; /* 等分剩余空间 */
background-color: red;
text-align: center;
}
flex: 1 的精髓:
flex: 1 是 flex-grow: 1; flex-shrink: 1; flex-basis: 0% 的缩写,意思是:把父容器剩余的空间,按 1:1:1:1 的比例分给每个子元素 。4 个子元素各有 flex: 1,于是 4 等分。
css
/* 不等分示例 */
.item:nth-child(1) { flex: 2; } /* 占 2 份 */
.item:nth-child(2) { flex: 1; } /* 占 1 份 */
/* 总共 3 份,第 1 个占 2/3,第 2 个占 1/3 */
全文总结
本文从 HTML5 Canvas 出发,引出了 CSS 3D 这一更适合前端 UI 动效的技术路径。我们从底层布局知识(行内/块级、弹性布局、居中策略)一路讲到定位体系,最终用 perspective + transform-style: preserve-3d + transform + 关键帧动画 组合拳,手把手实现了一个纯 CSS 驱动的 3D 旋转立方体。
核心知识点复盘
| 知识点 | 一句话总结 |
|---|---|
| Canvas vs CSS 3D | Canvas 用 JS 画图,CSS 3D 用属性声明,后者自动走 GPU 加速 |
vh / vw |
视口单位,100vh 直接占满屏幕高度,不依赖父元素,移动端神器 |
| Flexbox 居中 | display: flex + justify-content: center + align-items: center,4 行搞定水平垂直居中 |
perspective |
定义 3D 视距,值越小透视感越强;必须设在父元素上 |
transform-style: preserve-3d |
让子元素保留在 3D 空间中,不设这个属性 3D 出不来 |
position 组合 |
父 relative + 子 absolute,6 个面叠在同一原点再各自位移 |
transform 顺序 |
从左到右执行,translate + rotate 的顺序不同结果不同 |
inline-block 空白坑 |
换行符被解析为空格,导致 50% + 空格 + 50% > 100%,用 font-size: 0 或改用 flex 解决 |
flex: 1 |
等分剩余空间,flex-grow: 1 + flex-shrink: 1 + flex-basis: 0% 的缩写 |
| GPU 加速原理 | transform 触发合成层(Composite Layer),不触发重排,动画丝滑不掉帧 |
常见问题 / 避坑指南
❓ Q1:为什么我的 3D 效果看起来是"扁的"?
90% 的情况是忘了写
transform-style: preserve-3d。这个属性必须加在包裹 6 个面的父容器 上(即.box),表示"我的子元素要在 3D 空间里待着,别拍扁它们"。默认值是flat。
❓ Q2:perspective 写在哪儿?写多大合适?
写在外层容器 上(本文的
.box-wrap),不是写在内层box上。值一般在500px ~ 1200px之间选,600px是比较自然的常用值。值越小透视感越强(变形越厉害),值越大越接近正交投影。
❓ Q3:rotateX 和 rotateY 的旋转方向怎么记?
想象一根轴穿过元素中心 ------
rotateX绕 X 轴转(像翻书,前后翻),rotateY绕 Y 轴转(像转盘,左右转)。正值方向用左手定则:大拇指指向轴的正方向,四指弯曲方向即为旋转正方向。
❓ Q4:translateZ(100px) 中的 100px 为什么是立方体边长的一半?
因为 6 个面初始在立方体的中心点,每个面的尺寸是 200×200。要把前面从中心推到立方体的前表面,距离 = 边长 ÷ 2 = 100px。如果你改成 300px 大小的面,这里就是 150px。
❓ Q5:position: absolute 后元素从哪开始算位置?
找最近的已定位祖先 (
position值不是默认的static即可),通常由父亲position: relative提供。如果找不到,就以<body>为基准。这就是为什么父元素要写position: relative。
❓ Q6:Flexbox 中 justify-content 和 align-items 总搞混?
记住口诀:justify 主,align 次 。主轴方向由
flex-direction决定,默认row时主轴是水平,所以justify-content: center= 水平居中,align-items: center= 垂直居中。如果你把flex-direction切成了column,两者就互换了。
如果这篇文章对你有帮助,欢迎点赞收藏,有问题可以在评论区留言交流 🚀