PWA 是专门应对手机开发而提出的概念,不过,由于手机端在国内四分五裂的局面看来(还包括 PC/Pad),屏幕尺寸的不同,网页设计的样式和大小当然也是完全不一样的。为了让 Web 能完美的在多端上运行,这里,我们就需要进行响应式开发。
响应式的目的就是让一个网站尽可能的适配所有大小的网页。当然,响应式开发所用到的技术也是非常之多,不过,主要的还是 CSS 。
meta 标签
meta 标签之所以重要,是因为它在手机端适配上起到的作用可以说是非凡的意义。meta 标签分很多种,比如:
<meta name="keywords" content="your tags" />
<meta name="description" content="150 words" />
<meta name="robots" content="index,follow" />
<meta name="author" content="author name" /> <!-- 定义网页作者 -->
<meta name="google" content="index,follow" />
<meta name="googlebot" content="index,follow" />
<meta name="verify" content="index,follow" />
//...
meta 标签根据 name 的不同有着自己独特的任务,大部分是设计 SEO 的,我这里就不详谈了。meta 与响应式相关的 name
是 viewport
。它是为了防止页面被浏览器/用户私自缩放而制定的。如何使用呢?很简单,在 head
标签中直接添加:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
</head>
下图是设置了之后的效果:
不过,上述 viewport
meta 标签对 PC 是无效的,所以,在 PC 上无需担心。我先简单讲解一下 viewport
里面的基本属性:
- width: viewport 的大小
- device-width: 当前设备屏幕的宽度
- height: viewport 的高度
- device-height: 当前设备屏幕的高度
- initial-scale: 当前页面的缩放比例。1.0 是不缩放
- maximum-scale: 当前页面的最大缩放比。
- user-scalable: 能让用户能够进行缩放页面(大部分情况是双击)。为了取消一些浏览器的 click 延迟,需要设置为
no
。
media query
MQ(media query)是在 CSS2.1 提出来的概念,在 CSS3 中得到增强。它主要是针对屏幕的相关参数的变化,做出相关的文件选取或者显示区别。具体用法为:
<link rel="stylesheet" type="text/css"
media="screen and (max-device-width: 480px)"
href="shetland.css" />
<style>
@media (max-width: 600px) {
.facet_sidebar {
display: none;
}
}
</style>
只有满足 MQ 要求的样式/CSS 才能被应用,不过,不管用不用 MQ,所有的 CSS StyleSheet 都会被下载。所以,这就比较尴尬。我们来先来看一下语法。 这是所有的语法值:
media_query_list: <media_query> [, <media_query> ]*
media_query: [[only | not]? <media_type> [ and <expression> ]*]
| <expression> [ and <expression> ]*
expression: ( <media_feature> [: <value>]? )
media_type: all | aural | braille | handheld | print |
projection | screen | tty | tv | embossed | speech
media_feature: width | min-width | max-width
| height | min-height | max-height
| aspect-ratio | min-aspect-ratio | max-aspect-ratio
| color | min-color | max-color
| color-index | min-color-index | max-color-index
| monochrome | min-monochrome | max-monochrome
| resolution | min-resolution | max-resolution
| scan | grid
我们先来看一下基础属性。
基础属性
首先,我们这里不会对所有的基础属性进行讲解,只会选常用的。基础属性需要对属性进行赋值,例如:
height:200px
width
定义设备的宽度。当然配套的就有:max-width
,min-width
。还有啥 device-width
/device-height
这两个属性,它们已经从标准从废除了,一般都用 width
/height
进行替代。 最经常用到的就是 min-width
和 max-width
这两个属性。
- min-width 表示
>=
该宽度值。min-width: 200em
。 - max-width 表示
<=
该宽度值。max-width: 200em
。
举个实在的例子,假如有一个元素要在 500px~800px 之内显示,其它范围内隐藏,使用 media query 就应该写为。
// 范围在 [500,800] 之内
@media (max-width: 800px) and (min-width:500px) {
.facet_sidebar {
display: block;
}
}
// 范围在 [500,800] 之外隐藏
@media (max-width: 500px) , (min-width:800px) {
.facet_sidebar {
display: none;
}
}
height
定义设备的高度,不过这个不常用到。用法和 width
一样,只是它是用来定义高度的。
// 范围在 [500,800] 之内
@media (max-height: 800px) and (min-height:500px) {
.facet_sidebar {
display: block;
}
}
device-ratio
该属性表示设备屏幕的宽高比,这主要是对完美主义设置的,不同屏幕的比例不一样,带来的视觉也会发生变化。注意,在赋值的时候,应该写为:宽/高。中间用 /
连接。例如,针对 3:7 和 1:1 的屏幕应用不同的 CSS:
// 3:7
<link rel="stylesheet" type="text/css"
media="device-ration: 3/7"
href="3-to-7.css" />
// 1:1
<link rel="stylesheet" type="text/css"
media="device-ration: 3:7"
href="3-to-7.css" />
另外,还有两个配套的:max-device-ratio
和 min-deivce-ratio
。用法和上面的一样,这里我就不多说了。
orientation
该属性是用来检测屏幕的翻转,即,横向和纵向。不过,它的取值有点怪。
- landscape:横向模式。大家可以理解为一般风景的时候都是广角,能够将很大的美景收入眼中。
- portrait:纵向模式(也就正常模式)。本意是肖像画的意思,大家想想
蒙娜丽莎的微笑
的画就 OK。
用法也简单:
@media (orientation: portrait) { ... }
resolution
该属性对于 retina 屏幕来说很有效,这支持两种单位,一个是 dpi(dots per inch)
和 dppx(Dots Per Pixel)
。这里,我们需要对这两个单位做一些解释,首先是 dpi。
dpi 表示每单位英寸面积内有多少像素点(注意,是物理像素点)。一般来说,Web 是大于 72dpi 的。不过,该单位不常用到 Web 中,通常是针对打印机和照相机相关参数的描述(特别是打印机,主要是该单位)。
dppx 每个设备像素(即,CSS 中的 px 单位)中包含几个物理像素,该单位和 dpr
完全,只是不同的叫法。dpr 是 device-pixel-ratio,像素分辨率。它的计算公式为:
设备像素比 = 物理像素 / 设备独立像素
一般,retina 的 dppx 是 2。不过,iphone6 一下是 2,iphone6p 是 2.64,而 iphone 6 Plus 以上是 3(尴尬)。一般情况下,我们只要对 2 和 3 做相关处理即可。当然,他还有 min-resulotion
和 max-resolution
两个配套处理。
@media (min-resolution:2dppx){ ... }
逻辑处理
在 MQ 中,逻辑处理和程序中的一样,有:and
,not
,,
(表示或),only
。例如:
@media (max-width: 800px) and (min-width:500px) {
.facet_sidebar {
display: block;
}
}
在 MQ 中,还有几个常量值,表示设备的类型,常用的有:all
,handheld
,print
,screen
等等。
- all:所有设备适用
- handheld: 手提设备
- print:打印设备
- screen:屏幕设备
例如:
@media screen and (max-width: 800px) and (min-width:500px) {
.facet_sidebar {
display: block;
}
}
不同资源文件
media
不仅可以用在 CSS 中,还可以直接运动到 link,用来筛选我们网站需要的资源,比如,根据 resolution 来进行区分:
<link rel="stylesheet" media="screen and (resolution: 0.75dppx )" href="ldpi.css" />
<link rel="stylesheet" media="screen and (resolution: 1.0dppx )" href="mdpi.css" />
<link rel="stylesheet" media="screen and (resolution: 1.5dppx )" href="hdpi.css" />
<link rel="stylesheet" media="screen and (resolution: 2.0dppx )" href="retina.css" />
或者横竖不同的样式:
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css"> // 竖放加载
<link rel="stylesheet" media="all and (orientation:landscape)"href="landscape.css"> // 横放加载
flex 布局
弹性布局是在 2009 年提出的,这是区别于以往常规布局的一种新的布局方式。它是基于一维(x/y)方向进行布局。最新提出的 grid
二维布局,则是面向未来的大型网站的布局结构。这里,我们不贪多,先讲一下 flex 的一维布局方式。
基本术语
首先声明一点,flex 布局,不仅仅是你使用 display:flex
就行。这是一个完整的概念,是由许多标签属性共同组成的。我们先来看一下它的基本术语:
其中,最为重要的是主轴(main axis) 和 辅轴(cross axis),容器(container) 和 子元素(items)的概念。我们一个一个来介绍一下:
- main axis:主轴是 flex item 在设置尺寸大小,排列方式的参照物。是有
flex-direction
设置。- main-start / main-end:主轴的开头/末尾
- main-size:主轴的尺寸大小
- cross axis:辅轴是垂直于主轴的方向。
- cross-start / cross-end:辅轴的开头/末尾
- cross-size:辅轴的大小
- container:是 flexbox 的容器元素。使用
display:flex
来进行命名。 - items:是容器的第一级子元素。两者的区别,可参照下图:
在 flexbox 中,可以根据作用对象的不同,将属性分为父容器属性和元素属性。下面,我们就这两类来展开讲解。
容器属性
定义一个容器很简单,直接使用 display:flex
进行修饰即可:
// inline-flex 是修饰行内不可分离的 flexbox 布局
.container {
display: flex; /* or inline-flex */
}
flex-direction
该标签可以用来设置 主轴 的方向,通常有 4 种:
.container {
flex-direction: row | row-reverse | column | column-reverse;
}
- row:从左到右(按照中国排版标准来定义)。默认值
- row-reverse:反向,从右到左排列。
- column:自上而下
- column-reverse:自下而上
具体排布可参照如图:
flex-wrap
该标签定义是否可以进行换行。默认情况下,所有弹性盒子里的元素都会排列在一行里。它有 3 个属性值:
.container{
flex-wrap: nowrap | wrap | wrap-reverse;
}
- nowrap:不换行。超出的元素会根据剩余空间的大小来确定自身的大小。默认值
- wrap:可以换行,如果元素超出容器的尺寸,则可以排列到下一行中。
- wrap-reverse:反向换行,该属性和上一个的区别在于,多余的那一行将会替代原来的位置。差异如图:
flex-flow
该标签没啥说的,就是 flex-direction 和 flex-wrap 的合并标签。
flex-flow: <‘flex-direction’> || <‘flex-wrap’>
justify-content
用来定义子元素在主轴上排列的方式。可用属性值有 5 个:
.container {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
- flex-start:items 紧贴着 container 的
main-start
排列。默认值 - flex-end:items 紧贴着 container 的
main-end
排列 - center:items 在 container 里,居中排布
- space-between:items 会带上空隙,在 container 中均匀排布,不过,首尾 item 紧贴边。
- space-around:每个 item 会均匀分配一些空隙,首尾只有一份空隙,其余有两份,因为,其余是两个 item 的和。具体差异如图:
align-items
定义元素在辅轴上的排列。它的几个值和 justify-content
差不多:
.container {
align-items: flex-start | flex-end | center | baseline | stretch;
}
- flex-start: items 紧贴在辅轴的
cross-start
。 - flex-end:items 紧贴在辅轴的
cross-end
。 - center:items 在辅轴上,居中排列。
- baseline:items 根据内容的 baseline 对齐排列。
- stretch:items 充满整个辅轴。具体排布如下图:
align-content
该标签是用来定义多行情况下,行元素整体的排布。(如果只是一行,就没必要了。)具体属性有:
.container {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
- flex-start:多行元素紧贴着
main-start
排布 - flex-end:多行元素紧贴着
main-end
排布 - center:多行元素在辅轴上,居中排布
- space-between:多行元素根据空隙,在辅轴上均匀排布,并且,首尾贴边
- space-around:每行元素两端会自动带上一份空隙,不过,首尾两端只有一份,其余有两份(是两行空隙之和)
- stretch:每行元素均匀充满整个主轴,默认值。具体排布见图:
接下来,我们来看一下 items 相关的 flexbox 属性。
items 属性
在 items 设置相关的属性,是直接在子元素中设置,而不是在父容器中。只要是父容器下的第一级子元素都被称作为 items。接下来,我们就来看一下,与之相关的标签属性。
order
默认情况下 items 是 HTML 中的顺序来排布的,不过,你可以通过 order
标签来手动更改他们的顺序。数值越大,排列的位置约靠后
// 有 3 个元素。item_1 排第一个
.item_1 {
order: 2;
}
// 当设置 order 之后 item_1 变为最后一个
flex-grow
该标签从来设置子元素自身可以占据多大的空隙,接受的值只能为 >=0 的整数,默认为 0。例如:
.item_1 {
flex-grow: 1;
}
.item_2 {
flex-grow: 1;
}
此时,会将空隙平均分为两份,item_1 和 item_2 各占一份。如果这样:
.item_1 {
flex-grow: 1;
}
.item_2 {
flex-grow: 2;
}
那么,空隙一共会分为 3 分,item_1 占 1 份,item_2 占两份。
flex-shrink
该标签是用来定义,如果在行内时,有元素超出容器的宽度,那么其它元素会相应的自适应缩小。默认情况下,所有的子元素的 shrink 都为 1:
flex-shrink:1
如果你将 item_1 设置为 2,则超出的部分会被多分 1 份到 item_1 上进行缩小。
flex-basis
设置在没有空隙分配时的默认尺寸。如果有空隙出现,并且,你没有设置 flex-grow
,那么默认是不会分配空隙到该元素的 content
中。如果你设置了 flex-grow
,那么优先当其他元素已经达到使用 width
或者 height
设置的尺寸时,才会给其分配相应的空隙。 在弹性盒子中,width
和 height
有效性是在有多余空间,并且处于默认 item 样式(即,没 grow/shrink)时。大部分情况,如果你想设置固定大小的盒子,需要将 grow 和 shrink 设置为 0.
.fix-item{
flex-basis:200px;
flex-grow:0;
flex-shrink:0;
}
该标签有两个取值类型:
.item {
flex-basis: <length> | auto; /* default auto */
}
auto
表示按默认情况来,即,遵循缩放,多余的空间会分配到 content
中去。
flex
该标签也是一个综合标签,flex-shrink 和 flex-basis 是可选的。
.item {
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}
默认值为:
.item {
flex: 0 1 auto;
}
align-self
单独设置 item 在辅轴上分布的位置。取值和 align-items
一样。
.item {
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
例如:
另外,float, clear 和 vertical-align 这几个标签,对 items 无效,这点需要牢记。
兼容
flex 现在的兼容性已经很棒了,所有的主流平台都会兼容,不过,如果你还有针对 Android 4.3 以前的版本,则需要加上相关的 prefix。大致的 prefix,如下:
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
不过,最好还是采用编译的形式,有时候就不用自己手动加了。
rem vh vw 响应式
在响应式开发中,一般提倡使用的是灵活单位而非固定的 px
值。近年来比较好用的就是 rem
单位。另外,还有其他的单位,例如:em
,vh
,vw
等。这里,集中说明一下:
- em:准确的说是相对于父节点的字号来计算的,如果自身定义了字号那么就相对自身字号来计算(针对其他属性使用
em
单位)。例如:
.parent{
font-size:20px;
}
.child{
font-size:2em; // 40px
padding:2em; // 80px
}
- vh/vw:这两个单位实际上是相当于
viewport
来定义的。vh 相当于 1% 的viewport
高度。vw 相当于 1% 的viewport
宽度。这就可以让我们脱离父元素宽度的限制了。常常用于整块布局,而它的兼容性也挺好的,Android 4.4 都支持。 - vmin/vmax:这两个单位和上面的区别在于,它们不代表某个具体的宽高,而是代表大小,如果宽 > 高则,vmax = 1% 宽,vmin = 1% 高,反之依然。他们的兼容性和 vh/vw 一样,也是在 Android 4.4 以上可用。这两个属性看起来挺鸡肋的,不过实际上用处很多。例如,实现背景图的
container
和cover
的效果。
// 实现 container
.bg{
width:100vmin;
height:100vmin;
}
如图:
// 实现 cover 效果
.bg{
width: 100vmax;
height: 100vmax;
}
如图:
- rem:该单位是相对于根元素
html
的大小。该属性值用处比较大,常常用在 retina 兼容上。
// 根据根元素的 font-size 设置
html{
font-size:14px;
}
.some-node{
font-size:2rem; // 28px
}
在 retina 兼容屏上,常常会根据 dpr 的大小来手动调整根元素的 font-size,来对相关的屏幕进行兼容。
// iphone6 的基准值
@media (min-width : 375px) and (max-width : 667px) and (resolution : 2dppx){
html{font-size: 37.5px;}
}
//iphone5 的基准值
@media (min-width : 320px) and (max-width : 568px) and (resolution : 2dppx){
html{font-size: 32px;}
}
大致的属性使用就是上面几类,大家有空的时候多多写一下,就能铭记于心了。