媒体查询即Media Queries,它是实现响应式的核心。我们会在本文中涉及到那些媒体查询最常用的地方,例如它允许我们在某个窗口宽度(或高度)应用不同的css样式,可以理解为窗口尺寸的断点。此时网页似乎完全换了一种布局,就像变脸一样。另外,我们还可以用媒体查询来判断当前设备类型(如打印机)、设备显示器分辨率等媒体特性。

一、引入媒体查询

在页面开发中,我们常用以下两种方法引入媒体查询:

1. link引入

<link rel="stylesheet" media="screen and (max-width: 720px)" href="small.css" />

可以用link方式将样式文件按断点或者是媒体类型分割。比如我们可以把专用于打印的样式单独放到一个css文件里面,如此一来只需要将上面代码做media=”print”的更改就可以在页面被打印的时候应用此样式文件里的样式。但是,值得注意的一点是:link标签中的样式文件始终会被加载。对,它不会根据当前media中的判断条件做动态加载,而是会无视这些条件而直接把css文件加载进来,然后在满足条件的情况下应用相应的已经加载好的样式。

2. @media引入

@media screen and (max-width: 720px) {
    .someclass{
        display: none;
    }
}

上面的代码是直接写在某个css文件中的,当引入这个css文件的页面在满足@media后面的条件时,就会应用@media花括号中的样式。

此外我们还可以使用@import语句来引入,如下:

@import url(print.css) print;

二、媒体特性

虽然我们可以查询多个不同的媒体特性(你可以在这里查看到较为完整的媒体特性列表),但响应式网页设计最常使用的特性有以下几个:

  • min-width当浏览器宽度大于查询中定义的值时适用的规则。
  • max-width当浏览器宽度小于查询中定义的值时适用的规则。
  • min-height当浏览器高度大于查询中定义的值时适用的规则。
  • max-height当浏览器高度小于查询中定义的值时适用的规则。
  • orientation=portrait : 高度大于或等于宽度的浏览器适用的规则,即手机竖屏时适用的规则。
  • orientation=landscape : 宽度大于高度的浏览器适用的规则,即手机横屏时适用的规则。

此外,我们还要说下device-pixel-radio这个媒体特性,它常被用来处理高清屏幕导致的图像模糊的问题。从字面意思可以理解出它指的是“设备像素比例”,这个比例就是指一个CSS像素在渲染到物理设备上显示的时候会乘以的那个比例系数,典型的比例系数有1x、2x和3x。就拿iphone6来说:

Screen Width = 375px /*CSS 中的像素值*/
Screen Height = 667px /*CSS 中的像素值*/

Screen Width = 750px /*实际设备的像素值*/
Screen Height = 1334px /*实际设备的像素值*/

因此我们不难得出device-pixel-ratio为2,也就是说在这样的retina屏幕上1个CSS像素是用4个物理像素来渲染的。就如同一个CSS像素被放大了4倍(每边乘以系数device-pixel-radio:2)来显示:
device-pixel-radio
这样就不难理解为什么我们之前在PC或者其他低分辨率手机上能清晰显示的图片到了retina等高清屏下会发生模糊的问题了。那为了解决这个问题我们就要检测当前设备使用的是不是高清屏,下面是我曾在australia.cn中用过的一段代码:

@media only screen and (-webkit-min-device-pixel-ratio: 2),only screen and (min--moz-device-pixel-ratio: 2),only screen and (-o-min-device-pixel-ratio: 2/1),only screen and (min-device-pixel-ratio: 2) {
    .mob_nav-search-btn{
        background-image:url(images/dest/search-butt@2x.png);
        background-size:28px 28px;
    }
}

device-pixel-radio并不是一个标准的媒体特性,因此需要加上浏览器的前缀。从上面的代码中我们可以看出,我们不仅要为高清屏替换两倍尺寸的大图,还要结合background-size属性让图片缩小回CSS像素下正常显示(可参考《响应式与移动端(二):了不起的百分比》中 3.2 背景图片 )。对于background-size你可以设置具体的像素值,也可以根据比例系数设置成百分比,例如50%。

此外,上面的代码中我们看到媒体查询的条件语句中出现在了only、and等逻辑操作符,下面我们就了解下我们可以使用哪些逻辑操作符来把这些媒体属性组装成一条媒体查询。

三、逻辑操作符

操作符 not,and 和 only等可以用来构建复杂的媒体查询。

1、and 操作符用来把多个媒体属性合并到同一条媒体查询中。只有当每个属性都为真时,这条查询的结果才为真,相当于“且”。

@media only screen
and (min-width : 320px)
and (max-width : 480px){ /* 样式代码 */}

and操作符不仅可以组合媒体特性也可以组合媒体类型,如上面代码中的only screen。

2、only 操作符表示仅在媒体查询匹配成功的情况下应用指定样式,但必须为其指定媒体类型。

<link rel="stylesheet" media="only screen and (color)" href="example.css" />

最常用的可能就是对screen或print媒体类型的检测了。此外,我们可以用上面的语句作为一种hack的方式防止样式应用给不支持媒体特性查询的古董浏览器。

3、逗号(,)操作符可以将多个媒体查询以逗号分隔放在一起;只要其中任何一个为真,整个媒体语句就返回真。相当于 “或”。

@media only screen 
and (-webkit-min-device-pixel-ratio: 2),
only screen and (min--moz-device-pixel-ratio: 2),
only screen and (-o-min-device-pixel-ratio: 2/1),
only screen and (min-device-pixel-ratio: 2) {/* 样式代码 */}

其实我们在上面检测高清屏的时候就已经用过逗号操作符了,这条查询是为了兼容不同的浏览器的。

4、not 操作符用来对一条媒体查询的结果进行取反,相当于“非”,但必须为其指定媒体类型。

@media not print and (color){/* 样式代码 */}

用来排除某种特定的媒体类型,注意上面的查询not是作用于整个查询语句的,也就是说上面的代码等价于:

@media not (print and (color)){/* 样式代码 */}

四、要在哪里变脸

其实我们这里要说的是——如何选择断点。你可以在很多网站上找到能检测到各种设备的媒体查询,例如Media Query Snippets。但断点的设置有一个宗旨,那就是内容优先。也就是说,不要完全以检测各种设备为设置断点的依据,这样无疑会增加后期维护的成本,而是要根据页面内容来确定版式如何调整。同时兼顾几个常见断点,比如320px或360px、480px、640px、720px或768px等。

对于断点的方向,我们可以是从小屏幕扩展到大屏幕(即移动优先),也可以是从大屏幕收缩到小屏幕。因为我们的媒体查询常结合流动布局一起使用,所以在扩展(或收缩)至不同尺寸屏幕的过程中,当内容的展示必须添加新断点时,才应该适时的加入断点。这样的话我们就可以根据内容优化断点,并有效降低断点数量。让我们来看一个示例:

pc2mobile

让我们简单来看下图中布局的html结构:

<div class="list-wrap">
    <ul>
        <li>
            <div><img src="http://img0.bdstatic.com/img/image/shouye/systsy-11973574263.jpg" alt=""></div>
        </li>

        ......

        <li>
            <div><img src="http://img0.bdstatic.com/img/image/shouye/systsy-11973574263.jpg" alt=""></div>
        </li>
    </ul>
</div>

上图中的页面是从大屏幕收缩到小屏幕,通俗来讲就是从PC到Mobile。CSS核心代码如下:

.list-wrap ul li{
    width: 25%;
}

......

@media screen and (max-width: 640px){
    .list-wrap ul li{
        width: 50%;
}
@media screen and (max-width: 360px){
    .list-wrap ul li{
        width: 100%;
}

从上面的CSS代码中我们看出,我们使用max-width创建了两个断点,此时媒体查询的条件为当屏幕小于等于某一宽度时使用的样式。顺着示例图中的箭头方向,默认情况下页面一行显示4张图片,在640px宽度下变成一行2张,最终在360px时变成一行1张。我们使用了流动布局,用<li>元素来控制列数,每个断点只需要改变<li>元素的宽度,就能达到改变列数的效果,相当方便。你可以在jsbin上查看上面示例的完整代码。

此外,断点也可以是从Mobile变向PC的,也就是倍受推崇的移动优先。逆着示例图中的箭头方向,默认情况下页面显示一行只有一张图片,随着浏览器尺寸变宽,每行显示的图片数量会根据断点设置增多。让我们来看一下CSS代码的变化:

.list-wrap ul li{
    width: 100%;
}

......

@media screen and (min-width: 360px){
    .list-wrap ul li{
        width: 50%;
}
@media screen and (min-width: 640px){
    .list-wrap ul li{
        width: 25%;
}

注意到上面的代码我们把max-width变成了min-width,那么媒体查询的条件为当屏幕大于某一宽度时使用的样式。另外也调整了断点的顺序。这是因为断点中的样式也是遵循CSS权重规则的。你可以在jsbin上查看移动优先的完整代码。

流动布局结合媒体查询的响应式网站基本可以满足各种分辨率了,当然也会有一些过度显得不那么尽如人意,比如字体的大小,单行文本的长度等。因此,必要时要加入一些小断点来做些调整,但始终还是根据内容来的。

此外,随着你接触响应式的布局越来越多,你会在布局的过程中甚至是布局之前就能够预测到之后的布局的变化趋势。你会提前考虑到HTML和CSS代码上如何编写才能更有利于后面页面内容版式的变化。

五、响应式图片

需要注意的一点是,虽然我自己在项目中会把用于PC端的大图不做任何改变直接在Mobile端的小屏幕中使用(只是用百分比来控制下显示尺寸而真正的体积还是原来那么大,这样会增请求数据量),但是并不推荐将这种做法用于实践中。解决方案有两类:

1、一类就是用代码自己处理图片。这个是支持度较为完整的一个解决方案,就是根据当前分辨率让后端返回适当尺寸的img,可以把屏幕分辨率、DPI等设备信息写到cookie里,后端根据cookie中的设备信息直接在img的src发起请求时返回合理尺寸的图片文件(现在也有一些网站提供此类线上服务)。这种做法同样可以在输出HTML代码上做优化,比如我们最好不要在小屏幕时把显示不下的内容直接display:none掉,而是应该让后端动态输出。此外,还有一种办法就是根据设备信息用js动态改变img的src的值。当然要么结合后端,要么就是你在服务器上已经准备好不同尺寸的静态图片。网上有一个这样的jQuery插件Responsive-Img

2、那么第二类解决方案我们可以把它们归为标准类。就是依仗HTML5或者CSS3的标准来实现响应式图片的需求。具体有心下三种:

backgroud的image-set属性值:

background-image: url(assets/test.png); 
background-image: -webkit-image-set(url(assets/test.png) 1x,
        url(assets/test@2x.png) 2x);
background-image: -moz-image-set(url(assets/test.png) 1x,
        url(assets/test@2x.png) 2x);
background-image: -o-image-set(url(assets/test.png) 1x,
        url(assets/test@2x.png) 2x);
background-image: -ms-image-set(url(assets/test.png) 1x,
        url(assets/test@2x.png) 2x);

如果你想用背景图片来代替img标签的话,还需要在样式上做些处理,我们之前有在《响应式与移动端(二):了不起的百分比》中提到过padding占位方法。你可以从Can I Use上查看image-set目前的浏览器支持情况。

img元素的srcset属性:

<img src="test.png"
    srcset="test.png 1x, 
    test@2x.png 2x" />

你可以从Can I Use上查看srcset目前的浏览器支持情况。

<picture>元素:

<picture alt="">
    <source src=test_2.png media="min-width:800px">
    <source src=test_1.png media="min-width:480px">
    <source src=test.png>
    <!-- 不支持的浏览器降级处理 -->
    <img src=test.png alt="">
</picture>

你可以从Can I Use上查看<picture>目前的浏览器支持情况。

相比之下,浏览器对响应式图片标准的支持程度和我们上面罗列的顺序相符,即由高到低。

六、题外话

这篇博客距离我上一篇已有7个月的时间。这半年多的时间里一半是在无尽的加班中度过,几乎天天都是凌晨3、4点才回家。另一半时间就是离职后的休息,几乎是在脑死的状态下度过的。以前我一直把身体当成灵魂的工具,不加爱护,直到我在无尽的疲惫以及对工作无由的抗拒中实在无法坚挺,把自己的身体变成一副眼窝深陷,形容憔悴的皮囊后,才知道自己必须要停一停了。只因为每晚都等我回家的这位爱人,开始担心自己万一哪天挂了,她怎么办?我答应过要比她活得久些。

 

 


响应式与移动端系列:
响应式与移动端(一):万事俱备了吗?
响应式与移动端(二):了不起的百分比
响应式与移动端(三):弹力十足的图文混排
响应式与移动端(四):会变脸的媒体查询
响应式与移动端(五):交互的过渡