Skip to content

响应式设计

响应式设计是一种让网页能够自适应不同设备和屏幕尺寸的设计方法。通过响应式设计,同一个网页可以在手机、平板、笔记本和桌面显示器上都有良好的显示效果。

响应式设计基础

什么是响应式设计

响应式设计(Responsive Web Design,简称 RWD)的核心思想是:网页的内容和布局应该根据浏览设备的特性(如屏幕宽度、分辨率等)自动调整。

响应式设计的三大核心技术:

  1. 流式布局:使用百分比而非固定像素
  2. 弹性图片:图片能够随容器缩放
  3. 媒体查询:根据设备特性应用不同样式

视口设置

视口(Viewport)是用户在网页上的可见区域。在移动设备上,视口宽度通常与屏幕宽度不同。

html
<!-- 在 HTML 的 <head> 中添加视口元标签 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!--
    视口属性说明:
    - width=device-width:视口宽度等于设备宽度
    - initial-scale=1.0:初始缩放比例为 1
    - maximum-scale=1.0:最大缩放比例为 1(可选)
    - user-scalable=no:禁止用户缩放(不推荐,影响可访问性)
-->

媒体查询

媒体查询是响应式设计的核心技术,它允许根据设备特性应用不同的 CSS 样式。

基本语法

css
/* 媒体查询的基本结构 */
@media 媒体类型 and (媒体特性) {
    /* 当条件满足时应用的样式 */
    选择器 {
        属性: 值;
    }
}

/* 常用媒体类型 */
/* all - 所有设备(默认) */
/* screen - 屏幕设备 */
/* print - 打印设备 */
/* speech - 屏幕阅读器 */

/* 示例:当屏幕宽度小于等于 768px 时应用样式 */
@media screen and (max-width: 768px) {
    .container {
        width: 100%;  /* 容器宽度占满 */
        padding: 10px;  /* 减小内边距 */
    }
}

/* 示例:当屏幕宽度大于等于 1200px 时应用样式 */
@media screen and (min-width: 1200px) {
    .container {
        max-width: 1140px;  /* 限制最大宽度 */
        margin: 0 auto;  /* 居中显示 */
    }
}

常用媒体特性

css
/* 宽度相关 */
@media (width: 600px) {
    /* 视口宽度恰好为 600px */
}

@media (min-width: 768px) {
    /* 视口宽度大于等于 768px */
}

@media (max-width: 576px) {
    /* 视口宽度小于等于 576px */
}

/* 高度相关 */
@media (min-height: 500px) {
    /* 视口高度大于等于 500px */
}

/* 方向:横屏或竖屏 */
@media (orientation: landscape) {
    /* 横屏模式 */
    .gallery {
        flex-direction: row;  /* 横向排列 */
    }
}

@media (orientation: portrait) {
    /* 竖屏模式 */
    .gallery {
        flex-direction: column;  /* 纵向排列 */
    }
}

/* 分辨率相关 */
@media (min-resolution: 2dppx) {
    /* 高分辨率屏幕(如 Retina) */
    .logo {
        background-image: url('logo@2x.png');  /* 使用高清图片 */
    }
}

/* 组合多个条件 */
@media screen and (min-width: 768px) and (max-width: 1024px) {
    /* 宽度在 768px 到 1024px 之间 */
    .sidebar {
        width: 250px;  /* 侧边栏中等宽度 */
    }
}

/* 使用 or 逻辑 */
@media (max-width: 576px), (orientation: portrait) {
    /* 宽度小于 576px 或竖屏模式 */
    .nav {
        flex-direction: column;  /* 导航纵向排列 */
    }
}

/* 使用 not 逻辑 */
@media not screen {
    /* 非屏幕设备(如打印机) */
    .no-print {
        display: none;  /* 打印时隐藏 */
    }
}

断点设置

断点是响应式设计中屏幕宽度的临界值,用于触发不同的布局。

css
/* 常用断点(参考 Bootstrap) */

/* 超小屏幕(手机) */
@media (max-width: 575.98px) {
    .col-xs-12 {
        flex: 0 0 100%;  /* 占满一行 */
        max-width: 100%;
    }
}

/* 小屏幕(平板竖屏) */
@media (min-width: 576px) and (max-width: 767.98px) {
    .col-sm-6 {
        flex: 0 0 50%;  /* 占半行 */
        max-width: 50%;
    }
}

/* 中等屏幕(平板横屏) */
@media (min-width: 768px) and (max-width: 991.98px) {
    .col-md-4 {
        flex: 0 0 33.333%;  /* 占三分之一 */
        max-width: 33.333%;
    }
}

/* 大屏幕(桌面显示器) */
@media (min-width: 992px) and (max-width: 1199.98px) {
    .col-lg-3 {
        flex: 0 0 25%;  /* 占四分之一 */
        max-width: 25%;
    }
}

/* 超大屏幕 */
@media (min-width: 1200px) {
    .col-xl-2 {
        flex: 0 0 16.666%;  /* 占六分之一 */
        max-width: 16.666%;
    }
}

移动优先 vs 桌面优先

css
/* 移动优先:先写小屏幕样式,再用 min-width 扩展 */
/* 推荐方式,更符合渐进增强原则 */

/* 基础样式(移动端) */
.container {
    width: 100%;
    padding: 15px;
}

/* 平板及以上 */
@media (min-width: 768px) {
    .container {
        max-width: 720px;
        margin: 0 auto;
    }
}

/* 桌面及以上 */
@media (min-width: 992px) {
    .container {
        max-width: 960px;
    }
}

/* 大屏桌面 */
@media (min-width: 1200px) {
    .container {
        max-width: 1140px;
    }
}

/* 桌面优先:先写大屏幕样式,再用 max-width 缩减 */
/* 基础样式(桌面端) */
.container {
    max-width: 1140px;
    margin: 0 auto;
}

/* 平板及以下 */
@media (max-width: 1199.98px) {
    .container {
        max-width: 960px;
    }
}

/* 手机及以下 */
@media (max-width: 767.98px) {
    .container {
        width: 100%;
        padding: 15px;
    }
}

弹性布局

流式布局

使用百分比和相对单位代替固定像素值。

css
/* 流式容器 */
.container {
    width: 90%;  /* 占视口宽度的 90% */
    max-width: 1200px;  /* 最大不超过 1200px */
    margin: 0 auto;  /* 居中 */
}

/* 流式网格 */
.row {
    display: flex;
    flex-wrap: wrap;  /* 允许换行 */
    margin: 0 -15px;  /* 负边距抵消列的内边距 */
}

.column {
    flex: 1;  /* 等分剩余空间 */
    padding: 0 15px;
    min-width: 200px;  /* 最小宽度防止过窄 */
}

/* 使用 calc() 计算宽度 */
.sidebar {
    width: calc(25% - 20px);  /* 25% 减去间距 */
}

.main-content {
    width: calc(75% - 20px);
}

弹性图片

让图片能够自适应容器大小。

css
/* 基本弹性图片 */
img {
    max-width: 100%;  /* 最大不超过容器宽度 */
    height: auto;  /* 高度自动调整,保持比例 */
}

/* 背景图片自适应 */
.hero {
    width: 100%;
    height: 400px;
    background-image: url('hero-bg.jpg');
    background-size: cover;  /* 覆盖整个容器 */
    background-position: center;  /* 居中显示 */
    background-repeat: no-repeat;
}

/* 响应式图片容器 */
.image-container {
    position: relative;
    width: 100%;
    padding-bottom: 56.25%;  /* 16:9 比例 */
    overflow: hidden;
}

.image-container img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;  /* 保持比例填充 */
}

/* 不同比例的容器 */
.square {
    padding-bottom: 100%;  /* 1:1 正方形 */
}

.portrait {
    padding-bottom: 150%;  /* 2:3 竖向 */
}

.widescreen {
    padding-bottom: 56.25%;  /* 16:9 宽屏 */
}

使用 picture 元素

HTML5 的 <picture> 元素可以根据不同条件加载不同图片。

html
<!-- 根据屏幕宽度加载不同图片 -->
<picture>
    <!-- 大屏幕加载高清图 -->
    <source media="(min-width: 1200px)" srcset="large.jpg">
    <!-- 中等屏幕加载中等图片 -->
    <source media="(min-width: 768px)" srcset="medium.jpg">
    <!-- 小屏幕加载小图 -->
    <img src="small.jpg" alt="响应式图片">
</picture>

<!-- 根据像素密度加载不同图片 -->
<picture>
    <!-- 2x 屏幕加载高清图 -->
    <source srcset="image@2x.jpg 2x">
    <!-- 默认加载普通图 -->
    <img src="image.jpg" alt="高清图片">
</picture>

<!-- 使用 WebP 格式(更小的文件体积) -->
<picture>
    <!-- 支持 WebP 的浏览器加载 WebP -->
    <source type="image/webp" srcset="image.webp">
    <!-- 不支持则加载 JPG -->
    <img src="image.jpg" alt="现代图片格式">
</picture>

响应式排版

相对单位

css
/* 使用相对单位设置字体大小 */

/* rem:相对于根元素(html)的字体大小 */
html {
    font-size: 16px;  /* 基准字体大小 */
}

body {
    font-size: 1rem;  /* 16px */
}

h1 {
    font-size: 2.5rem;  /* 40px */
}

h2 {
    font-size: 2rem;  /* 32px */
}

/* em:相对于父元素的字体大小 */
.parent {
    font-size: 18px;
}

.parent .child {
    font-size: 1.2em;  /* 21.6px = 18px * 1.2 */
    padding: 1em;  /* 21.6px */
}

/* vw:视口宽度的百分比 */
.hero-title {
    font-size: 5vw;  /* 视口宽度的 5% */
}

/* 响应式字体大小 */
.text {
    /* clamp(最小值, 首选值, 最大值) */
    font-size: clamp(1rem, 2.5vw, 2rem);
    /* 最小 1rem,首选 2.5vw,最大 2rem */
}

/* 使用 calc() 混合单位 */
.fluid-text {
    font-size: calc(1rem + 0.5vw);
    /* 基础 1rem 加上视口宽度的 0.5% */
}

响应式行高和间距

css
/* 响应式行高 */
p {
    font-size: 1rem;
    line-height: 1.5;  /* 无单位值,相对于字体大小 */
}

/* 响应式间距 */
.section {
    padding: 5%;  /* 使用百分比 */
    margin: 2rem 0;  /* 使用 rem */
}

/* 使用 clamp() 设置响应式间距 */
.article {
    padding: clamp(1rem, 5vw, 3rem);
    /* 内边距在 1rem 到 3rem 之间,根据视口宽度变化 */
}

/* 响应式网格间距 */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: clamp(1rem, 3vw, 2rem);
}

响应式导航

移动端汉堡菜单

html
<!-- HTML 结构 -->
<header class="header">
    <div class="logo">Logo</div>
    
    <!-- 汉堡按钮 -->
    <button class="menu-toggle" aria-label="打开菜单">
        <span class="hamburger"></span>
    </button>
    
    <!-- 导航菜单 -->
    <nav class="nav">
        <ul class="nav-list">
            <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>
    </nav>
</header>
css
/* 基础样式 */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 2rem;
    background-color: #333;
    color: white;
}

.logo {
    font-size: 1.5rem;
    font-weight: bold;
}

/* 导航列表 */
.nav-list {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 2rem;
}

.nav-list a {
    color: white;
    text-decoration: none;
    transition: color 0.3s;
}

.nav-list a:hover {
    color: #4CAF50;
}

/* 汉堡按钮默认隐藏 */
.menu-toggle {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    padding: 10px;
}

/* 汉堡图标 */
.hamburger {
    display: block;
    width: 25px;
    height: 3px;
    background-color: white;
    position: relative;
}

.hamburger::before,
.hamburger::after {
    content: '';
    position: absolute;
    width: 25px;
    height: 3px;
    background-color: white;
    left: 0;
}

.hamburger::before {
    top: -8px;
}

.hamburger::after {
    top: 8px;
}

/* 移动端样式 */
@media (max-width: 768px) {
    /* 显示汉堡按钮 */
    .menu-toggle {
        display: block;
        z-index: 1001;
    }
    
    /* 导航菜单变为垂直布局 */
    .nav {
        position: fixed;
        top: 0;
        right: -100%;  /* 默认隐藏在屏幕右侧 */
        width: 70%;
        max-width: 300px;
        height: 100vh;
        background-color: #222;
        padding-top: 80px;
        transition: right 0.3s ease;
        z-index: 1000;
    }
    
    /* 打开菜单时的状态 */
    .nav.active {
        right: 0;
    }
    
    .nav-list {
        flex-direction: column;
        padding: 0 2rem;
        gap: 0;
    }
    
    .nav-list li {
        border-bottom: 1px solid #444;
    }
    
    .nav-list a {
        display: block;
        padding: 1rem 0;
    }
    
    /* 汉堡按钮动画 */
    .menu-toggle.active .hamburger {
        background-color: transparent;
    }
    
    .menu-toggle.active .hamburger::before {
        top: 0;
        transform: rotate(45deg);
    }
    
    .menu-toggle.active .hamburger::after {
        top: 0;
        transform: rotate(-45deg);
    }
}
javascript
// JavaScript 控制菜单开关
const menuToggle = document.querySelector('.menu-toggle');
const nav = document.querySelector('.nav');

menuToggle.addEventListener('click', () => {
    menuToggle.classList.toggle('active');
    nav.classList.toggle('active');
});

// 点击导航链接后关闭菜单
document.querySelectorAll('.nav-list a').forEach(link => {
    link.addEventListener('click', () => {
        menuToggle.classList.remove('active');
        nav.classList.remove('active');
    });
});

响应式网格系统

简单的响应式网格

css
/* 基础网格容器 */
.grid {
    display: grid;
    gap: 20px;
    padding: 20px;
}

/* 自动填充列 */
.grid-auto {
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    /* 自动填充,每列最小 250px,最大等分 */
}

/* 自动适应列 */
.grid-fit {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    /* 自动适应,空列会折叠 */
}

/* 响应式网格项 */
.grid-item {
    background-color: #f5f5f5;
    padding: 20px;
    border-radius: 8px;
}

/* 不同断点的列数 */
.grid-responsive {
    grid-template-columns: 1fr;  /* 默认 1 列 */
}

@media (min-width: 576px) {
    .grid-responsive {
        grid-template-columns: repeat(2, 1fr);  /* 2 列 */
    }
}

@media (min-width: 992px) {
    .grid-responsive {
        grid-template-columns: repeat(3, 1fr);  /* 3 列 */
    }
}

@media (min-width: 1200px) {
    .grid-responsive {
        grid-template-columns: repeat(4, 1fr);  /* 4 列 */
    }
}

12 列网格系统

css
/* 12 列网格系统 */
.row {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -15px;
}

[class*="col-"] {
    padding: 0 15px;
    box-sizing: border-box;
}

/* 基础列宽 */
.col-1 { flex: 0 0 8.333%; max-width: 8.333%; }   /* 1/12 */
.col-2 { flex: 0 0 16.666%; max-width: 16.666%; } /* 2/12 */
.col-3 { flex: 0 0 25%; max-width: 25%; }         /* 3/12 */
.col-4 { flex: 0 0 33.333%; max-width: 33.333%; } /* 4/12 */
.col-5 { flex: 0 0 41.666%; max-width: 41.666%; } /* 5/12 */
.col-6 { flex: 0 0 50%; max-width: 50%; }         /* 6/12 */
.col-7 { flex: 0 0 58.333%; max-width: 58.333%; } /* 7/12 */
.col-8 { flex: 0 0 66.666%; max-width: 66.666%; } /* 8/12 */
.col-9 { flex: 0 0 75%; max-width: 75%; }         /* 9/12 */
.col-10 { flex: 0 0 83.333%; max-width: 83.333%; } /* 10/12 */
.col-11 { flex: 0 0 91.666%; max-width: 91.666%; } /* 11/12 */
.col-12 { flex: 0 0 100%; max-width: 100%; }       /* 12/12 */

/* 偏移 */
.offset-1 { margin-left: 8.333%; }
.offset-2 { margin-left: 16.666%; }
.offset-3 { margin-left: 25%; }

/* 小屏幕(sm) */
@media (min-width: 576px) {
    .col-sm-1 { flex: 0 0 8.333%; max-width: 8.333%; }
    .col-sm-2 { flex: 0 0 16.666%; max-width: 16.666%; }
    .col-sm-3 { flex: 0 0 25%; max-width: 25%; }
    .col-sm-4 { flex: 0 0 33.333%; max-width: 33.333%; }
    .col-sm-6 { flex: 0 0 50%; max-width: 50%; }
    .col-sm-12 { flex: 0 0 100%; max-width: 100%; }
}

/* 中等屏幕(md) */
@media (min-width: 768px) {
    .col-md-1 { flex: 0 0 8.333%; max-width: 8.333%; }
    .col-md-2 { flex: 0 0 16.666%; max-width: 16.666%; }
    .col-md-3 { flex: 0 0 25%; max-width: 25%; }
    .col-md-4 { flex: 0 0 33.333%; max-width: 33.333%; }
    .col-md-6 { flex: 0 0 50%; max-width: 50%; }
    .col-md-12 { flex: 0 0 100%; max-width: 100%; }
}

/* 大屏幕(lg) */
@media (min-width: 992px) {
    .col-lg-1 { flex: 0 0 8.333%; max-width: 8.333%; }
    .col-lg-2 { flex: 0 0 16.666%; max-width: 16.666%; }
    .col-lg-3 { flex: 0 0 25%; max-width: 25%; }
    .col-lg-4 { flex: 0 0 33.333%; max-width: 33.333%; }
    .col-lg-6 { flex: 0 0 50%; max-width: 50%; }
    .col-lg-12 { flex: 0 0 100%; max-width: 100%; }
}
html
<!-- 使用示例 -->
<div class="row">
    <!-- 手机 1 列,平板 2 列,桌面 3 列 -->
    <div class="col-12 col-md-6 col-lg-4">
        <div class="card">卡片 1</div>
    </div>
    <div class="col-12 col-md-6 col-lg-4">
        <div class="card">卡片 2</div>
    </div>
    <div class="col-12 col-md-6 col-lg-4">
        <div class="card">卡片 3</div>
    </div>
</div>

<!-- 侧边栏布局 -->
<div class="row">
    <div class="col-12 col-md-8">
        <main>主内容区</main>
    </div>
    <div class="col-12 col-md-4">
        <aside>侧边栏</aside>
    </div>
</div>

响应式表格

水平滚动表格

css
/* 表格容器添加滚动 */
.table-responsive {
    overflow-x: auto;  /* 水平滚动 */
    -webkit-overflow-scrolling: touch;  /* iOS 平滑滚动 */
}

/* 表格样式 */
.table {
    width: 100%;
    border-collapse: collapse;
    min-width: 600px;  /* 最小宽度,触发滚动 */
}

.table th,
.table td {
    padding: 12px 15px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

.table th {
    background-color: #f8f9fa;
    font-weight: bold;
}

/* 斑马纹 */
.table tbody tr:nth-child(even) {
    background-color: #f9f9f9;
}
html
<!-- 响应式表格结构 -->
<div class="table-responsive">
    <table class="table">
        <thead>
            <tr>
                <th>姓名</th>
                <th>邮箱</th>
                <th>电话</th>
                <th>地址</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>张三</td>
                <td>zhangsan@example.com</td>
                <td>13800138000</td>
                <td>北京市朝阳区</td>
                <td><button>编辑</button></td>
            </tr>
        </tbody>
    </table>
</div>

卡片式表格(移动端)

css
/* 桌面端:普通表格 */
.responsive-table {
    width: 100%;
}

.responsive-table thead {
    display: table-header-group;
}

.responsive-table th {
    display: table-cell;
}

.responsive-table td {
    display: table-cell;
}

/* 移动端:卡片式布局 */
@media (max-width: 576px) {
    /* 隐藏表头 */
    .responsive-table thead {
        display: none;
    }
    
    /* 每行变成卡片 */
    .responsive-table tr {
        display: block;
        margin-bottom: 15px;
        border: 1px solid #ddd;
        border-radius: 8px;
        padding: 15px;
        background-color: white;
    }
    
    /* 每个单元格变成行 */
    .responsive-table td {
        display: flex;
        justify-content: space-between;
        padding: 8px 0;
        border-bottom: 1px solid #eee;
    }
    
    .responsive-table td:last-child {
        border-bottom: none;
    }
    
    /* 使用 data-label 属性显示标签 */
    .responsive-table td::before {
        content: attr(data-label);
        font-weight: bold;
        margin-right: 10px;
    }
}
html
<!-- 卡片式表格 HTML -->
<table class="responsive-table">
    <thead>
        <tr>
            <th>姓名</th>
            <th>邮箱</th>
            <th>电话</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td data-label="姓名">张三</td>
            <td data-label="邮箱">zhangsan@example.com</td>
            <td data-label="电话">13800138000</td>
        </tr>
    </tbody>
</table>

响应式隐藏/显示

css
/* 响应式显示/隐藏类 */

/* 在特定断点隐藏 */
.hide-mobile {
    display: block;
}

@media (max-width: 767.98px) {
    .hide-mobile {
        display: none !important;  /* 手机端隐藏 */
    }
}

.hide-tablet {
    display: block;
}

@media (min-width: 768px) and (max-width: 991.98px) {
    .hide-tablet {
        display: none !important;  /* 平板端隐藏 */
    }
}

.hide-desktop {
    display: block;
}

@media (min-width: 992px) {
    .hide-desktop {
        display: none !important;  /* 桌面端隐藏 */
    }
}

/* 仅在特定断点显示 */
.show-mobile {
    display: none !important;
}

@media (max-width: 767.98px) {
    .show-mobile {
        display: block !important;  /* 仅手机端显示 */
    }
}

.show-tablet {
    display: none !important;
}

@media (min-width: 768px) and (max-width: 991.98px) {
    .show-tablet {
        display: block !important;  /* 仅平板端显示 */
    }
}

.show-desktop {
    display: none !important;
}

@media (min-width: 992px) {
    .show-desktop {
        display: block !important;  /* 仅桌面端显示 */
    }
}

实战案例:响应式卡片布局

html
<!-- 响应式卡片布局示例 -->
<div class="card-container">
    <article class="card">
        <div class="card-image">
            <img src="image1.jpg" alt="卡片图片">
        </div>
        <div class="card-content">
            <h3 class="card-title">卡片标题</h3>
            <p class="card-text">这是卡片的描述内容,介绍卡片的主要信息。</p>
            <a href="#" class="card-link">了解更多</a>
        </div>
    </article>
    
    <article class="card">
        <div class="card-image">
            <img src="image2.jpg" alt="卡片图片">
        </div>
        <div class="card-content">
            <h3 class="card-title">卡片标题</h3>
            <p class="card-text">这是卡片的描述内容,介绍卡片的主要信息。</p>
            <a href="#" class="card-link">了解更多</a>
        </div>
    </article>
    
    <article class="card">
        <div class="card-image">
            <img src="image3.jpg" alt="卡片图片">
        </div>
        <div class="card-content">
            <h3 class="card-title">卡片标题</h3>
            <p class="card-text">这是卡片的描述内容,介绍卡片的主要信息。</p>
            <a href="#" class="card-link">了解更多</a>
        </div>
    </article>
</div>
css
/* 卡片容器 */
.card-container {
    display: grid;
    gap: 30px;
    padding: 20px;
    max-width: 1200px;
    margin: 0 auto;
}

/* 卡片样式 */
.card {
    background-color: white;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    transition: transform 0.3s, box-shadow 0.3s;
}

.card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}

/* 卡片图片 */
.card-image {
    position: relative;
    padding-bottom: 60%;  /* 图片比例 */
    overflow: hidden;
}

.card-image img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.3s;
}

.card:hover .card-image img {
    transform: scale(1.05);  /* 悬停时图片放大 */
}

/* 卡片内容 */
.card-content {
    padding: 20px;
}

.card-title {
    font-size: 1.25rem;
    margin: 0 0 10px;
    color: #333;
}

.card-text {
    color: #666;
    line-height: 1.6;
    margin-bottom: 15px;
}

.card-link {
    display: inline-block;
    color: #4CAF50;
    text-decoration: none;
    font-weight: bold;
    transition: color 0.3s;
}

.card-link:hover {
    color: #388E3C;
}

/* 响应式布局 */
/* 默认:1 列 */
.card-container {
    grid-template-columns: 1fr;
}

/* 平板:2 列 */
@media (min-width: 576px) {
    .card-container {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* 桌面:3 列 */
@media (min-width: 992px) {
    .card-container {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* 大屏:4 列 */
@media (min-width: 1200px) {
    .card-container {
        grid-template-columns: repeat(4, 1fr);
    }
}

调试技巧

浏览器开发者工具

现代浏览器都提供了响应式设计模式:

  1. Chrome DevTools:按 F12 打开,点击设备图标或按 Ctrl+Shift+M
  2. Firefox DevTools:按 F12 打开,点击响应式设计模式图标
  3. Edge DevTools:按 F12 打开,点击设备仿真图标

功能包括:

  • 模拟不同设备尺寸
  • 自定义视口大小
  • 模拟触摸事件
  • 模拟网络速度
  • 截图功能

显示断点指示器

css
/* 调试时显示当前断点 */
body::before {
    content: 'xs';
    position: fixed;
    bottom: 10px;
    right: 10px;
    padding: 5px 10px;
    background-color: rgba(0, 0, 0, 0.7);
    color: white;
    font-size: 12px;
    border-radius: 4px;
    z-index: 9999;
}

@media (min-width: 576px) {
    body::before { content: 'sm'; background-color: #4CAF50; }
}

@media (min-width: 768px) {
    body::before { content: 'md'; background-color: #2196F3; }
}

@media (min-width: 992px) {
    body::before { content: 'lg'; background-color: #FF9800; }
}

@media (min-width: 1200px) {
    body::before { content: 'xl'; background-color: #9C27B0; }
}

最佳实践

  1. 移动优先:先设计移动端样式,再逐步增强到桌面端
  2. 合理断点:根据内容而非设备设置断点
  3. 性能优化:避免不必要的资源加载
  4. 触摸友好:按钮和链接足够大,间距合理
  5. 测试覆盖:在各种设备和浏览器上测试
  6. 渐进增强:确保基本功能在所有设备上可用

小结

本章学习了响应式设计的核心概念和技术:

  • 视口设置:使用 meta viewport 标签控制移动端显示
  • 媒体查询:根据设备特性应用不同样式
  • 断点设置:合理设置响应式断点
  • 弹性布局:使用流式布局和弹性图片
  • 响应式组件:导航、网格、表格等组件的响应式实现
  • 调试技巧:使用开发者工具和断点指示器

响应式设计是现代网页开发的必备技能,掌握这些技术可以创建出在各种设备上都有良好体验的网页。