本章概述
响应式设计是现代Web开发的核心理念,Bootstrap采用移动优先的设计策略,确保网站在各种设备上都能提供优秀的用户体验。本章将深入探讨Bootstrap的响应式设计系统,包括断点系统、网格布局、响应式工具类以及移动优先的设计原则。
学习目标
- 理解移动优先设计理念
- 掌握Bootstrap断点系统
- 学会使用响应式网格布局
- 掌握响应式工具类的使用
- 学会创建适配不同设备的界面
- 了解响应式图片和媒体处理
- 掌握响应式导航设计
Bootstrap断点系统
1. 断点定义
Bootstrap 5定义了6个主要断点,用于创建响应式布局:
// Bootstrap 5 断点
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
2. 断点使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap 断点系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.breakpoint-demo {
padding: 1rem;
margin: 0.5rem 0;
border-radius: 0.375rem;
text-align: center;
font-weight: bold;
}
.xs-demo { background-color: #f8f9fa; }
.sm-demo { background-color: #e9ecef; }
.md-demo { background-color: #dee2e6; }
.lg-demo { background-color: #ced4da; }
.xl-demo { background-color: #adb5bd; }
.xxl-demo { background-color: #6c757d; color: white; }
</style>
</head>
<body>
<div class="container my-5">
<h2 class="text-primary mb-4">Bootstrap 断点系统演示</h2>
<!-- 断点可视化 -->
<div class="row">
<div class="col-12">
<div class="breakpoint-demo xs-demo d-block d-sm-none">
XS: 超小屏幕 (< 576px)
</div>
<div class="breakpoint-demo sm-demo d-none d-sm-block d-md-none">
SM: 小屏幕 (≥ 576px)
</div>
<div class="breakpoint-demo md-demo d-none d-md-block d-lg-none">
MD: 中等屏幕 (≥ 768px)
</div>
<div class="breakpoint-demo lg-demo d-none d-lg-block d-xl-none">
LG: 大屏幕 (≥ 992px)
</div>
<div class="breakpoint-demo xl-demo d-none d-xl-block d-xxl-none">
XL: 超大屏幕 (≥ 1200px)
</div>
<div class="breakpoint-demo xxl-demo d-none d-xxl-block">
XXL: 超超大屏幕 (≥ 1400px)
</div>
</div>
</div>
<!-- 响应式列布局 -->
<div class="mt-5">
<h4>响应式列布局示例</h4>
<div class="row">
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-primary text-white p-3 mb-3 text-center">
响应式列 1
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-secondary text-white p-3 mb-3 text-center">
响应式列 2
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-success text-white p-3 mb-3 text-center">
响应式列 3
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-danger text-white p-3 mb-3 text-center">
响应式列 4
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-warning text-dark p-3 mb-3 text-center">
响应式列 5
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="bg-info text-white p-3 mb-3 text-center">
响应式列 6
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</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>响应式表单</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.form-container {
background: white;
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
overflow: hidden;
}
.form-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
text-align: center;
}
.form-step {
display: none;
}
.form-step.active {
display: block;
}
.step-indicator {
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
.step {
width: 40px;
height: 40px;
border-radius: 50%;
background: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
margin: 0 0.5rem;
font-weight: bold;
color: #6c757d;
position: relative;
}
.step.active {
background: #007bff;
color: white;
}
.step.completed {
background: #28a745;
color: white;
}
.step:not(:last-child)::after {
content: '';
position: absolute;
top: 50%;
left: 100%;
width: 2rem;
height: 2px;
background: #e9ecef;
transform: translateY(-50%);
}
.step.completed:not(:last-child)::after {
background: #28a745;
}
@media (max-width: 575.98px) {
.form-header {
padding: 1.5rem 1rem;
}
.step {
width: 30px;
height: 30px;
font-size: 0.875rem;
margin: 0 0.25rem;
}
.step:not(:last-child)::after {
width: 1rem;
}
}
</style>
</head>
<body>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6">
<div class="form-container">
<div class="form-header">
<h3 class="mb-2">用户注册</h3>
<p class="mb-0 opacity-75">请填写以下信息完成注册</p>
</div>
<div class="p-4">
<!-- 步骤指示器 -->
<div class="step-indicator">
<div class="step active" data-step="1">1</div>
<div class="step" data-step="2">2</div>
<div class="step" data-step="3">3</div>
</div>
<form id="registrationForm">
<!-- 第一步:基本信息 -->
<div class="form-step active" data-step="1">
<h5 class="mb-3">基本信息</h5>
<div class="row">
<div class="col-12 col-sm-6 mb-3">
<label for="firstName" class="form-label">姓</label>
<input type="text" class="form-control" id="firstName" required>
</div>
<div class="col-12 col-sm-6 mb-3">
<label for="lastName" class="form-label">名</label>
<input type="text" class="form-control" id="lastName" required>
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">邮箱地址</label>
<input type="email" class="form-control" id="email" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">手机号码</label>
<input type="tel" class="form-control" id="phone" required>
</div>
<div class="d-flex justify-content-end">
<button type="button" class="btn btn-primary" onclick="nextStep()">下一步</button>
</div>
</div>
<!-- 第二步:账户信息 -->
<div class="form-step" data-step="2">
<h5 class="mb-3">账户信息</h5>
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input type="text" class="form-control" id="username" required>
<div class="form-text">用户名必须是3-20个字符</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control" id="password" required>
<div class="form-text">密码必须包含至少8个字符</div>
</div>
<div class="mb-3">
<label for="confirmPassword" class="form-label">确认密码</label>
<input type="password" class="form-control" id="confirmPassword" required>
</div>
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-outline-secondary" onclick="prevStep()">上一步</button>
<button type="button" class="btn btn-primary" onclick="nextStep()">下一步</button>
</div>
</div>
<!-- 第三步:个人偏好 -->
<div class="form-step" data-step="3">
<h5 class="mb-3">个人偏好</h5>
<div class="mb-3">
<label for="country" class="form-label">国家/地区</label>
<select class="form-select" id="country" required>
<option value="">请选择国家/地区</option>
<option value="CN">中国</option>
<option value="US">美国</option>
<option value="JP">日本</option>
<option value="KR">韩国</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">兴趣爱好</label>
<div class="row">
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="tech" value="tech">
<label class="form-check-label" for="tech">科技</label>
</div>
</div>
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sports" value="sports">
<label class="form-check-label" for="sports">运动</label>
</div>
</div>
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="music" value="music">
<label class="form-check-label" for="music">音乐</label>
</div>
</div>
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="travel" value="travel">
<label class="form-check-label" for="travel">旅行</label>
</div>
</div>
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="reading" value="reading">
<label class="form-check-label" for="reading">阅读</label>
</div>
</div>
<div class="col-6 col-sm-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="cooking" value="cooking">
<label class="form-check-label" for="cooking">烹饪</label>
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="newsletter" value="newsletter">
<label class="form-check-label" for="newsletter">
订阅我们的新闻通讯
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="terms" required>
<label class="form-check-label" for="terms">
我同意 <a href="#">服务条款</a> 和 <a href="#">隐私政策</a>
</label>
</div>
</div>
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-outline-secondary" onclick="prevStep()">上一步</button>
<button type="submit" class="btn btn-success">完成注册</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
let currentStep = 1;
const totalSteps = 3;
function showStep(step) {
// 隐藏所有步骤
document.querySelectorAll('.form-step').forEach(el => {
el.classList.remove('active');
});
// 显示当前步骤
document.querySelector(`[data-step="${step}"]`).classList.add('active');
// 更新步骤指示器
document.querySelectorAll('.step').forEach((el, index) => {
const stepNumber = index + 1;
el.classList.remove('active', 'completed');
if (stepNumber < step) {
el.classList.add('completed');
} else if (stepNumber === step) {
el.classList.add('active');
}
});
}
function nextStep() {
if (validateCurrentStep() && currentStep < totalSteps) {
currentStep++;
showStep(currentStep);
}
}
function prevStep() {
if (currentStep > 1) {
currentStep--;
showStep(currentStep);
}
}
function validateCurrentStep() {
const currentStepElement = document.querySelector(`[data-step="${currentStep}"]`);
const requiredFields = currentStepElement.querySelectorAll('[required]');
for (let field of requiredFields) {
if (!field.value.trim()) {
field.focus();
field.classList.add('is-invalid');
return false;
} else {
field.classList.remove('is-invalid');
field.classList.add('is-valid');
}
}
// 特殊验证:密码确认
if (currentStep === 2) {
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirmPassword').value;
if (password !== confirmPassword) {
document.getElementById('confirmPassword').classList.add('is-invalid');
alert('密码和确认密码不匹配');
return false;
}
}
return true;
}
// 表单提交
document.getElementById('registrationForm').addEventListener('submit', function(e) {
e.preventDefault();
if (validateCurrentStep()) {
alert('注册成功!');
// 这里可以添加实际的表单提交逻辑
}
});
// 实时验证
document.querySelectorAll('input, select').forEach(field => {
field.addEventListener('blur', function() {
if (this.hasAttribute('required') && !this.value.trim()) {
this.classList.add('is-invalid');
this.classList.remove('is-valid');
} else {
this.classList.remove('is-invalid');
this.classList.add('is-valid');
}
});
});
</script>
</body>
</html>
响应式可访问性
1. 键盘导航支持
// 响应式键盘导航类
class ResponsiveKeyboardNavigation {
constructor() {
this.init();
}
init() {
this.setupFocusManagement();
this.setupSkipLinks();
this.setupMobileNavigation();
this.setupModalNavigation();
}
// 焦点管理
setupFocusManagement() {
// 为所有交互元素添加焦点样式
const focusableElements = 'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])';
document.querySelectorAll(focusableElements).forEach(element => {
element.addEventListener('focus', function() {
this.classList.add('keyboard-focus');
});
element.addEventListener('blur', function() {
this.classList.remove('keyboard-focus');
});
});
// 添加焦点样式
const style = document.createElement('style');
style.textContent = `
.keyboard-focus {
outline: 2px solid #007bff !important;
outline-offset: 2px !important;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25) !important;
}
@media (max-width: 767.98px) {
.keyboard-focus {
outline-width: 3px !important;
outline-offset: 3px !important;
}
}
`;
document.head.appendChild(style);
}
// 跳转链接
setupSkipLinks() {
const skipLink = document.createElement('a');
skipLink.href = '#main-content';
skipLink.textContent = '跳转到主要内容';
skipLink.className = 'skip-link';
// 跳转链接样式
const style = document.createElement('style');
style.textContent = `
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 9999;
border-radius: 4px;
}
.skip-link:focus {
top: 6px;
}
@media (max-width: 767.98px) {
.skip-link {
left: 10px;
padding: 12px;
font-size: 1.1rem;
}
.skip-link:focus {
top: 10px;
}
}
`;
document.head.appendChild(style);
document.body.insertBefore(skipLink, document.body.firstChild);
}
// 移动端导航
setupMobileNavigation() {
const navToggle = document.querySelector('.navbar-toggler');
const navMenu = document.querySelector('.navbar-collapse');
if (navToggle && navMenu) {
navToggle.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
navToggle.click();
}
});
// ESC 键关闭菜单
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && navMenu.classList.contains('show')) {
navToggle.click();
navToggle.focus();
}
});
}
}
// 模态框导航
setupModalNavigation() {
document.addEventListener('shown.bs.modal', (e) => {
const modal = e.target;
const focusableElements = modal.querySelectorAll(
'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length > 0) {
focusableElements[0].focus();
}
// Tab 键循环
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
});
}
}
// 初始化键盘导航
new ResponsiveKeyboardNavigation();
2. ARIA 标签优化
<!-- 响应式导航栏 ARIA 优化 -->
<nav class="navbar navbar-expand-lg" role="navigation" aria-label="主导航">
<div class="container">
<a class="navbar-brand" href="#" aria-label="网站首页">
<img src="logo.png" alt="公司标志" width="30" height="30">
公司名称
</a>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="切换导航菜单">
<span class="navbar-toggler-icon" aria-hidden="true"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav" role="menubar">
<li class="nav-item" role="none">
<a class="nav-link" href="#" role="menuitem" aria-current="page">首页</a>
</li>
<li class="nav-item dropdown" role="none">
<a class="nav-link dropdown-toggle"
href="#"
role="menuitem"
data-bs-toggle="dropdown"
aria-expanded="false"
aria-haspopup="true">
产品
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="产品菜单">
<li role="none">
<a class="dropdown-item" href="#" role="menuitem">产品A</a>
</li>
<li role="none">
<a class="dropdown-item" href="#" role="menuitem">产品B</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- 响应式表单 ARIA 优化 -->
<form role="form" aria-labelledby="form-title">
<h2 id="form-title">用户注册表单</h2>
<fieldset>
<legend>基本信息</legend>
<div class="mb-3">
<label for="email" class="form-label">邮箱地址 *</label>
<input type="email"
class="form-control"
id="email"
required
aria-required="true"
aria-describedby="email-help email-error">
<div id="email-help" class="form-text">我们不会分享您的邮箱地址</div>
<div id="email-error" class="invalid-feedback" aria-live="polite"></div>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码 *</label>
<input type="password"
class="form-control"
id="password"
required
aria-required="true"
aria-describedby="password-help"
minlength="8">
<div id="password-help" class="form-text">
密码必须至少包含8个字符,包括字母和数字
</div>
</div>
</fieldset>
<fieldset>
<legend>偏好设置</legend>
<div class="mb-3" role="group" aria-labelledby="interests-label">
<div id="interests-label" class="form-label">兴趣爱好</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="tech" value="tech">
<label class="form-check-label" for="tech">科技</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sports" value="sports">
<label class="form-check-label" for="sports">运动</label>
</div>
</div>
</fieldset>
<button type="submit" class="btn btn-primary" aria-describedby="submit-help">
提交注册
</button>
<div id="submit-help" class="form-text">
点击提交即表示您同意我们的服务条款
</div>
</form>
<!-- 响应式表格 ARIA 优化 -->
<div class="table-responsive" role="region" aria-labelledby="table-title" tabindex="0">
<h3 id="table-title">用户数据表</h3>
<table class="table" role="table">
<caption class="visually-hidden">用户信息列表,包含姓名、邮箱、状态等信息</caption>
<thead>
<tr role="row">
<th scope="col" role="columnheader">姓名</th>
<th scope="col" role="columnheader">邮箱</th>
<th scope="col" role="columnheader">状态</th>
<th scope="col" role="columnheader">操作</th>
</tr>
</thead>
<tbody>
<tr role="row">
<td role="cell">张三</td>
<td role="cell">zhangsan@example.com</td>
<td role="cell">
<span class="badge bg-success" aria-label="状态:活跃">活跃</span>
</td>
<td role="cell">
<button class="btn btn-sm btn-outline-primary" aria-label="编辑张三的信息">
编辑
</button>
<button class="btn btn-sm btn-outline-danger" aria-label="删除张三的账户">
删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
性能优化建议
1. 响应式图片优化
<!-- 响应式图片优化 -->
<picture>
<!-- 移动端优化图片 -->
<source media="(max-width: 575px)"
srcset="image-mobile-320w.webp 320w,
image-mobile-480w.webp 480w"
sizes="100vw"
type="image/webp">
<!-- 平板端优化图片 -->
<source media="(max-width: 991px)"
srcset="image-tablet-768w.webp 768w,
image-tablet-1024w.webp 1024w"
sizes="100vw"
type="image/webp">
<!-- 桌面端优化图片 -->
<source media="(min-width: 992px)"
srcset="image-desktop-1200w.webp 1200w,
image-desktop-1920w.webp 1920w"
sizes="100vw"
type="image/webp">
<!-- 回退图片 -->
<img src="image-fallback.jpg"
alt="产品图片"
class="img-fluid"
loading="lazy"
decoding="async">
</picture>
<!-- 背景图片优化 -->
<div class="hero-section"
data-bg-mobile="hero-mobile.webp"
data-bg-tablet="hero-tablet.webp"
data-bg-desktop="hero-desktop.webp">
<div class="container">
<h1>响应式背景图片</h1>
</div>
</div>
<script>
// 响应式背景图片加载
function loadResponsiveBackground() {
const elements = document.querySelectorAll('[data-bg-mobile]');
elements.forEach(element => {
let bgImage;
if (window.innerWidth <= 575) {
bgImage = element.dataset.bgMobile;
} else if (window.innerWidth <= 991) {
bgImage = element.dataset.bgTablet;
} else {
bgImage = element.dataset.bgDesktop;
}
if (bgImage) {
const img = new Image();
img.onload = () => {
element.style.backgroundImage = `url(${bgImage})`;
};
img.src = bgImage;
}
});
}
// 初始加载和窗口大小改变时重新加载
loadResponsiveBackground();
window.addEventListener('resize', debounce(loadResponsiveBackground, 250));
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
</script>
2. CSS 优化
/* 关键 CSS 内联优化 */
/* 将首屏关键样式内联到 HTML 中 */
<style>
/* 关键路径 CSS */
.container {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
}
/* 首屏内容样式 */
.hero-section {
min-height: 100vh;
display: flex;
align-items: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn {
display: inline-block;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.375rem;
text-decoration: none;
border: 1px solid transparent;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
border-color: #007bff;
color: #fff;
}
</style>
/* 非关键 CSS 异步加载 */
<link rel="preload" href="bootstrap.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="bootstrap.min.css"></noscript>
3. JavaScript 优化
// 响应式组件懒加载
class ResponsiveLazyLoader {
constructor() {
this.components = new Map();
this.observer = null;
this.init();
}
init() {
// 创建 Intersection Observer
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
{
rootMargin: '50px',
threshold: 0.1
}
);
// 注册组件
this.registerComponents();
// 监听窗口大小变化
window.addEventListener('resize',
this.debounce(this.handleResize.bind(this), 250)
);
}
registerComponents() {
// 注册轮播图组件
this.components.set('carousel', {
selector: '[data-lazy="carousel"]',
mobile: () => import('./components/mobile-carousel.js'),
desktop: () => import('./components/desktop-carousel.js')
});
// 注册图表组件
this.components.set('chart', {
selector: '[data-lazy="chart"]',
mobile: () => import('./components/mobile-chart.js'),
desktop: () => import('./components/desktop-chart.js')
});
// 注册数据表格组件
this.components.set('datatable', {
selector: '[data-lazy="datatable"]',
mobile: () => import('./components/mobile-table.js'),
desktop: () => import('./components/desktop-table.js')
});
// 开始观察所有组件
this.observeComponents();
}
observeComponents() {
this.components.forEach((config, name) => {
const elements = document.querySelectorAll(config.selector);
elements.forEach(element => {
element.dataset.componentName = name;
this.observer.observe(element);
});
});
}
async handleIntersection(entries) {
for (const entry of entries) {
if (entry.isIntersecting) {
const element = entry.target;
const componentName = element.dataset.componentName;
const config = this.components.get(componentName);
if (config && !element.dataset.loaded) {
await this.loadComponent(element, config);
element.dataset.loaded = 'true';
this.observer.unobserve(element);
}
}
}
}
async loadComponent(element, config) {
try {
// 显示加载指示器
this.showLoadingIndicator(element);
// 根据屏幕尺寸选择合适的组件
const isMobile = window.innerWidth < 768;
const moduleLoader = isMobile ? config.mobile : config.desktop;
// 动态导入组件
const module = await moduleLoader();
const Component = module.default;
// 初始化组件
new Component(element);
// 隐藏加载指示器
this.hideLoadingIndicator(element);
} catch (error) {
console.error(`Failed to load component:`, error);
this.showErrorIndicator(element);
}
}
showLoadingIndicator(element) {
const indicator = document.createElement('div');
indicator.className = 'loading-indicator';
indicator.innerHTML = `
<div class="spinner-border" role="status">
<span class="visually-hidden">加载中...</span>
</div>
`;
element.appendChild(indicator);
}
hideLoadingIndicator(element) {
const indicator = element.querySelector('.loading-indicator');
if (indicator) {
indicator.remove();
}
}
showErrorIndicator(element) {
const indicator = element.querySelector('.loading-indicator');
if (indicator) {
indicator.innerHTML = `
<div class="alert alert-warning" role="alert">
<i class="bi bi-exclamation-triangle"></i>
组件加载失败,请刷新页面重试
</div>
`;
}
}
handleResize() {
// 窗口大小改变时重新评估组件
const loadedElements = document.querySelectorAll('[data-loaded="true"]');
loadedElements.forEach(async element => {
const componentName = element.dataset.componentName;
const config = this.components.get(componentName);
if (config) {
// 清除当前组件
element.innerHTML = '';
element.dataset.loaded = 'false';
// 重新加载适合当前屏幕尺寸的组件
await this.loadComponent(element, config);
element.dataset.loaded = 'true';
}
});
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
// 初始化懒加载器
const lazyLoader = new ResponsiveLazyLoader();
// 预加载关键资源
class ResourcePreloader {
constructor() {
this.preloadCriticalResources();
}
preloadCriticalResources() {
// 预加载关键字体
this.preloadFont('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
// 预加载关键图片
this.preloadImage('/images/hero-background.webp');
// 预加载关键 JavaScript
this.preloadScript('/js/critical.js');
}
preloadFont(href) {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = href;
link.onload = () => {
link.rel = 'stylesheet';
};
document.head.appendChild(link);
}
preloadImage(src) {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = src;
document.head.appendChild(link);
}
preloadScript(src) {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'script';
link.href = src;
document.head.appendChild(link);
}
}
// 初始化资源预加载器
new ResourcePreloader();
本章总结
在本章中,我们深入学习了 Bootstrap 的响应式设计与移动优先原则:
核心概念
- 断点系统:掌握了 Bootstrap 的五个断点及其应用
- 移动优先:理解了从小屏幕开始设计的重要性
- 响应式网格:学会了创建灵活的布局系统
- 工具类:掌握了响应式显示、对齐、间距等工具类
实践技能
- 响应式导航:创建了适应不同屏幕的导航系统
- 响应式表格:实现了桌面表格和移动卡片的切换
- 响应式表单:设计了多步骤的响应式表单
- 响应式图片:优化了不同设备的图片加载
高级技术
- 自定义断点:学会了扩展 Bootstrap 的断点系统
- 可访问性:实现了键盘导航和 ARIA 标签优化
- 性能优化:掌握了懒加载和资源预加载技术
最佳实践
- 始终采用移动优先的设计思路
- 合理使用断点,避免过度复杂化
- 注重可访问性和用户体验
- 优化性能,特别是移动端的加载速度
- 测试不同设备和屏幕尺寸的显示效果
响应式设计是现代 Web 开发的基础,通过本章的学习,您已经掌握了使用 Bootstrap 创建优秀响应式网站的核心技能。
练习题
基础练习
- 创建一个响应式的产品展示页面,包含网格布局和卡片组件
- 实现一个响应式的联系表单,在移动端使用步骤式设计
- 设计一个响应式的图片画廊,支持不同尺寸的图片显示
进阶练习
- 创建一个完整的响应式电商网站首页
- 实现一个响应式的后台管理界面
- 设计一个支持多种设备的在线学习平台
挑战练习
- 创建一个自定义的响应式组件库
- 实现一个高性能的响应式图片懒加载系统
- 设计一个完全可访问的响应式 Web 应用
通过这些练习,您将能够熟练运用 Bootstrap 的响应式设计功能,创建出适应各种设备的优秀网站。
移动优先设计原则
1. 移动优先CSS
/* 移动优先设计示例 */
/* 基础样式(移动设备) */
.mobile-first-card {
padding: 1rem;
margin-bottom: 1rem;
background-color: #f8f9fa;
border-radius: 0.375rem;
text-align: center;
}
.mobile-first-card h3 {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.mobile-first-card p {
font-size: 0.875rem;
line-height: 1.4;
}
/* 小屏幕及以上 */
@media (min-width: 576px) {
.mobile-first-card {
padding: 1.5rem;
text-align: left;
}
.mobile-first-card h3 {
font-size: 1.5rem;
}
.mobile-first-card p {
font-size: 1rem;
}
}
/* 中等屏幕及以上 */
@media (min-width: 768px) {
.mobile-first-card {
padding: 2rem;
display: flex;
align-items: center;
}
.mobile-first-card h3 {
font-size: 1.75rem;
margin-right: 1rem;
margin-bottom: 0;
flex-shrink: 0;
}
}
/* 大屏幕及以上 */
@media (min-width: 992px) {
.mobile-first-card {
padding: 2.5rem;
}
.mobile-first-card h3 {
font-size: 2rem;
}
.mobile-first-card p {
font-size: 1.125rem;
}
}
2. 移动优先布局示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>移动优先设计</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.mobile-first-demo {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
}
.feature-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 1rem;
padding: 2rem;
margin-bottom: 1.5rem;
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-icon {
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1rem;
font-size: 1.5rem;
}
@media (min-width: 768px) {
.feature-icon {
margin: 0 0 1rem 0;
}
}
</style>
</head>
<body>
<div class="mobile-first-demo">
<div class="container py-5">
<!-- 头部区域 -->
<div class="row justify-content-center text-center mb-5">
<div class="col-12 col-md-8 col-lg-6">
<h1 class="display-4 fw-bold mb-3">移动优先设计</h1>
<p class="lead">从小屏幕开始设计,逐步增强到大屏幕体验</p>
</div>
</div>
<!-- 特性展示 -->
<div class="row">
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
📱
</div>
<h3 class="h4 mb-3">移动优先</h3>
<p>从最小的屏幕开始设计,确保核心功能在所有设备上都能正常工作。</p>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
🎨
</div>
<h3 class="h4 mb-3">渐进增强</h3>
<p>随着屏幕尺寸增大,逐步添加更多功能和视觉效果。</p>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
⚡
</div>
<h3 class="h4 mb-3">性能优化</h3>
<p>优先考虑移动设备的性能限制,确保快速加载和流畅体验。</p>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
🔧
</div>
<h3 class="h4 mb-3">灵活布局</h3>
<p>使用弹性网格系统,适应各种屏幕尺寸和设备方向。</p>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
👆
</div>
<h3 class="h4 mb-3">触摸友好</h3>
<p>设计适合触摸操作的界面元素,提供良好的移动交互体验。</p>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="feature-card text-center text-md-start">
<div class="feature-icon">
🌐
</div>
<h3 class="h4 mb-3">跨平台</h3>
<p>确保在不同操作系统和浏览器上都能提供一致的用户体验。</p>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
响应式网格系统
1. 基础网格布局
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式网格系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.grid-demo {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
padding: 1rem;
margin-bottom: 1rem;
text-align: center;
border-radius: 0.375rem;
}
.grid-demo-primary { background-color: #cfe2ff; border-color: #b6d7ff; }
.grid-demo-secondary { background-color: #e2e3e5; border-color: #d3d3d4; }
.grid-demo-success { background-color: #d1e7dd; border-color: #badbcc; }
.grid-demo-danger { background-color: #f8d7da; border-color: #f5c2c7; }
.grid-demo-warning { background-color: #fff3cd; border-color: #ffecb5; }
.grid-demo-info { background-color: #d1ecf1; border-color: #bee5eb; }
</style>
</head>
<body>
<div class="container my-5">
<h2 class="text-primary mb-4">响应式网格系统</h2>
<!-- 等宽列 -->
<div class="mb-5">
<h4>等宽列</h4>
<div class="row">
<div class="col">
<div class="grid-demo grid-demo-primary">col</div>
</div>
<div class="col">
<div class="grid-demo grid-demo-secondary">col</div>
</div>
<div class="col">
<div class="grid-demo grid-demo-success">col</div>
</div>
</div>
</div>
<!-- 指定宽度列 -->
<div class="mb-5">
<h4>指定宽度列</h4>
<div class="row">
<div class="col-4">
<div class="grid-demo grid-demo-primary">col-4</div>
</div>
<div class="col-8">
<div class="grid-demo grid-demo-secondary">col-8</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="grid-demo grid-demo-success">col-6</div>
</div>
<div class="col-6">
<div class="grid-demo grid-demo-danger">col-6</div>
</div>
</div>
</div>
<!-- 响应式列 -->
<div class="mb-5">
<h4>响应式列</h4>
<div class="row">
<div class="col-12 col-sm-6 col-md-4 col-lg-3">
<div class="grid-demo grid-demo-primary">
<small>XS: 12</small><br>
<small>SM: 6</small><br>
<small>MD: 4</small><br>
<small>LG: 3</small>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3">
<div class="grid-demo grid-demo-secondary">
<small>XS: 12</small><br>
<small>SM: 6</small><br>
<small>MD: 4</small><br>
<small>LG: 3</small>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3">
<div class="grid-demo grid-demo-success">
<small>XS: 12</small><br>
<small>SM: 6</small><br>
<small>MD: 4</small><br>
<small>LG: 3</small>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3">
<div class="grid-demo grid-demo-danger">
<small>XS: 12</small><br>
<small>SM: 6</small><br>
<small>MD: 4</small><br>
<small>LG: 3</small>
</div>
</div>
</div>
</div>
<!-- 列偏移 -->
<div class="mb-5">
<h4>列偏移</h4>
<div class="row">
<div class="col-4">
<div class="grid-demo grid-demo-primary">col-4</div>
</div>
<div class="col-4 offset-4">
<div class="grid-demo grid-demo-secondary">col-4 offset-4</div>
</div>
</div>
<div class="row">
<div class="col-3 offset-3">
<div class="grid-demo grid-demo-success">col-3 offset-3</div>
</div>
<div class="col-3 offset-3">
<div class="grid-demo grid-demo-danger">col-3 offset-3</div>
</div>
</div>
</div>
<!-- 响应式偏移 -->
<div class="mb-5">
<h4>响应式偏移</h4>
<div class="row">
<div class="col-12 col-sm-6 col-md-4 offset-md-4 col-lg-3 offset-lg-3">
<div class="grid-demo grid-demo-warning">
响应式偏移列
</div>
</div>
</div>
</div>
<!-- 嵌套网格 -->
<div class="mb-5">
<h4>嵌套网格</h4>
<div class="row">
<div class="col-12 col-md-8">
<div class="grid-demo grid-demo-primary">
主要内容区域
<div class="row mt-2">
<div class="col-6">
<div class="grid-demo grid-demo-info">嵌套 1</div>
</div>
<div class="col-6">
<div class="grid-demo grid-demo-info">嵌套 2</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="grid-demo grid-demo-secondary">
侧边栏
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</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>复杂网格布局</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.layout-section {
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.5rem;
margin-bottom: 1rem;
color: white;
font-weight: bold;
text-align: center;
}
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.sidebar { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.main-content { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
.footer { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
.widget { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
</style>
</head>
<body>
<div class="container-fluid my-4">
<h2 class="text-primary mb-4 text-center">复杂响应式布局示例</h2>
<!-- 经典三栏布局 -->
<div class="row mb-5">
<div class="col-12">
<h4>经典三栏布局</h4>
</div>
<!-- 头部 -->
<div class="col-12">
<div class="layout-section header">
网站头部 (Header)
</div>
</div>
<!-- 主要内容区域 -->
<div class="col-12 col-lg-3 order-lg-1">
<div class="layout-section sidebar">
左侧边栏<br>
<small>(在移动端显示在底部)</small>
</div>
</div>
<div class="col-12 col-lg-6 order-lg-2">
<div class="layout-section main-content">
主要内容区域<br>
<small>(响应式调整宽度)</small>
</div>
</div>
<div class="col-12 col-lg-3 order-lg-3">
<div class="layout-section sidebar">
右侧边栏<br>
<small>(在移动端显示在底部)</small>
</div>
</div>
<!-- 底部 -->
<div class="col-12 order-lg-4">
<div class="layout-section footer">
网站底部 (Footer)
</div>
</div>
</div>
<!-- 卡片网格布局 -->
<div class="row mb-5">
<div class="col-12">
<h4>卡片网格布局</h4>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 1
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 2
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 3
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 4
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 5
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="layout-section widget" style="min-height: 150px;">
卡片 6
</div>
</div>
</div>
<!-- 不等高列布局 -->
<div class="row mb-5">
<div class="col-12">
<h4>不等高列布局</h4>
</div>
<div class="col-12 col-md-8">
<div class="layout-section main-content" style="min-height: 300px;">
主要内容区域<br>
<small>较高的内容区域</small>
</div>
</div>
<div class="col-12 col-md-4">
<div class="row">
<div class="col-12">
<div class="layout-section sidebar" style="min-height: 140px;">
侧边栏 1
</div>
</div>
<div class="col-12">
<div class="layout-section widget" style="min-height: 140px;">
侧边栏 2
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
响应式工具类
1. 显示和隐藏工具类
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式工具类</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.utility-demo {
padding: 1rem;
margin: 0.5rem 0;
border-radius: 0.375rem;
text-align: center;
font-weight: bold;
border: 2px solid;
}
.demo-primary {
background-color: #cfe2ff;
border-color: #0d6efd;
color: #0d6efd;
}
.demo-success {
background-color: #d1e7dd;
border-color: #198754;
color: #198754;
}
.demo-warning {
background-color: #fff3cd;
border-color: #ffc107;
color: #664d03;
}
.demo-danger {
background-color: #f8d7da;
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container my-5">
<h2 class="text-primary mb-4">响应式工具类</h2>
<!-- 显示/隐藏工具类 -->
<div class="mb-5">
<h4>显示/隐藏工具类</h4>
<div class="utility-demo demo-primary d-block d-sm-none">
只在 XS 屏幕显示 (d-block d-sm-none)
</div>
<div class="utility-demo demo-success d-none d-sm-block d-md-none">
只在 SM 屏幕显示 (d-none d-sm-block d-md-none)
</div>
<div class="utility-demo demo-warning d-none d-md-block d-lg-none">
只在 MD 屏幕显示 (d-none d-md-block d-lg-none)
</div>
<div class="utility-demo demo-danger d-none d-lg-block">
只在 LG 及以上屏幕显示 (d-none d-lg-block)
</div>
</div>
<!-- 文本对齐工具类 -->
<div class="mb-5">
<h4>响应式文本对齐</h4>
<div class="utility-demo demo-primary text-center text-md-start">
移动端居中,桌面端左对齐 (text-center text-md-start)
</div>
<div class="utility-demo demo-success text-start text-sm-center text-lg-end">
XS左对齐,SM居中,LG右对齐 (text-start text-sm-center text-lg-end)
</div>
</div>
<!-- Flexbox 工具类 -->
<div class="mb-5">
<h4>响应式 Flexbox</h4>
<div class="d-flex flex-column flex-md-row">
<div class="utility-demo demo-primary flex-fill me-md-2">
Flex 项目 1<br>
<small>(移动端垂直排列,桌面端水平排列)</small>
</div>
<div class="utility-demo demo-success flex-fill ms-md-2">
Flex 项目 2<br>
<small>(移动端垂直排列,桌面端水平排列)</small>
</div>
</div>
<div class="d-flex justify-content-center justify-content-md-between align-items-center mt-3">
<div class="utility-demo demo-warning">
左侧内容
</div>
<div class="utility-demo demo-danger d-none d-md-block">
右侧内容 (桌面端显示)
</div>
</div>
</div>
<!-- 间距工具类 -->
<div class="mb-5">
<h4>响应式间距</h4>
<div class="utility-demo demo-primary p-2 p-md-4">
响应式内边距 (p-2 p-md-4)
</div>
<div class="utility-demo demo-success mt-2 mt-md-4 mb-2 mb-md-4">
响应式外边距 (mt-2 mt-md-4 mb-2 mb-md-4)
</div>
</div>
<!-- 尺寸工具类 -->
<div class="mb-5">
<h4>响应式尺寸</h4>
<div class="utility-demo demo-primary w-100 w-md-75 w-lg-50">
响应式宽度 (w-100 w-md-75 w-lg-50)
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
响应式图片和媒体
1. 响应式图片
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式图片和媒体</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.image-demo {
background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
border: 2px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-weight: bold;
min-height: 200px;
}
.media-container {
background-color: #f8f9fa;
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 2rem;
}
</style>
</head>
<body>
<div class="container my-5">
<h2 class="text-primary mb-4">响应式图片和媒体</h2>
<!-- 响应式图片 -->
<div class="media-container">
<h4>响应式图片</h4>
<div class="row">
<div class="col-12 col-md-6 mb-3">
<h6>img-fluid 类</h6>
<div class="image-demo img-fluid">
响应式图片<br>
(自动缩放)
</div>
<small class="text-muted">使用 .img-fluid 类使图片响应式</small>
</div>
<div class="col-12 col-md-6 mb-3">
<h6>固定尺寸图片</h6>
<div class="image-demo" style="width: 300px; height: 200px;">
固定尺寸图片<br>
(300x200px)
</div>
<small class="text-muted">固定尺寸可能在小屏幕上溢出</small>
</div>
</div>
</div>
<!-- 图片形状 -->
<div class="media-container">
<h4>图片形状</h4>
<div class="row text-center">
<div class="col-12 col-sm-4 mb-3">
<div class="image-demo rounded" style="width: 150px; height: 150px; margin: 0 auto;">
圆角图片<br>
.rounded
</div>
</div>
<div class="col-12 col-sm-4 mb-3">
<div class="image-demo rounded-circle" style="width: 150px; height: 150px; margin: 0 auto;">
圆形图片<br>
.rounded-circle
</div>
</div>
<div class="col-12 col-sm-4 mb-3">
<div class="image-demo img-thumbnail" style="width: 150px; height: 150px; margin: 0 auto;">
缩略图<br>
.img-thumbnail
</div>
</div>
</div>
</div>
<!-- 响应式嵌入 -->
<div class="media-container">
<h4>响应式嵌入</h4>
<div class="row">
<div class="col-12 col-lg-6 mb-3">
<h6>16:9 比例视频</h6>
<div class="ratio ratio-16x9">
<div class="bg-secondary d-flex align-items-center justify-content-center text-white">
<div class="text-center">
<i class="bi bi-play-circle" style="font-size: 3rem;"></i><br>
16:9 视频播放器
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 mb-3">
<h6>4:3 比例视频</h6>
<div class="ratio ratio-4x3">
<div class="bg-info d-flex align-items-center justify-content-center text-white">
<div class="text-center">
<i class="bi bi-play-circle" style="font-size: 3rem;"></i><br>
4:3 视频播放器
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 mb-3">
<h6>1:1 比例内容</h6>
<div class="ratio ratio-1x1">
<div class="bg-warning d-flex align-items-center justify-content-center text-dark">
<div class="text-center">
<i class="bi bi-square" style="font-size: 3rem;"></i><br>
1:1 正方形内容
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 mb-3">
<h6>21:9 比例内容</h6>
<div class="ratio ratio-21x9">
<div class="bg-danger d-flex align-items-center justify-content-center text-white">
<div class="text-center">
<i class="bi bi-aspect-ratio" style="font-size: 3rem;"></i><br>
21:9 宽屏内容
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 图片网格 -->
<div class="media-container">
<h4>响应式图片网格</h4>
<div class="row g-2">
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 1
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 2
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 3
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 4
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 5
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 6
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 7
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="image-demo" style="height: 150px;">
图片 8
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
响应式导航设计
1. 移动优先导航
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式导航设计</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
.navbar-custom {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.navbar-custom .navbar-brand {
font-weight: bold;
font-size: 1.5rem;
}
.navbar-custom .nav-link {
font-weight: 500;
transition: all 0.3s ease;
}
.navbar-custom .nav-link:hover {
transform: translateY(-2px);
}
.content-section {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.hero-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.feature-section {
background-color: #f8f9fa;
}
.contact-section {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
</style>
</head>
<body>
<!-- 响应式导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark navbar-custom fixed-top">
<div class="container">
<!-- 品牌标识 -->
<a class="navbar-brand" href="#">
<i class="bi bi-bootstrap"></i>
响应式网站
</a>
<!-- 移动端切换按钮 -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- 导航菜单 -->
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" href="#home">
<i class="bi bi-house"></i>
<span class="d-lg-none">首页</span>
<span class="d-none d-lg-inline">首页</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#features">
<i class="bi bi-star"></i>
<span class="d-lg-none">特性</span>
<span class="d-none d-lg-inline">特性</span>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-grid"></i>
服务
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Web 开发</a></li>
<li><a class="dropdown-item" href="#">移动应用</a></li>
<li><a class="dropdown-item" href="#">UI/UX 设计</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">咨询服务</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="#contact">
<i class="bi bi-envelope"></i>
<span class="d-lg-none">联系我们</span>
<span class="d-none d-lg-inline">联系</span>
</a>
</li>
</ul>
<!-- 搜索框 (桌面端显示) -->
<form class="d-none d-lg-flex ms-3" role="search">
<input class="form-control me-2" type="search" placeholder="搜索..." aria-label="Search">
<button class="btn btn-outline-light" type="submit">
<i class="bi bi-search"></i>
</button>
</form>
</div>
</div>
</nav>
<!-- 主要内容区域 -->
<main style="padding-top: 76px;">
<!-- 首页部分 -->
<section id="home" class="content-section hero-section">
<div class="container">
<div class="row justify-content-center text-center">
<div class="col-12 col-md-8 col-lg-6">
<h1 class="display-4 fw-bold mb-4">响应式设计</h1>
<p class="lead mb-4">使用Bootstrap创建适应所有设备的现代化网站</p>
<div class="d-flex flex-column flex-sm-row justify-content-center gap-3">
<a href="#features" class="btn btn-light btn-lg">
<i class="bi bi-arrow-down"></i>
了解更多
</a>
<a href="#contact" class="btn btn-outline-light btn-lg">
<i class="bi bi-envelope"></i>
联系我们
</a>
</div>
</div>
</div>
</div>
</section>
<!-- 特性部分 -->
<section id="features" class="content-section feature-section">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-8 text-center">
<h2 class="display-5 fw-bold mb-5">核心特性</h2>
<div class="row">
<div class="col-12 col-md-4 mb-4">
<div class="text-primary mb-3">
<i class="bi bi-phone" style="font-size: 3rem;"></i>
</div>
<h4>移动优先</h4>
<p>从移动设备开始设计,确保在所有屏幕尺寸上都有完美体验。</p>
</div>
<div class="col-12 col-md-4 mb-4">
<div class="text-success mb-3">
<i class="bi bi-grid-3x3" style="font-size: 3rem;"></i>
</div>
<h4>灵活网格</h4>
<p>使用强大的网格系统创建复杂的响应式布局。</p>
</div>
<div class="col-12 col-md-4 mb-4">
<div class="text-info mb-3">
<i class="bi bi-speedometer2" style="font-size: 3rem;"></i>
</div>
<h4>高性能</h4>
<p>优化的CSS和JavaScript确保快速加载和流畅交互。</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 联系部分 -->
<section id="contact" class="content-section contact-section">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6 text-center">
<h2 class="display-5 fw-bold mb-4">联系我们</h2>
<p class="lead mb-4">准备开始您的响应式网站项目了吗?</p>
<div class="row text-start">
<div class="col-12 col-sm-6 mb-3">
<div class="d-flex align-items-center">
<i class="bi bi-envelope-fill me-3" style="font-size: 1.5rem;"></i>
<div>
<strong>邮箱</strong><br>
contact@example.com
</div>
</div>
</div>
<div class="col-12 col-sm-6 mb-3">
<div class="d-flex align-items-center">
<i class="bi bi-telephone-fill me-3" style="font-size: 1.5rem;"></i>
<div>
<strong>电话</strong><br>
+86 138 0013 8000
</div>
</div>
</div>
</div>
<div class="mt-4">
<a href="mailto:contact@example.com" class="btn btn-light btn-lg me-3">
<i class="bi bi-envelope"></i>
发送邮件
</a>
<a href="tel:+8613800138000" class="btn btn-outline-light btn-lg">
<i class="bi bi-telephone"></i>
拨打电话
</a>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- 底部导航 (移动端) -->
<nav class="navbar navbar-dark bg-dark fixed-bottom d-lg-none">
<div class="container-fluid">
<div class="d-flex justify-content-around w-100">
<a href="#home" class="text-light text-decoration-none text-center">
<i class="bi bi-house d-block"></i>
<small>首页</small>
</a>
<a href="#features" class="text-light text-decoration-none text-center">
<i class="bi bi-star d-block"></i>
<small>特性</small>
</a>
<a href="#contact" class="text-light text-decoration-none text-center">
<i class="bi bi-envelope d-block"></i>
<small>联系</small>
</a>
<a href="#" class="text-light text-decoration-none text-center">
<i class="bi bi-person d-block"></i>
<small>我的</small>
</a>
</div>
</div>
</nav>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// 导航栏活动状态
window.addEventListener('scroll', function() {
const sections = document.querySelectorAll('section');
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop - 100;
if (pageYOffset >= sectionTop) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + current) {
link.classList.add('active');
}
});
});
</script>
</body>
</html>
实际应用案例
1. 响应式电商网站
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式电商网站</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
border-radius: 1rem;
overflow: hidden;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
.product-image {
height: 200px;
background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 3rem;
}
.price {
font-size: 1.25rem;
font-weight: bold;
color: #e74c3c;
}
.original-price {
text-decoration: line-through;
color: #95a5a6;
font-size: 0.9rem;
}
.hero-banner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 4rem 0;
}
.category-card {
background: white;
border-radius: 1rem;
padding: 2rem;
text-align: center;
transition: all 0.3s ease;
border: 1px solid #e9ecef;
}
.category-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.category-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1rem;
color: white;
font-size: 2rem;
}
@media (max-width: 767.98px) {
.hero-banner {
padding: 2rem 0;
}
.hero-banner h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm sticky-top">
<div class="container">
<a class="navbar-brand fw-bold text-primary" href="#">
<i class="bi bi-shop"></i>
电商商城
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="#">首页</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
分类
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">电子产品</a></li>
<li><a class="dropdown-item" href="#">服装配饰</a></li>
<li><a class="dropdown-item" href="#">家居用品</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="#">特价商品</a>
</li>
</ul>
<div class="d-flex align-items-center">
<!-- 搜索框 -->
<form class="d-none d-md-flex me-3">
<div class="input-group">
<input type="search" class="form-control" placeholder="搜索商品...">
<button class="btn btn-outline-primary" type="submit">
<i class="bi bi-search"></i>
</button>
</div>
</form>
<!-- 购物车 -->
<a href="#" class="btn btn-outline-primary me-2 position-relative">
<i class="bi bi-cart"></i>
<span class="d-none d-lg-inline ms-1">购物车</span>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
3
</span>
</a>
<!-- 用户菜单 -->
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown">
<i class="bi bi-person"></i>
<span class="d-none d-lg-inline ms-1">账户</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#">我的订单</a></li>
<li><a class="dropdown-item" href="#">个人资料</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">退出登录</a></li>
</ul>
</div>
</div>
</div>
</div>
</nav>
<!-- 英雄横幅 -->
<section class="hero-banner">
<div class="container">
<div class="row align-items-center">
<div class="col-12 col-lg-6">
<h1 class="display-4 fw-bold mb-3">发现精选商品</h1>
<p class="lead mb-4">在我们的响应式电商平台上找到您需要的一切</p>
<div class="d-flex flex-column flex-sm-row gap-3">
<a href="#products" class="btn btn-light btn-lg">
<i class="bi bi-arrow-down"></i>
浏览商品
</a>
<a href="#" class="btn btn-outline-light btn-lg">
<i class="bi bi-percent"></i>
查看优惠
</a>
</div>
</div>
<div class="col-12 col-lg-6 text-center">
<div class="d-none d-lg-block">
<i class="bi bi-bag-heart" style="font-size: 10rem; opacity: 0.3;"></i>
</div>
</div>
</div>
</div>
</section>
<!-- 商品分类 -->
<section class="py-5 bg-light">
<div class="container">
<h2 class="text-center mb-5">热门分类</h2>
<div class="row g-4">
<div class="col-6 col-md-4 col-lg-3">
<div class="category-card">
<div class="category-icon">
<i class="bi bi-laptop"></i>
</div>
<h5>电子产品</h5>
<p class="text-muted small">最新科技产品</p>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="category-card">
<div class="category-icon">
<i class="bi bi-bag"></i>
</div>
<h5>服装配饰</h5>
<p class="text-muted small">时尚潮流单品</p>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="category-card">
<div class="category-icon">
<i class="bi bi-house"></i>
</div>
<h5>家居用品</h5>
<p class="text-muted small">舒适生活必需品</p>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="category-card">
<div class="category-icon">
<i class="bi bi-heart"></i>
</div>
<h5>美妆护肤</h5>
<p class="text-muted small">美丽从这里开始</p>
</div>
</div>
</div>
</div>
</section>
<!-- 商品展示 -->
<section id="products" class="py-5">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>热销商品</h2>
<div class="d-none d-md-flex">
<button class="btn btn-outline-secondary me-2 active">全部</button>
<button class="btn btn-outline-secondary me-2">电子产品</button>
<button class="btn btn-outline-secondary me-2">服装</button>
<button class="btn btn-outline-secondary">家居</button>
</div>
</div>
<div class="row g-4">
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-laptop"></i>
</div>
<div class="card-body">
<h6 class="card-title">笔记本电脑</h6>
<p class="card-text small text-muted">高性能办公笔记本</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥4,999</span>
<span class="original-price ms-1">¥5,999</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-phone"></i>
</div>
<div class="card-body">
<h6 class="card-title">智能手机</h6>
<p class="card-text small text-muted">最新款智能手机</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥2,999</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-headphones"></i>
</div>
<div class="card-body">
<h6 class="card-title">无线耳机</h6>
<p class="card-text small text-muted">降噪无线蓝牙耳机</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥599</span>
<span class="original-price ms-1">¥799</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-watch"></i>
</div>
<div class="card-body">
<h6 class="card-title">智能手表</h6>
<p class="card-text small text-muted">健康监测智能手表</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥1,299</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-camera"></i>
</div>
<div class="card-body">
<h6 class="card-title">数码相机</h6>
<p class="card-text small text-muted">专业摄影相机</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥3,599</span>
<span class="original-price ms-1">¥4,299</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-mouse"></i>
</div>
<div class="card-body">
<h6 class="card-title">无线鼠标</h6>
<p class="card-text small text-muted">人体工学无线鼠标</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥199</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-keyboard"></i>
</div>
<div class="card-body">
<h6 class="card-title">机械键盘</h6>
<p class="card-text small text-muted">RGB背光机械键盘</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥499</span>
<span class="original-price ms-1">¥699</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-lg-3">
<div class="card product-card h-100">
<div class="product-image">
<i class="bi bi-display"></i>
</div>
<div class="card-body">
<h6 class="card-title">显示器</h6>
<p class="card-text small text-muted">4K高清显示器</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">¥1,899</span>
</div>
<button class="btn btn-primary btn-sm">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="text-center mt-5">
<button class="btn btn-outline-primary btn-lg">
<i class="bi bi-arrow-clockwise"></i>
加载更多商品
</button>
</div>
</div>
</section>
<!-- 底部导航 (移动端) -->
<nav class="navbar navbar-light bg-white border-top fixed-bottom d-lg-none">
<div class="container-fluid">
<div class="d-flex justify-content-around w-100">
<a href="#" class="text-decoration-none text-center">
<i class="bi bi-house d-block text-primary"></i>
<small>首页</small>
</a>
<a href="#" class="text-decoration-none text-center">
<i class="bi bi-grid d-block text-muted"></i>
<small>分类</small>
</a>
<a href="#" class="text-decoration-none text-center position-relative">
<i class="bi bi-cart d-block text-muted"></i>
<small>购物车</small>
<span class="position-absolute top-0 start-50 translate-middle badge rounded-pill bg-danger" style="font-size: 0.6rem;">
3
</span>
</a>
<a href="#" class="text-decoration-none text-center">
<i class="bi bi-person d-block text-muted"></i>
<small>我的</small>
</a>
</div>
</div>
</nav>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 商品筛选功能
document.querySelectorAll('.btn-outline-secondary').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.btn-outline-secondary').forEach(b => b.classList.remove('active'));
this.classList.add('active');
});
});
// 添加到购物车动画
document.querySelectorAll('.btn-primary').forEach(btn => {
btn.addEventListener('click', function() {
const originalText = this.innerHTML;
this.innerHTML = '<i class="bi bi-check"></i>';
this.classList.add('btn-success');
this.classList.remove('btn-primary');
setTimeout(() => {
this.innerHTML = originalText;
this.classList.add('btn-primary');
this.classList.remove('btn-success');
}, 1000);
});
});
</script>
</body>
</html>
自定义响应式样式
1. 自定义断点
// 自定义断点变量
$custom-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px,
xxxl: 1600px // 添加超大断点
);
// 自定义媒体查询混合器
@mixin respond-to($breakpoint) {
@if map-has-key($custom-breakpoints, $breakpoint) {
@media (min-width: map-get($custom-breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Breakpoint #{$breakpoint} not found in $custom-breakpoints.";
}
}
// 使用示例
.custom-responsive-element {
padding: 1rem;
@include respond-to(sm) {
padding: 1.5rem;
}
@include respond-to(md) {
padding: 2rem;
}
@include respond-to(lg) {
padding: 2.5rem;
}
@include respond-to(xl) {
padding: 3rem;
}
@include respond-to(xxxl) {
padding: 4rem;
}
}
2. 响应式字体系统
/* 响应式字体系统 */
:root {
/* 基础字体大小 */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-size-5xl: 3rem; /* 48px */
/* 行高 */
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;
}
/* 响应式标题 */
.responsive-heading-1 {
font-size: var(--font-size-2xl);
line-height: var(--line-height-tight);
font-weight: 700;
}
@media (min-width: 576px) {
.responsive-heading-1 {
font-size: var(--font-size-3xl);
}
}
@media (min-width: 768px) {
.responsive-heading-1 {
font-size: var(--font-size-4xl);
}
}
@media (min-width: 992px) {
.responsive-heading-1 {
font-size: var(--font-size-5xl);
}
}
/* 响应式正文 */
.responsive-body {
font-size: var(--font-size-sm);
line-height: var(--line-height-normal);
}
@media (min-width: 768px) {
.responsive-body {
font-size: var(--font-size-base);
}
}
@media (min-width: 992px) {
.responsive-body {
font-size: var(--font-size-lg);
line-height: var(--line-height-relaxed);
}
}
3. 响应式间距系统
/* 响应式间距系统 */
:root {
--spacing-1: 0.25rem; /* 4px */
--spacing-2: 0.5rem; /* 8px */
--spacing-3: 0.75rem; /* 12px */
--spacing-4: 1rem; /* 16px */
--spacing-5: 1.25rem; /* 20px */
--spacing-6: 1.5rem; /* 24px */
--spacing-8: 2rem; /* 32px */
--spacing-10: 2.5rem; /* 40px */
--spacing-12: 3rem; /* 48px */
--spacing-16: 4rem; /* 64px */
--spacing-20: 5rem; /* 80px */
}
/* 响应式容器间距 */
.responsive-container {
padding: var(--spacing-4);
}
@media (min-width: 576px) {
.responsive-container {
padding: var(--spacing-6);
}
}
@media (min-width: 768px) {
.responsive-container {
padding: var(--spacing-8);
}
}
@media (min-width: 992px) {
.responsive-container {
padding: var(--spacing-12);
}
}
/* 响应式卡片间距 */
.responsive-card {
padding: var(--spacing-4);
margin-bottom: var(--spacing-4);
border-radius: 0.5rem;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
@media (min-width: 768px) {
.responsive-card {
padding: var(--spacing-6);
margin-bottom: var(--spacing-6);
}
}
@media (min-width: 992px) {
.responsive-card {
padding: var(--spacing-8);
margin-bottom: var(--spacing-8);
}
}
响应式组件优化
1. 响应式表格
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式表格</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.responsive-table-card {
background: white;
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
overflow: hidden;
}
.table-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1.5rem;
}
@media (max-width: 767.98px) {
.mobile-table {
display: none;
}
.mobile-cards {
display: block;
}
.mobile-card {
background: #f8f9fa;
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 1rem;
border-left: 4px solid #007bff;
}
}
@media (min-width: 768px) {
.mobile-table {
display: table;
}
.mobile-cards {
display: none;
}
}
</style>
</head>
<body>
<div class="container my-5">
<div class="responsive-table-card">
<div class="table-header">
<h3 class="mb-0">用户数据表</h3>
<p class="mb-0 opacity-75">响应式表格展示</p>
</div>
<!-- 桌面端表格 -->
<div class="table-responsive">
<table class="table table-hover mobile-table mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>电话</th>
<th>状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>zhangsan@example.com</td>
<td>138-0013-8000</td>
<td><span class="badge bg-success">活跃</span></td>
<td>2024-01-15</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>lisi@example.com</td>
<td>138-0013-8001</td>
<td><span class="badge bg-warning">待激活</span></td>
<td>2024-01-16</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>wangwu@example.com</td>
<td>138-0013-8002</td>
<td><span class="badge bg-danger">已禁用</span></td>
<td>2024-01-17</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 移动端卡片 -->
<div class="mobile-cards p-3">
<div class="mobile-card">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="mb-0">张三 (#001)</h6>
<span class="badge bg-success">活跃</span>
</div>
<p class="mb-1"><strong>邮箱:</strong> zhangsan@example.com</p>
<p class="mb-1"><strong>电话:</strong> 138-0013-8000</p>
<p class="mb-2"><strong>注册时间:</strong> 2024-01-15</p>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-pencil"></i> 编辑
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i> 删除
</button>
</div>
</div>
<div class="mobile-card">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="mb-0">李四 (#002)</h6>
<span class="badge bg-warning">待激活</span>
</div>
<p class="mb-1"><strong>邮箱:</strong> lisi@example.com</p>
<p class="mb-1"><strong>电话:</strong> 138-0013-8001</p>
<p class="mb-2"><strong>注册时间:</strong> 2024-01-16</p>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-pencil"></i> 编辑
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i> 删除
</button>
</div>
</div>
<div class="mobile-card">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="mb-0">王五 (#003)</h6>
<span class="badge bg-danger">已禁用</span>
</div>
<p class="mb-1"><strong>邮箱:</strong> wangwu@example.com</p>
<p class="mb-1"><strong>电话:</strong> 138-0013-8002</p>
<p class="mb-2"><strong>注册时间:</strong> 2024-01-17</p>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-pencil"></i> 编辑
</button>
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-trash"></i> 删除
</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>