一、响应式 web 设计基础
1. 什么是响应式 Web 设计?
所谓响应式 Web 设计,就是网页内容会随着访问它的视口及设备的不同而呈现不同的样式
最初,响应式设计是从“桌面”、固定宽度设计开始的。然后,到了小屏幕上,内容会重排,或者根据情况隐藏部分内容。可是,随着时间推移,人们发现,还是采用相反的设计思路更好,即先为小屏幕设计内容、样式,然后再向大屏幕扩展。
2. 浏览器支持
浏览器支持一直是响应式 Web 设计面临的最大问题。市面上如此多的浏览器和设备,要想一个不落地支持并不现实。有时候是时间问题,有时候是预算问题,有时候两方面问题都有。
一般来说,要支持的浏览器版本越早,想达到现代浏览器中相同功能和效果所需的工作量就越大。因此,最好的做法是先写一个较轻量的代码架构,然后根据所需的体验针对能力更强的浏览器进行扩展,包括视觉和功能。
在通常情况下,我们写的网站必须在所有常用浏览器里表现正常。除了基本功能,有必要提前确定针对哪个平台要实现最强的功能,以便对其他平台上的视觉和功能作出相应取舍。这是非常实际的做法,因为从最简单的“基本”体验开始,逐步扩充(所谓“渐进增强”)更容易。相反,先做出大而全的版本,然后再针对能力不足的平台寻找后备方案(所谓“平稳退化”)则要麻烦得多。
兼容性参考:caniuse.com
二、媒体查询
响应式网页相关的基本要素:弹性布局、弹性图片和媒体查询
1.为什么响应式 Web 设计需要媒体查询
CSS3 媒体查询可以让我们针对特定的设备能力或条件为网页应用特定的 CSS 样式。如果没有媒体查询,光用 CSS 是无法大大修改网页外观的。这个模块让我们可以提前编写出适应很多不可预测因素的 CSS 规则,比如屏幕方向水平或垂直、视口或大或小,等等。
2.媒体查询的语法
可以在 link 标签中使用媒体查询,<link>
标签的 media 属性中指定设备类型( screen 或 print ),为不同设备应用样式表
<link
rel="style sheet"
type="text/css"
media="screen"
href="screenstyles.css"
/>
媒体查询更进一步,不仅可以指定设备类型,还能指定设备的能力和特性。可以将其想象为对浏览器的询问。如果浏览器回答“是”,那么就会应用对应的样式表。如果浏览器回答“否”,就不会应用
媒体查询还可以问:“你是在有屏幕的设备上,而且设备是垂直朝向的吗?”
<!--样式表portrait-screen.css会应用给任何有屏幕且屏幕方向垂直的设备 -->
<link
rel="stylesheet"
media="screen and (orientation: portrait)"
href="portrait-screen.css"
/>
<!--样式表应用给垂直朝向的非屏幕设备 -->
<link
rel="stylesheet"
media="not screen and (orientation: portrait)"
href="portrait-screen.css"
/>
3.组合媒体查询
可以将多个媒体查询串在一起。比如,在前面一个示例的基础上,可以进一步限制只把样式表应用给视口大于 800 像素的设备
<link
rel="stylesheet"
media="screen and (orientation: portrait) and(min-width: 800px)"
href="800wide-portrait-screen.css"
/>
此外,可以组合多个媒体查询。只要其中任何一个媒体查询表达式为真,就会应用样式; 如果没有一个为真,则样式表没用
<link
rel="stylesheet"
media="screen and (orientation: portrait) and(min-width: 800px), projection"
href="800wide-portrait-screen.css"
/>
-
@import 与媒体查询
CSS 中的 @import 会增加 HTTP 请求(进而影响加载速度),因此请慎用
/* 以下代码会导入样式表phone.css,但条件是必须是屏幕设备,而且视口不超过360像素 */ @import url("phone.css") screen and (max-width: 360px);
-
在 CSS 中使用媒体查询
/* 屏幕设备的宽度为400像素及以下时把所有 h1 元素变 成绿色 */ @media screen and (max-device-width: 400px) { h1 { color: green; } }
媒体查询的常用特性
width :视口宽度。
height :视口高度。
device-width :渲染表面的宽度(可以认为是设备屏幕的宽度)。
device-height :渲染表面的高度(可以认为是设备屏幕的高度)。
orientation :设备方向是水平还是垂直。
aspect-ratio :视口的宽高比。16∶9 的宽屏显示器可以写成 aspect-ratio:16/9 。
color :颜色组分的位深。比如 min-color:16 表示设备至少支持 16 位深。
color-index :设备颜色查找表中的条目数,值必须是数值,且不能为负。
monochrome :单色帧缓冲中表示每个像素的位数,值必须是数值(整数),比 monochrome: 2 ,且不能为负。
resolution :屏幕或打印分辨率,比如 min-resolution: 300dpi 。也可以接受每厘米多少点,比如 min-resolution: 118dpcm 。
scan :针对电视的逐行扫描(progressive)和隔行扫描(interlace)。例如 720p HD TV(720p 中的 p 表示 progressive,即逐行)可以使用 scan: progressive 来判断; 而 1080i HD TV(1080i 中的 i 表示 interlace,即隔行)可以使用 scan: interlace 来判断。
grid :设备基于栅格还是位图。
/* tiny.css只在设备视口介于200像素和360像素之间时才会被应用 */
@import url("tiny.css") screen and (min-width: 200px) and (maxwidth: 360px);
4.组织和编写媒体查询的注意事项
4.1 使用媒体查询链接不同的 CSS 文件
从浏览器的角度看,CSS 属于“阻塞渲染”的资源。换句话说,浏览器需要下载并解析链接的 CSS 文件,然后再渲染页面,不过,现代浏览器都很聪明,知道哪些样式表(在头部通过媒体查询链接的样表)必须立即分析,而哪些样式可以等到页面初始渲染结束后再处理。在这些浏览器看来,不符合媒体查询指定条件(比如屏幕比媒体查询指定的小)的 CSS 文件可以延缓执行(deferred),到页面初始加载后再处理,以便让用户感觉页面加载速度更快。
因此,如果浏览器要加载的响应式页面,通过不同的媒体查询链接了 4 个不同的样式表(分别为不同视口的设备应用样式),那它就会下载 4 个 CSS 文件,但在渲染页面之前,它只会解析那个针对当前视口大小的样式表。
<head>
<link rel="stylesheet" media="(min-width: 300px)" href="w300.css" />
<link rel="stylesheet" media="(min-width: 600px)" href="w600.css" />
<link rel="stylesheet" media="(min-width: 900px)" href="w900.css" />
<link rel="stylesheet" media="(min-width: 1200px)" href="w1200.css/" />
</head>
<body>
<div class="w300">width 300px file downloaded</div>
<div class="w600">width 600px file downloaded</div>
<div class="w900">width 900px file downloaded</div>
<div class="w1200">width 1200px file downloaded</div>
</body>
4.2 分隔媒体查询的利弊
编写多个媒体查询分别对应不同的样式表虽然有好处,但有时候也不一定。
多一个文件就要多一次 HTTP 请求,在某些条件下,HTTP 请求多了会明显影响页面加载速度。Web 开发可不是件容易的事儿!此时应该关注的是网站的整体性能
4.3 把媒体查询写在常规样式表中
除非在极端情况下,否则我都建议在既有的样式表中写媒体查询,跟常规的规则写在一起
5. 问题思考
是该把媒体查询声明在相关的选择符下面,还是该把相同的媒体查询并列起来,每个媒体查询单独一块?
/* 重复写两遍 @media 真的是冗余和浪费吗? */
.thing {
width: 50%;
}
@media screen and (min-width: 30rem) {
.thing {
width: 75%;
}
}
.thing2 {
width: 65%;
}
@media screen and (min-width: 30rem) {
.thing2 {
width: 75%;
}
}
/* 从维护代码的角度看,这种写法不利于维护 */
.thing {
width: 50%;
}
.thing2 {
width: 65%;
}
@media screen and (min-width: 30rem) {
.thing {
width: 75%;
}
.thing2 {
width: 75%;
}
}
6. 关于视口的 meta 标签
<meta name="viewport" content="initial-scale=2.0,width=device-width" />
- name="viewport" 表示针对视口
- content="initial-scale=2.0" 的意思是“把内容放大为实际大小的两倍”(0.5 就是一半,3.0 就是三倍)
- width=device-width 告诉浏览器页面的宽度等于设备的宽度( width=device-width )
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
- user-scalable=no 是禁止用户缩放的
三、弹性布局与响应式图片
混沌初开之际(大约公元 20 世纪 90 年代末),网站的宽度大都以百分比形式定义。百分比布局使得网页宽度能够随着查看它们的屏幕窗口大小变化,因而得名弹性布局。
媒体查询虽然可以让我们根据视口大小分别切换不同的样式,但我们的设计在这些“断点”之间必须要平滑过渡才行。而使用弹性布局就可以轻松解决这个问题,实现设计在媒体查询断点间的平滑过渡
1.将固定像素大小转换为弹性比例大小
Photoshop、Illustrator、(令人怀念的)Fireworks 以及 Sketch 等图形图像软件做出来的图都是以固定像素来衡量大小的。开发者如果要在弹性布局中使用这些图,有时候需要将固定像素大小转换为比例大小
公式:结果 = 目标/上下文
可以这么想:用元素所在容器的大小除元素的大小,理解了这个转换,就可以将任何固定大小布局转换成响应式或弹性布局
2. 基础知识检讨
- 行内块与空白:
使用行内块(inline-block)来布局的最大问题
就是它会在HTML元素间渲染空白
- 浮动缺点:
第一个,如果给浮动元素的宽度设定百分比,那么最终计算值在不同平台上的结果不一样(有
的浏览器向上取整,有的浏览器向下取整)。于是,有时候某些区块会跑到其他区块底下,而有
时候这些区块一侧又会莫名出现一块明显的间隙
第二个,通常都要清除浮动,才能避免父盒子/元素折叠
- 表格与表元
别把 display:table 和 display:table-cell 与对应的HTML元素搞混!
这两个CSS属性只是用于模仿它们的好兄弟的。实际上,它们不会真正影响HTML的结构
3. 使用 Flexbox
以上所有布局方法都存在严重缺陷,Flexbox 可以解决前面提到的显示机制的问题,关于它的超能力,可以概括如下:
- 方便地垂直居中内容
- 改变元素的视觉次序
- 在盒子里自动插入空白以及对齐元素,自动对齐元素间的空白
Flexbox 有 4 个关键特性:方向、对齐、次序和弹性
display: flex
align-items :这是要在Flexbox中沿交叉轴对齐项目
justify-content :在这里设置内容沿主轴居中(如Word中的左中右对齐按钮)
偏移
<style>
.MenuWrap {
background-color: indigo;
font-family: "Oswald", sans-serif;
font-size: 1rem;
min-height: 2.75rem;
display: flex;
align-items: center;
padding: 0 1rem;
}
.ListItem,
.LastItem {
color: #ebebeb;
text-decoration: none;
}
.ListItem {
margin-right: 1rem;
}
.LastItem {
/* 魔法魔法魔法魔法魔法魔法魔法 */
margin-left: auto;
}
</style>
<body>
<div class="MenuWrap">
<a href="#" class="ListItem">Home</a>
<a href="#" class="ListItem">About Us</a>
<a href="#" class="ListItem">Products</a>
<a href="#" class="ListItem">Policy</a>
<a href="#" class="LastItem">Contact Us</a>
</div>
</body>
反序
<style>
.MenuWrap {
background-color: indigo;
font-family: "Oswald", sans-serif;
font-size: 1rem;
min-height: 2.75rem;
display: flex;
/* ---------- */
flex-direction: row-reverse;
/* ---------- */
align-items: center;
padding: 0 1rem;
}
.ListItem,
.LastItem {
color: #ebebeb;
text-decoration: none;
}
.ListItem {
margin-right: 1rem;
}
.LastItem {
/* ---------- */
margin-right: auto;
/* ---------- */
}
</style>
<body>
<div class="MenuWrap">
<a href="#" class="ListItem">Home</a>
<a href="#" class="ListItem">About Us</a>
<a href="#" class="ListItem">Products</a>
<a href="#" class="ListItem">Policy</a>
<a href="#" class="LastItem">Contact Us</a>
</div>
</body>
对齐
关于 Flexbox 的对齐,最重要的是理解坐标轴。有两个轴,“主轴”和“交叉轴”,比如,如果将 Flexbox 的方向设置为 row ,则主轴就是横轴,而交叉轴就是纵轴
flex
flex 实际上是三个属性合体的简写: flex-grow 、 flex-shrink 和 flex-basis
4. 响应式图片
响应式图片的固有问题是开发者不可能知道或预见浏览网站的所有设备,只有浏览器在打开和渲染内容时才会知道使用它的设备的具体情况(屏幕大小、设备能力等)。另一方面,只有开发者(你和我)知道自己手里有几种大小的图片。比如,我们有同一图片的三个版本,分别是小、中、大,分别对应于相应的屏幕大小和分辨率。浏览器不知道这些,我们得想办法让它知道
通过 srcset 切换分辨率
假设一张图片有三种分辨率的版本,一张小的针对小屏幕,一个中等的针对中等屏幕,还有一个比较大的针对所有其他屏幕。要让浏览器知道这三个版本,怎么办呢?
<!-- 图片名 分辨率说明-->
<img
src="scones_small.jpg"
srcset="scones_medium.jpg 1.5x, scones_large.jpg 2x"
alt="Scones taste amazing"
/>
srcset 及 sizes 联合切换
经常可以看到小屏幕中全屏显示,而在大屏幕上只显示一半宽的图
<!-- 添加了以 w 为后缀的值。这个值的意思是告诉浏览器图片有多宽 -->
<img
srcset="scones-small.jpg 450w, scones-medium.jpg 900w"
sizes="(min-width: 17em) 100vw, (min-width: 40em) 50vw"
src="sconessmall.jpg"
alt="Scones"
/>
picture 元素
首先,要知道 picture 元素只是一个容器,为我们给其中的 img 元素指定图片提供便利。假如你想为图片添加样式,那目标应该是它内部的那个 img 。
<picture>
<source media="(min-width: 30em)" srcset="cake-table.jpg" />
<source media="(min-width: 60em)" srcset="cake-shop.jpg" />
<img src="scones.jpg" alt="One way or another, you WILL getcake." />
</picture>