现在,各种屏幕尺寸的设备越来越多,一个网页要在不同设备上都显示正常美观,就需要一定的技术手段来实现。我们可以针对不同设备开发不同的网页版本,但是这种方式会极大的增加开发工作量,另一种方式就是使用响应式设计。
什么是响应式网页设计
响应式网页设计,指的是,利用 CSS 或 JavaScript,让同一个网页,能根据屏幕的大小,自动调整布局和样式,包括对容器元素、布局、图片和字体的响应式调整。这个视频我们来看一下如何利用 CSS 来实现响应式。
纯 HTML 元素默认都是响应式的
要注意的是,原生的 HTML 元素,在没有编写任何 CSS 的情况下,本身就是响应式的,不过每个网页都避免不了使用 CSS 设置样式,响应式则主要针对设置了固定宽度,或者影响布局的元素进行调整。
开启移动设备响应式
要开启响应式,需要在在 html 中添加一个 meta 元素,设置 name 属性值为 viewport, content 属性值为 content="width=device-width, initial-scale=1.0"
,这是因为移动设备下,默认是对 PC 网页进行缩放来展示,把它缩小到能放下整个网站为止,用户体验不友好,在设置 width=device-width
之后,网页的可视区域(视口)就会取移动设备屏幕的宽度,设置了 initial-scale 为 1.0 之后,就会使用正常的缩放比例,即 1 倍,不缩放。设置 view port <meta name="viewport" content="width=device-width, initial-scale=1.0">
容器响应式
对于设置了固定宽度的容器,在浏览器可视区域大于它的宽度时,会有大片的空白,如果小于它的宽度就会有横向滚动条,影响阅读:
<div class="container">
<p>
晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。<b>忽逢桃花林,夹岸数百步</b>,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。见渔人,乃大惊,<del>问所从来。具答之</del>。便要还家,设酒杀鸡作食。<em>村中闻有此人,咸来问讯</em>。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。南阳刘子骥,高尚士也,闻之,<strong>欣然规往</strong>。未果,寻病终,后遂无问津者。
</p>
</div>
.container { width: 1200px; }
那么,我们可以百分比宽度,来让它根据浏览器的可视区域宽度,自动调整尺寸:
.container { width: 100%; }
或者使用 max-width,限制最大宽度:
.container {
max-width: 80%;
margin: 0 auto;
}
如果必须使用固定宽度,我们可以利用 media query,查询可视区域的宽度,根据不同的宽度,调整容器的尺寸:
.container {
width: 800px;
}
@media (max-width: 900px) {
.container {
width: 600px;
}
}
@media (max-width: 700px) {
.container {
width: 500px;
}
}
布局响应式
对于布局,HTML 元素默认为竖向排列,这个情况下没有问题。如果对里边的子元素进行横向排列,例如使用 flex 布局,那么在浏览器可视区域过窄时,会影响阅读:
.container {
display: flex;
}
我们可以利用 flex-wrap 和 flex 属性,来让 flex 子元素保持最小宽度,并自动折行:
/* 当屏幕过窄,会影响阅读,设置换行 */
.container {
display: flex;
flex-wrap: wrap;
}
.container p {
/* 每个 flex item 最小宽度 */
flex: 250px;
}
如果是使用 grid 布局,默认它是竖向排列,这样没问题:
.container {
display: grid;
}
但如果是多列布局,并且每列都是浮动宽度的话,也会出现 flex 一样影响阅读的问题:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
这时有两种解决方法,第一种方法是,在一行中,尽量容纳最多的列,通过 repeat 和 minmax 函数,设置每列最小宽度为 250px,超出后自动折行,与 flex-wrap 效果类似:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
第二种方法是:使用 media query,手动控制列数:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
@media (max-width: 900px) {
.container {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 600px) {
.container {
grid-template-columns: 1fr;
}
}
图片响应式
对于图片,浏览器默认会显示图片原本大小。要开启图片响应式,我们可以使用百分比宽度,设置 width 或 max-width,但是在移动设备下:
仍然会加载原图,造成体积过大,影响性能。
移动设备可能需要不同的图片纵横比。
img {
max-width: 100%;
}
这个时候也有两中解决方案:
解决方案 1,img + srcset + sizes:
srcset 设置一组不同尺寸的图片路径,多个值用逗号隔开,每个值第一项为图片 url,第二项是图片原始宽度,这里使用 w 单位而不是 px,因为浏览器还要考虑像素密度。
sizes 里通过 media query 查询可视区域宽度,满足条件时,通过第二值指定图片的宽度。
浏览器会自行根据像素密度和屏幕宽度来加载合适的图片。
<img
src="../image-300.png"
srcset="../image-1240.png 1240w, ../image-600.png 600w, ../image-300.png 300w"
sizes="(max-width: 400px) 300px, (max-width: 900px) 600px, 1240px"
/>
解决方案 2,picture:
结合 source 元素,指定图片 url 和 media query,在不同可视区域宽度下,加载不同的图片。
最后使用 img 来加载保底的图片。
<picture>
<source media="(max-width: 400px)" srcset="../image-300.png" />
<source media="(max-width: 900px)" srcset="../image-600.png" />
<img src="../image-1240.png" />
</picture>
字体响应式
对于字体响应式,我们也可以使用 media query 或浮动单位来设置。需要注意一下,字体单位使用 rem 或 em,就可以根据用户设置的浏览器默认字体大小,进行缩放,而不是固定尺寸。不过即使使用这两个单位,也需要根据屏幕的大小做响应式调整。使用 media query:
h1 {
font-size: 4rem;
}
@media (max-width: 900px) {
h1 {
font-size: 3rem;
}
}
@media (max-width: 700px) {
h1 {
font-size: 2rem;
}
}
使用浮动大小:
h1 {
font-size: 4vw;
}
防止过小,以及避免影响移动设备字体缩放:
h1 {
font-size: calc(2rem + 2vw);
}