Day 15 – Flexbox 弹性盒子布局
🎯 学习目标
- 理解 Flexbox 布局的核心概念
- 掌握容器和项目的属性
- 学会创建响应式的一维布局
- 能够解决常见的布局问题
💡 核心概念
1. 什么是 Flexbox?
**Flexbox(弹性盒子)**是一种一维布局模型,用于在容器中分配空间和对齐项目:
┌─────────────────────────────────────┐
│ 传统布局 vs Flexbox │
├─────────────────────────────────────┤
│ 传统布局(Float + Position): │
│ ┌───┐ │
│ │ A │ float: left │
│ └───┘ │
│ ┌───┐ 需要清除浮动 │
│ │ B │ clear: both │
│ └───┘ 垂直居中困难 │
│ │
│ Flexbox 布局: │
│ ┌───┬───┬───┐ │
│ │ A │ B │ C │ 自动分配空间 │
│ └───┴───┴───┘ 轻松居中 │
│ 一行代码搞定! │
└─────────────────────────────────────┘
核心特点:
- ✅ 一维布局(主轴 + 交叉轴)
- ✅ 灵活的空间分配
- ✅ 强大的对齐能力
- ✅ 响应式友好
2. 主轴和交叉轴
主轴(Main Axis):
↓
┌───┬───┬───┐
│ A │ B │ C │
└───┴───┴───┘
←────────→ 主轴方向(默认从左到右)
↑
交叉轴(默认从上到下)
flex-direction: row; 主轴:左→右,交叉轴:上→下
flex-direction: row-reverse; 主轴:右→左
flex-direction: column; 主轴:上→下
flex-direction: column-reverse; 主轴:下→上
关键概念:
- 主轴(Main Axis):主要排列方向(由
flex-direction决定) - 交叉轴(Cross Axis):垂直于主轴的方向
- 主轴起点/终点:main-start / main-end
- 交叉轴起点/终点:cross-start / cross-end
📝 容器属性
1. 启用 Flexbox
.container {
display: flex; /* 块级 flex 容器 */
/* 或 */
display: inline-flex; /* 行内 flex 容器 */
}
2. 主轴方向(flex-direction)
.container {
/* 默认值:主轴从左到右 */
flex-direction: row;
/* 主轴从右到左 */
flex-direction: row-reverse;
/* 主轴从上到下 */
flex-direction: column;
/* 主轴从下到上 */
flex-direction: column-reverse;
}
可视化:
row: row-reverse: column: column-reverse:
┌───┬───┬───┐ ┌───┬───┬───┐ ┌───┐ ┌───┐
│ A │ B │ C │ │ C │ B │ A │ │ A │ │ C │
└───┴───┴───┘ └───┴───┴───┘ ├───┤ ├───┤
│ B │ │ B │
├───┤ ├───┤
│ C │ │ A │
└───┘ └───┘
3. 换行(flex-wrap)
.container {
/* 默认:不换行,所有项目在一行 */
flex-wrap: nowrap;
/* 换行,第一行在上 */
flex-wrap: wrap;
/* 换行,第一行在下 */
flex-wrap: wrap-reverse;
}
示例:
nowrap: wrap:
┌──┬──┬──┬──┐ ┌──┬──┬──┐
│A │B │C │D │ │E │F │ │
│ ← 压缩 → │ ├──┼──┼──┤
└──┴──┴──┴──┘ │A │B │C │
│D │ │ │
└──┴──┴──┘
4. 简写(flex-flow)
/* flex-flow = flex-direction + flex-wrap */
.container {
flex-flow: row wrap; /* 主轴从左到右,允许换行 */
flex-flow: column wrap; /* 主轴从上到下,允许换行 */
flex-flow: row-reverse; /* 默认 wrap 为 nowrap */
}
5. 主轴对齐(justify-content)
.container {
/* 默认:主轴起点对齐 */
justify-content: flex-start;
/* 主轴终点对齐 */
justify-content: flex-end;
/* 居中对齐 */
justify-content: center;
/* 两端对齐,项目间隔相等 */
justify-content: space-between;
/* 每个项目两侧间隔相等 */
justify-content: space-around;
/* 项目间隔完全相等 */
justify-content: space-evenly;
}
可视化:
flex-start: center: space-between: space-around: space-evenly:
┌───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┐ ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┐
│ A │ B │ C │ │ A │ B │ C │ │ A │ B │ C │ │A│B│C│D│E│ │A│B│C│D│E│
└───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┘ └─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┘
← ← ← ← ←
6. 交叉轴对齐(align-items)
.container {
/* 默认:交叉轴起点对齐 */
align-items: stretch;
/* 交叉轴起点对齐 */
align-items: flex-start;
/* 交叉轴终点对齐 */
align-items: flex-end;
/* 居中对齐 */
align-items: center;
/* 基线对齐 */
align-items: baseline;
}
可视化(假设容器高度 100px,项目高度不同):
stretch: flex-start: center: flex-end:
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
│┌─────┐│ │┌─────┐│ │┌─────┐│ │┌─────┐│
││ A ││ ││ A ││ ││ A ││ ││ A ││
│└─────┘│ │└─────┘│ │└─────┘│ │└─────┘│
│┌──────┤ │┌──────┤ │┌──────┤ │┌──────┤
││ B │ ││ B │ ││ B │ ││ B │
│└──────┘ │└──────┘ │└──────┘ │└──────┘
│┌───┐ │┌───┐ │┌───┐ │┌───┐
││ C │ ││ C │ ││ C │ ││ C │
│└───┘ │└───┘ │└───┘ │└───┘
└───────┘ └───────┘ └───────┘ └───────┘
7. 多行对齐(align-content)
.container {
/* 多行时,整体在交叉轴的对齐 */
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
注意:只有当容器有多行时(flex-wrap: wrap)才生效。
📝 项目属性
1. 放大比例(flex-grow)
.item {
/* 默认:不放大 */
flex-grow: 0;
/* 所有项目等比例放大 */
flex-grow: 1;
/* 第二个项目占 2 份 */
flex-grow: 1;
}
.item:nth-child(2) {
flex-grow: 2;
}
示例:
容器宽度 600px,三个项目初始宽度 100px
flex-grow: 0(默认):
┌────┬────┬────┐ 剩余 300px 不分配
│100 │100 │100 │
└────┴────┴────┘
flex-grow: 1(所有):
┌──────┬──────┬──────┐ 剩余 300px 平均分配
│ 200 │ 200 │ 200 │ 每个项目 200px
└──────┴──────┴──────┘
flex-grow: 1, 2, 1:
┌──────┬─────────┬──────┐ 剩余 300px 按 1:2:1 分配
│ 175 │ 250 │ 175 │ (150/4, 150*2/4, 150/4)
└──────┴─────────┴──────┘
2. 缩小比例(flex-shrink)
.item {
/* 默认:等比例缩小 */
flex-shrink: 1;
/* 不缩小 */
flex-shrink: 0;
}
示例:
容器宽度 600px,三个项目宽度 300px
flex-shrink: 1(默认):
┌────────┬────────┬────────┐ 总共 900px,超出 300px
│ 200 │ 200 │ 200 │ 每个项目缩小 100px
└────────┴────────┴────────┘
flex-shrink: 0(所有):
┌────────┬────────┬────────┐ 不会缩小,溢出容器
│ 300 │ 300 │ 300 │
└────────┴────────┴────────┘
3. 初始大小(flex-basis)
.item {
/* 默认:auto(根据内容或 width) */
flex-basis: auto;
/* 固定初始宽度 */
flex-basis: 200px;
/* 百分比 */
flex-basis: 50%;
}
4. 简写(flex)
/* flex = flex-grow + flex-shrink + flex-basis */
.item {
/* 默认值 */
flex: 0 1 auto;
/* 常用简写 */
flex: 1; /* flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
flex: auto; /* flex-grow: 1, flex-shrink: 1, flex-basis: auto */
flex: none; /* flex-grow: 0, flex-shrink: 0, flex-basis: auto */
/* 完整写法 */
flex: 2 1 200px; /* grow: 2, shrink: 1, basis: 200px */
}
5. 单独对齐(align-self)
.item {
/* 覆盖容器的 align-items */
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
示例:
.container {
display: flex;
align-items: flex-start; /* 默认顶部对齐 */
}
.special {
align-self: center; /* 这个项目垂直居中 */
}
6. 排序(order)
.item {
/* 默认:0 */
order: 0;
/* 改变顺序(数值越小越靠前) */
order: -1; /* 排在最前 */
order: 1; /* 排在后面 */
}
示例:
<div class="container">
<div class="item" style="order: 2">A</div>
<div class="item" style="order: 3">B</div>
<div class="item" style="order: 1">C</div>
</div>
/* 显示顺序:C → A → B */
🎮 实战示例
示例 1:完美居中
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>完美居中 - Flexbox</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
background: white;
padding: 60px;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
text-align: center;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 2.5rem;
}
p {
color: #7f8c8d;
font-size: 1.2rem;
line-height: 1.6;
}
.code {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-top: 20px;
text-align: left;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
color: #2c3e50;
}
.highlight {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>完美居中</h1>
<p>使用 Flexbox 只需要 <span class="highlight">三行代码</span></p>
<div class="code">
.container {<br>
display: flex;<br>
justify-content: center;<br>
align-items: center;<br>
}
</div>
</div>
</body>
</html>
示例 2:导航栏布局
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>导航栏 - Flexbox</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: #f5f7fa;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
background: white;
padding: 0 40px;
height: 70px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 1000;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: #667eea;
text-decoration: none;
}
.nav-links {
display: flex;
list-style: none;
gap: 30px;
}
.nav-links a {
text-decoration: none;
color: #2c3e50;
font-size: 1rem;
transition: color 0.3s;
position: relative;
}
.nav-links a::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 0;
height: 2px;
background: #667eea;
transition: width 0.3s;
}
.nav-links a:hover {
color: #667eea;
}
.nav-links a:hover::after {
width: 100%;
}
.nav-buttons {
display: flex;
gap: 15px;
}
.btn {
padding: 10px 25px;
border-radius: 25px;
text-decoration: none;
font-size: 0.9rem;
transition: all 0.3s;
}
.btn-login {
background: transparent;
color: #667eea;
border: 2px solid #667eea;
}
.btn-login:hover {
background: #667eea;
color: white;
}
.btn-signup {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
}
.btn-signup:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar {
padding: 0 20px;
}
.nav-links {
display: none;
}
}
</style>
</head>
<body>
<nav class="navbar">
<a href="#" class="logo">🚀 FlexNav</a>
<ul class="nav-links">
<li><a href="#">首页</a></li>
<li><a href="#">功能</a></li>
<li><a href="#">价格</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">联系</a></li>
</ul>
<div class="nav-buttons">
<a href="#" class="btn btn-login">登录</a>
<a href="#" class="btn btn-signup">注册</a>
</div>
</nav>
<div style="padding: 60px; text-align: center;">
<h1 style="color: #2c3e50; font-size: 2.5rem; margin-bottom: 20px;">
现代导航栏布局
</h1>
<p style="color: #7f8c8d; font-size: 1.2rem; line-height: 1.8; max-width: 600px; margin: 0 auto;">
使用 Flexbox 可以轻松创建响应式导航栏,
支持元素对齐、空间分配和自适应布局。
</p>
</div>
</body>
</html>
示例 3:卡片网格布局
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>卡片网格 - Flexbox</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: #f5f7fa;
padding: 40px 20px;
}
.header {
text-align: center;
margin-bottom: 50px;
}
.header h1 {
color: #2c3e50;
font-size: 2.5rem;
margin-bottom: 10px;
}
.header p {
color: #7f8c8d;
font-size: 1.1rem;
}
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 25px;
max-width: 1200px;
margin: 0 auto;
justify-content: center;
}
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
transition: transform 0.3s, box-shadow 0.3s;
flex: 1 1 300px;
max-width: 380px;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
.card-image {
height: 200px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
font-size: 4rem;
}
.card:nth-child(2) .card-image {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.card:nth-child(3) .card-image {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.card-content {
padding: 25px;
}
.card-title {
font-size: 1.4rem;
color: #2c3e50;
margin-bottom: 10px;
}
.card-description {
color: #7f8c8d;
line-height: 1.6;
margin-bottom: 15px;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-tag {
background: #ecf0f1;
padding: 5px 12px;
border-radius: 20px;
font-size: 0.85rem;
color: #7f8c8d;
}
.card-button {
background: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.3s;
}
.card-button:hover {
background: #2980b9;
}
@media (max-width: 768px) {
.card {
flex: 1 1 100%;
max-width: 100%;
}
}
</style>
</head>
<body>
<div class="header">
<h1>响应式卡片网格</h1>
<p>使用 Flexbox 创建自适应布局</p>
</div>
<div class="card-grid">
<div class="card">
<div class="card-image">🎨</div>
<div class="card-content">
<h3 class="card-title">弹性布局</h3>
<p class="card-description">
Flexbox 让创建响应式布局变得简单,
自动适应不同屏幕尺寸。
</p>
<div class="card-footer">
<span class="card-tag">布局</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
<div class="card">
<div class="card-image">⚡</div>
<div class="card-content">
<h3 class="card-title">灵活对齐</h3>
<p class="card-description">
轻松实现水平、垂直居中,
以及各种对齐方式。
</p>
<div class="card-footer">
<span class="card-tag">对齐</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
<div class="card">
<div class="card-image">📱</div>
<div class="card-content">
<h3 class="card-title">响应式设计</h3>
<p class="card-description">
配合媒体查询,
完美支持移动设备。
</p>
<div class="card-footer">
<span class="card-tag">移动端</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
</div>
<script>
document.querySelectorAll('.card-button').forEach(button => {
button.addEventListener('click', function() {
const card = this.closest('.card');
const title = card.querySelector('.card-title').textContent;
alert(`你点击了:${title}`);
});
});
</script>
</body>
</html>
示例 4:圣杯布局(三栏布局)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>圣杯布局 - Flexbox</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px 40px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header h1 {
font-size: 1.8rem;
}
.main-container {
display: flex;
flex: 1;
}
.sidebar-left {
width: 200px;
background: #2c3e50;
color: white;
padding: 20px;
}
.sidebar-left h3 {
margin-bottom: 15px;
color: #ecf0f1;
}
.sidebar-left ul {
list-style: none;
}
.sidebar-left li {
padding: 10px 0;
border-bottom: 1px solid #34495e;
}
.sidebar-left a {
color: #bdc3c7;
text-decoration: none;
transition: color 0.3s;
}
.sidebar-left a:hover {
color: white;
}
.content {
flex: 1;
padding: 30px;
background: #ecf0f1;
}
.content h2 {
color: #2c3e50;
margin-bottom: 15px;
font-size: 1.8rem;
}
.content p {
color: #7f8c8d;
line-height: 1.8;
margin-bottom: 15px;
}
.sidebar-right {
width: 200px;
background: #34495e;
color: white;
padding: 20px;
}
.sidebar-right h3 {
margin-bottom: 15px;
}
.ad-item {
background: #2c3e50;
padding: 15px;
margin-bottom: 15px;
border-radius: 8px;
}
.footer {
background: #1a252f;
color: #bdc3c7;
padding: 20px;
text-align: center;
}
/* 响应式设计 */
@media (max-width: 900px) {
.main-container {
flex-direction: column;
}
.sidebar-left,
.sidebar-right {
width: 100%;
}
}
</style>
</head>
<body>
<header class="header">
<h1>🎨 Flexbox 圣杯布局</h1>
<nav>
<a href="#" style="color: white; text-decoration: none; margin-left: 20px;">首页</a>
<a href="#" style="color: white; text-decoration: none; margin-left: 20px;">关于</a>
</nav>
</header>
<div class="main-container">
<aside class="sidebar-left">
<h3>📚 导航</h3>
<ul>
<li><a href="#">HTML 基础</a></li>
<li><a href="#">CSS 样式</a></li>
<li><a href="#">JavaScript</a></li>
<li><a href="#">响应式设计</a></li>
</ul>
</aside>
<main class="content">
<h2>什么是圣杯布局?</h2>
<p>
圣杯布局是网页设计中经典的布局模式,包含页眉、页脚、
主内容区、左侧边栏和右侧边栏。
</p>
<h2>Flexbox 的优势</h2>
<p>
使用 Flexbox 可以轻松实现这种布局,
不需要复杂的浮动和定位。
</p>
<h2>响应式设计</h2>
<p>
通过媒体查询和 Flexbox 的灵活性,
我们可以轻松创建适应不同屏幕尺寸的布局。
</p>
</main>
<aside class="sidebar-right">
<h3>📢 广告</h3>
<div class="ad-item">
<h4>推荐课程</h4>
<p>学习 Flexbox 布局!</p>
</div>
<div class="ad-item">
<h4>最新文章</h4>
<p>CSS Flexbox 完全指南</p>
</div>
</aside>
</div>
<footer class="footer">
<p>© 2024 Flexbox 布局示例</p>
</footer>
</body>
</html>
⚠️ 重要注意事项
1. Flexbox vs Grid
选择 Flexbox 当:
✅ 一维布局(行或列)
✅ 内容驱动布局
✅ 简单的对齐需求
✅ 导航栏、卡片组件
选择 Grid 当:
✅ 二维布局(行和列同时控制)
✅ 布局驱动内容
✅ 复杂的页面结构
✅ 整体页面布局
2. 浏览器兼容性
现代浏览器完全支持 Flexbox:
- ✅ Chrome 29+
- ✅ Firefox 28+
- ✅ Safari 9+
- ✅ Edge 12+
- ✅ iOS Safari 9+
前缀处理(旧浏览器):
.container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.item {
-webkit-box-flex: 1;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
}
3. 常见陷阱
陷阱 1:忘记重置默认样式
/* 某些浏览器默认有 min-width: auto */
.item {
min-width: 0; /* 允许缩小到内容以下 */
}
陷阱 2:百分比宽度问题
/* 错误 */
.item {
width: 50%; /* 在 flex 容器中可能不工作 */
flex: 1; /* 应该使用 flex 而不是 width */
}
陷阱 3:垂直居中不生效
/* 确保容器有高度 */
.container {
display: flex;
align-items: center;
min-height: 100vh; /* 必须有高度 */
}
✍️ 练习任务
基础练习
-
创建居中布局
- 使用 Flexbox 实现完美居中
- 水平和垂直都居中
- 添加多个项目测试
-
导航栏
- 创建响应式导航栏
- Logo 在左,链接居中,按钮在右
- 移动端隐藏部分元素
-
卡片布局
- 创建卡片网格
- 使用
flex-wrap实现响应式 - 卡片等高、等宽
进阶练习
-
圣杯布局
- 实现经典三栏布局
- 左侧边栏固定宽度
- 中间内容自适应
- 右侧边栏固定宽度
-
flex 属性练习
- 创建三个项目
- 第一个:
flex: 1 - 第二个:
flex: 2 - 第三个:
flex: 1 - 观察空间分配
-
对齐方式
- 尝试所有
justify-content值 - 尝试所有
align-items值 - 使用
align-self单独控制项目
- 尝试所有
实战挑战
-
响应式相册
- 使用 Flexbox 创建照片网格
- 悬停时放大效果
- 移动端单列显示
-
仪表板
- 顶部:标题栏
- 左侧:导航菜单
- 中间:主要内容
- 右侧:统计信息
-
社交媒体卡片
- 头像 + 用户名
- 发布内容
- 底部操作按钮(点赞、评论、分享)
- 使用 Flexbox 对齐
💡 常见问题 FAQ
Q1: Flexbox 可以实现瀑布流布局吗?
答:原生 Flexbox 不支持瀑布流,需要使用 CSS Columns 或 JavaScript 库(如 Masonry.js)。
/* 使用 CSS Columns */
.waterfall {
column-count: 3;
column-gap: 20px;
}
Q2: 为什么项目不等高?
答:默认 align-items: stretch 会拉伸项目。如果设置了固定高度或 align-items: flex-start,项目将不等高。
/* 解决方案 */
.container {
align-items: stretch; /* 默认值 */
}
.item {
height: auto; /* 不要设置固定高度 */
}
``