Skip to content

响应式设计

响应式设计使网页能够适应不同设备和屏幕尺寸,提供良好的用户体验。

视口设置

在 HTML 中设置视口:

html
<meta name="viewport" content="width=device-width, initial-scale=1.0">

视口属性

属性说明
width视口宽度,通常设为 device-width
initial-scale初始缩放比例
maximum-scale最大缩放比例
minimum-scale最小缩放比例
user-scalable是否允许用户缩放

媒体查询

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

基本语法

css
@media media-type and (media-feature) {
    /* CSS 样式 */
}

媒体类型

css
@media all { }      /* 所有设备 */
@media screen { }   /* 屏幕 */
@media print { }    /* 打印 */
@media speech { }   /* 屏幕阅读器 */

常用媒体特性

css
/* 宽度 */
@media (width: 600px) { }
@media (min-width: 768px) { }
@media (max-width: 576px) { }

/* 高度 */
@media (height: 800px) { }
@media (min-height: 600px) { }
@media (max-height: 400px) { }

/* 设备方向 */
@media (orientation: portrait) { }   /* 竖屏 */
@media (orientation: landscape) { }  /* 横屏 */

/* 分辨率 */
@media (min-resolution: 2dppx) { }   /* 高清屏 */
@media (min-resolution: 300dpi) { }

/* 悬停能力 */
@media (hover: hover) { }            /* 支持悬停 */
@media (hover: none) { }             /* 不支持悬停 */

/* 指针类型 */
@media (pointer: fine) { }           /* 鼠标 */
@media (pointer: coarse) { }         /* 触摸屏 */

逻辑操作符

css
/* and - 同时满足 */
@media screen and (min-width: 768px) and (max-width: 1024px) { }

/* or - 逗号分隔 */
@media screen, print { }

/* not - 排除 */
@media not screen { }

/* only - 仅限旧浏览器 */
@media only screen and (min-width: 768px) { }

断点设置

常用断点:

css
/* 手机 */
@media (max-width: 575.98px) { }

/* 平板竖屏 */
@media (min-width: 576px) and (max-width: 767.98px) { }

/* 平板横屏 */
@media (min-width: 768px) and (max-width: 991.98px) { }

/* 小桌面 */
@media (min-width: 992px) and (max-width: 1199.98px) { }

/* 大桌面 */
@media (min-width: 1200px) { }

移动优先 vs 桌面优先

css
/* 移动优先 - 从小到大 */
.element {
    font-size: 14px;
}
@media (min-width: 768px) {
    .element {
        font-size: 16px;
    }
}
@media (min-width: 1024px) {
    .element {
        font-size: 18px;
    }
}

/* 桌面优先 - 从大到小 */
.element {
    font-size: 18px;
}
@media (max-width: 1023px) {
    .element {
        font-size: 16px;
    }
}
@media (max-width: 767px) {
    .element {
        font-size: 14px;
    }
}

响应式单位

相对单位

css
/* em - 相对父元素字体大小 */
.parent { font-size: 16px; }
.child { font-size: 1.5em; } /* 24px */

/* rem - 相对根元素字体大小 */
html { font-size: 16px; }
.element { font-size: 1.5rem; } /* 24px */

/* vw - 视口宽度的 1% */
.element { width: 50vw; }

/* vh - 视口高度的 1% */
.element { height: 100vh; }

/* vmin - vw 和 vh 中较小的值 */
.element { font-size: 5vmin; }

/* vmax - vw 和 vh 中较大的值 */
.element { font-size: 3vmax; }

/* % - 相对父元素 */
.element { width: 50%; }

calc() 函数

css
.element {
    width: calc(100% - 20px);
    height: calc(100vh - 60px);
    font-size: calc(16px + 0.5vw);
}

clamp() 函数

css
.element {
    /* 最小值 | 首选值 | 最大值 */
    font-size: clamp(14px, 2vw, 24px);
    width: clamp(300px, 50%, 800px);
}

响应式图片

max-width

css
img {
    max-width: 100%;
    height: auto;
}

srcset 属性

html
<img 
    src="small.jpg"
    srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
    sizes="(max-width: 600px) 480px, 800px"
    alt="响应式图片">

picture 元素

html
<picture>
    <source media="(min-width: 800px)" srcset="large.jpg">
    <source media="(min-width: 400px)" srcset="medium.jpg">
    <img src="small.jpg" alt="响应式图片">
</picture>

object-fit

css
img {
    width: 100%;
    height: 200px;
    object-fit: cover;     /* 裁剪填满 */
    object-fit: contain;   /* 完整显示 */
    object-fit: fill;      /* 拉伸填满 */
    object-fit: none;      /* 原始大小 */
    object-position: center;
}

响应式布局

Flexbox 响应式

css
.container {
    display: flex;
    flex-wrap: wrap;
}

.item {
    flex: 1 1 300px; /* 基础宽度300px,可伸缩 */
    margin: 10px;
}

Grid 响应式

css
.container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 20px;
}

响应式网格系统

css
.row {
    display: flex;
    flex-wrap: wrap;
    margin: -10px;
}

.col {
    flex: 1;
    padding: 10px;
}

/* 响应式列 */
@media (max-width: 767px) {
    .col { flex: 0 0 100%; }
}

@media (min-width: 768px) and (max-width: 991px) {
    .col-md-6 { flex: 0 0 50%; }
    .col-md-4 { flex: 0 0 33.333%; }
}

@media (min-width: 992px) {
    .col-lg-4 { flex: 0 0 33.333%; }
    .col-lg-3 { flex: 0 0 25%; }
}

响应式字体

css
html {
    font-size: 16px;
}

@media (min-width: 768px) {
    html {
        font-size: 18px;
    }
}

@media (min-width: 1200px) {
    html {
        font-size: 20px;
    }
}

/* 流体字体 */
h1 {
    font-size: clamp(1.5rem, 5vw, 3rem);
}

响应式导航

css
.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.nav-menu {
    display: flex;
    list-style: none;
}

/* 移动端隐藏菜单 */
@media (max-width: 767px) {
    .nav-menu {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background: white;
        flex-direction: column;
        padding: 20px;
    }
    
    .nav-menu.active {
        display: flex;
    }
    
    .nav-toggle {
        display: block;
    }
}

@media (min-width: 768px) {
    .nav-toggle {
        display: none;
    }
}

隐藏元素

css
/* 仅屏幕显示 */
.screen-only {
    display: block;
}
@media print {
    .screen-only {
        display: none;
    }
}

/* 仅打印显示 */
.print-only {
    display: none;
}
@media print {
    .print-only {
        display: block;
    }
}

/* 仅移动端显示 */
.mobile-only {
    display: block;
}
@media (min-width: 768px) {
    .mobile-only {
        display: none;
    }
}

/* 仅桌面端显示 */
.desktop-only {
    display: none;
}
@media (min-width: 768px) {
    .desktop-only {
        display: block;
    }
}

实践示例

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>响应式设计示例</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            line-height: 1.6;
            color: #333;
        }
        
        /* 响应式导航 */
        .navbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 20px;
            background-color: #333;
            color: white;
            position: relative;
        }
        
        .logo {
            font-size: 1.5rem;
            font-weight: bold;
        }
        
        .nav-menu {
            display: flex;
            list-style: none;
            gap: 20px;
        }
        
        .nav-menu a {
            color: white;
            text-decoration: none;
            transition: color 0.3s;
        }
        
        .nav-menu a:hover {
            color: #007bff;
        }
        
        .nav-toggle {
            display: none;
            background: none;
            border: none;
            color: white;
            font-size: 1.5rem;
            cursor: pointer;
        }
        
        @media (max-width: 767px) {
            .nav-menu {
                display: none;
                position: absolute;
                top: 100%;
                left: 0;
                right: 0;
                background-color: #333;
                flex-direction: column;
                padding: 20px;
                text-align: center;
            }
            
            .nav-menu.active {
                display: flex;
            }
            
            .nav-toggle {
                display: block;
            }
        }
        
        /* 响应式网格 */
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        .grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 20px;
            margin-top: 20px;
        }
        
        .card {
            background: white;
            border-radius: 8px;
            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-img {
            width: 100%;
            height: 200px;
            object-fit: cover;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        
        .card-body {
            padding: 20px;
        }
        
        .card-title {
            font-size: clamp(1.1rem, 2vw, 1.25rem);
            margin-bottom: 10px;
        }
        
        .card-text {
            color: #666;
            font-size: clamp(0.9rem, 1.5vw, 1rem);
        }
        
        /* 响应式字体 */
        h1 {
            font-size: clamp(1.5rem, 5vw, 2.5rem);
            text-align: center;
            margin-bottom: 10px;
        }
        
        .subtitle {
            text-align: center;
            color: #666;
            font-size: clamp(0.9rem, 2vw, 1.1rem);
            margin-bottom: 30px;
        }
        
        /* 响应式布局示例 */
        .layout-demo {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 30px;
        }
        
        .main-content {
            flex: 1 1 300px;
            background: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
        }
        
        .sidebar {
            flex: 0 0 250px;
            background: #e9ecef;
            padding: 20px;
            border-radius: 8px;
        }
        
        @media (max-width: 767px) {
            .sidebar {
                flex: 1 1 100%;
            }
        }
        
        /* 响应式图片 */
        .responsive-img {
            width: 100%;
            height: auto;
            max-width: 100%;
            border-radius: 8px;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <nav class="navbar">
        <div class="logo">Logo</div>
        <button class="nav-toggle" onclick="toggleMenu()">☰</button>
        <ul class="nav-menu" id="navMenu">
            <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>
    
    <div class="container">
        <h1>响应式设计示例</h1>
        <p class="subtitle">调整浏览器窗口大小查看效果</p>
        
        <div class="grid">
            <div class="card">
                <div class="card-img"></div>
                <div class="card-body">
                    <h3 class="card-title">卡片标题 1</h3>
                    <p class="card-text">这是一段卡片描述内容,展示响应式卡片布局。</p>
                </div>
            </div>
            <div class="card">
                <div class="card-img"></div>
                <div class="card-body">
                    <h3 class="card-title">卡片标题 2</h3>
                    <p class="card-text">这是一段卡片描述内容,展示响应式卡片布局。</p>
                </div>
            </div>
            <div class="card">
                <div class="card-img"></div>
                <div class="card-body">
                    <h3 class="card-title">卡片标题 3</h3>
                    <p class="card-text">这是一段卡片描述内容,展示响应式卡片布局。</p>
                </div>
            </div>
            <div class="card">
                <div class="card-img"></div>
                <div class="card-body">
                    <h3 class="card-title">卡片标题 4</h3>
                    <p class="card-text">这是一段卡片描述内容,展示响应式卡片布局。</p>
                </div>
            </div>
        </div>
        
        <div class="layout-demo">
            <div class="main-content">
                <h2>主内容区域</h2>
                <p>这是页面的主要内容区域,在小屏幕上会占满宽度,在大屏幕上与侧边栏并排显示。</p>
            </div>
            <div class="sidebar">
                <h3>侧边栏</h3>
                <p>侧边栏内容,在小屏幕上会移到主内容下方。</p>
            </div>
        </div>
    </div>
    
    <script>
        function toggleMenu() {
            const menu = document.getElementById('navMenu');
            menu.classList.toggle('active');
        }
    </script>
</body>
</html>