四、响应式图片
4.1 弹性图片
/* 基础弹性图片 */
img {
max-width: 100%;
height: auto;
}
/* 背景图片响应式 */
.hero {
background-image: url('hero-mobile.jpg');
background-size: cover;
background-position: center;
}
@media (min-width: 768px) {
.hero {
background-image: url('hero-desktop.jpg');
}
}
4.2 srcset 属性
<!-- 基于设备像素比 -->
<img src="image-1x.jpg"
srcset="image-1x.jpg 1x, image-2x.jpg 2x, image-3x.jpg 3x"
alt="响应式图片">
<!-- 基于宽度描述符 -->
<img src="image-small.jpg"
srcset="image-small.jpg 480w,
image-medium.jpg 768w,
image-large.jpg 1200w"
sizes="(max-width: 480px) 100vw,
(max-width: 768px) 90vw,
1200px"
alt="响应式图片">
4.3 picture 元素
<!-- 根据媒体条件切换图片 -->
<picture>
<source media="(min-width: 1200px)" srcset="image-large.jpg">
<source media="(min-width: 768px)" srcset="image-medium.jpg">
<source media="(min-width: 480px)" srcset="image-small.jpg">
<img src="image-default.jpg" alt="响应式图片">
</picture>
<!-- 根据格式支持切换 -->
<picture>
<source type="image/webp" srcset="image.webp">
<source type="image/avif" srcset="image.avif">
<img src="image.jpg" alt="图片">
</picture>
<!-- 组合使用:格式 + 尺寸 -->
<picture>
<source type="image/webp"
srcset="image-large.webp 1200w,
image-medium.webp 768w,
image-small.webp 480w"
sizes="(max-width: 480px) 100vw,
(max-width: 768px) 90vw,
1200px">
<img src="image.jpg" alt="图片">
</picture>
4.4 图片懒加载
<!-- 原生懒加载 -->
<img src="image.jpg" loading="lazy" alt="懒加载图片">
<iframe src="video.html" loading="lazy"></iframe>
<!-- 自定义懒加载实现 -->
<img data-src="image.jpg" class="lazy" alt="懒加载图片">
<script>
const lazyImages = document.querySelectorAll('.lazy');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
lazyImages.forEach(img => imageObserver.observe(img));
</script>
五、响应式字体
5.1 相对单位
/* em - 相对于父元素字体大小 */
.parent {
font-size: 16px;
}
.child {
font-size: 1.5em; /* 24px */
margin: 1em; /* 24px */
}
/* rem - 相对于根元素字体大小 */
html {
font-size: 16px;
}
.element {
font-size: 1rem; /* 16px */
margin: 2rem; /* 32px */
}
/* vw/vh - 相对于视口 */
h1 {
font-size: 5vw; /* 视口宽度的5% */
}
.hero {
height: 100vh; /* 视口高度的100% */
}
/* 响应式字体组合 */
html {
font-size: 16px;
}
@media (min-width: 768px) {
html {
font-size: 18px;
}
}
@media (min-width: 1200px) {
html {
font-size: 20px;
}
}
/* 使用 clamp() 实现流体字体 */
h1 {
font-size: clamp(24px, 5vw, 48px);
/* 最小24px,理想5vw,最大48px */
}
p {
font-size: clamp(14px, 3vw, 18px);
}
5.2 视口单位详解
/* 视口单位 */
.element {
width: 100vw; /* 视口宽度的100% */
height: 100vh; /* 视口高度的100% */
min-height: 100vh; /* 常用于全屏布局 */
}
/* 动态视口单位(现代浏览器) */
.element {
width: 100dvw; /* 动态视口宽度(考虑地址栏) */
height: 100dvh; /* 动态视口高度 */
width: 100svw; /* 小视口宽度 */
width: 100lvw; /* 大视口宽度 */
}
/* 视口最小/最大单位 */
.element {
width: 100vmin; /* 视口宽度和高度的最小值 */
height: 100vmax; /* 视口宽度和高度的最大值 */
}
5.3 字体缩放策略
/* 移动优先字体方案 */
body {
font-size: 14px;
line-height: 1.5;
}
h1 {
font-size: 1.5rem; /* 21px */
margin: 0.5em 0;
}
h2 {
font-size: 1.25rem; /* 17.5px */
}
@media (min-width: 768px) {
body {
font-size: 16px;
}
h1 {
font-size: 2rem; /* 32px */
}
h2 {
font-size: 1.5rem; /* 24px */
}
}
/* 使用 clamp 实现流体字体系统 */
:root {
--font-xs: clamp(10px, 2vw, 12px);
--font-sm: clamp(12px, 3vw, 14px);
--font-base: clamp(14px, 4vw, 16px);
--font-lg: clamp(16px, 5vw, 18px);
--font-xl: clamp(18px, 6vw, 20px);
--font-2xl: clamp(20px, 7vw, 24px);
--font-3xl: clamp(24px, 8vw, 32px);
--font-4xl: clamp(32px, 10vw, 48px);
}
body {
font-size: var(--font-base);
}
六、响应式导航
6.1 汉堡菜单
<!DOCTYPE html>
<html>
<head>
<style>
/* 基础样式 */
.nav {
background: #333;
position: relative;
}
.nav-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
max-width: 1200px;
margin: 0 auto;
}
.logo {
color: white;
font-size: 1.5rem;
text-decoration: none;
}
.nav-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
}
.nav-toggle span {
display: block;
width: 25px;
height: 3px;
background: white;
margin: 5px 0;
transition: 0.3s;
}
.nav-menu {
display: flex;
gap: 2rem;
list-style: none;
margin: 0;
padding: 0;
}
.nav-menu a {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
}
/* 移动端样式 */
@media (max-width: 768px) {
.nav-toggle {
display: block;
}
.nav-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #333;
flex-direction: column;
gap: 0;
padding: 1rem 0;
}
.nav-menu.active {
display: flex;
}
.nav-menu li {
text-align: center;
}
.nav-menu a {
display: block;
padding: 1rem;
}
/* 汉堡菜单动画 */
.nav-toggle.active span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.nav-toggle.active span:nth-child(2) {
opacity: 0;
}
.nav-toggle.active span:nth-child(3) {
transform: rotate(-45deg) translate(7px, -6px);
}
}
</style>
</head>
<body>
<nav class="nav">
<div class="nav-container">
<a href="/" class="logo">Logo</a>
<button class="nav-toggle" id="navToggle">
<span></span>
<span></span>
<span></span>
</button>
<ul class="nav-menu" id="navMenu">
<li><a href="/">首页</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/services">服务</a></li>
<li><a href="/contact">联系</a></li>
</ul>
</div>
</nav>
<script>
const navToggle = document.getElementById('navToggle');
const navMenu = document.getElementById('navMenu');
navToggle.addEventListener('click', () => {
navToggle.classList.toggle('active');
navMenu.classList.toggle('active');
});
</script>
</body>
</html>
6.2 响应式导航模式
/* 模式1:水平导航变垂直 */
.horizontal-nav {
display: flex;
justify-content: center;
gap: 2rem;
}
@media (max-width: 768px) {
.horizontal-nav {
flex-direction: column;
align-items: center;
gap: 1rem;
}
}
/* 模式2:下拉菜单 */
.dropdown {
position: relative;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
min-width: 200px;
display: none;
}
.dropdown:hover .dropdown-menu {
display: block;
}
@media (max-width: 768px) {
.dropdown-menu {
position: static;
box-shadow: none;
padding-left: 20px;
}
}
/* 模式3:顶部导航变底部导航 */
@media (max-width: 768px) {
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
display: flex;
justify-content: space-around;
padding: 10px;
}
.main-content {
padding-bottom: 70px;
}
}