手记

响应式开发之图片响应式实战


说到响应式网站,我们都知道弹性布局、弹性图片、媒体查询,更多指的是布局的方式,比如说使用max-width: 100%,这样让图片的宽度随着容器的大小而改变,响应式设计让网站能兼容各种屏幕设备,但是在我们流量这么昂贵的时代,让一个小屏幕的手机去加载一张为大屏幕PC设计的几百K的图片,虽然这张图片会看起来非常的清晰,但是疯涨的流量消耗肯定会让用户非常的苦恼,而且我们也需要加载更长的时间才能把页面加载出来。

我们开发的目的不是挑战用户的耐心和金钱,而是让用户在使用的过程中有更好的感受,在这种情况下加载与 用户设备相匹配的小图片,即快速,又不会影响用户的体验,帮用户节省了成本,同样的,你在PC端加载一张小图片也不会影响用户的体验,帮用户节省了成本,同样的呢,你在PC端加载一张小图片,虽然速度很快但是放大后呢,模糊的效果会让用户觉得网站很Low很不专业,而且呢,PC大部分使用的wifi,我们不需要去接受太多的流量,那么如何解决刚才说的这些问题呢?响应式图片的概念也就随之产生了,响应式图片,不仅仅是指图片的排版和布局,更多的还指可以根据设备大小而加载不同的图片,虽然这个过程增加了一点点UI设计师的工作量,不过那会大大改善用户的体验,那么想要响应式图片如何来实现呢?

第一种,JS或者服务端硬编码

这种方式的原理其实就是跟踪window的resize事件,然后修改一下图片的路径,我们准备三张图片,1-480.png、1-800.png、1-1600.png,分别对应三种大小设备屏幕,然后:

$(function(){
    function makeImageResponsive() {
        var width = $(window).width();
        var img = $('.container img');
        var imgUrlPrefix = 'http://sandbox.runjs.cn/uploads/rs/496/pkutja85/';
        if (width <= 480) {
            img.attr('src',imgUrlPrefix + '1-480.png');
        } else if (width <= 800) {
            img.attr('src',imgUrlPrefix + '1-800.png');
        } else {
            img.attr('src',imgUrlPrefix + '1-1600.png');
        }
    }

    $(window).on('resize load', makeImageResponsive);
});

在线查看源码

这种形式还有一种变种的方式, 就是把屏幕信息写入Cookie中,取图片的时候在服务器端决定返回哪一张图片,这样的话就不需要我们来写脚本了,我们通过Cookie和在服务器端进行控制就可以达到目的,兼容性也比较好。

第二种,srcset 方法

通过js或者服务器端返回确实可以达到响应式图片这样一个目的,兼容性也非常的好,但是需要我们编码实现响应式的逻辑,在修改的时候也不是很方便,这属于命令式的实现,那么有没有声明式的实现呢?也就是说我们把这几个图片地址声明在HTML中,由浏览器来决定如何加载,这样的话更加的灵活,把展示的逻辑从js脚本或者从服务器端硬编码分离出来,这样也降低的耦合,因为展现和我们的业务逻辑是分开的。

那么具体有没有呢?答案是有的。

经过了多年的努力和争辩,我们现在有了一些符合标准的标记,这些标记其实都经过了很多的修改、讨论,最后决定了这样几种,这些标记是来解决响应式图片的问题的。这些标记如下:

  • srcset
  • srcset 配合 sizes
  • picture
  • svg

这些新元素和新属性让我们可以编写多个可替换的图片,让每个客户端使用最适合的那一个,这些已经进入了官方的规范,下面我们分别来使用这些标记。

我们新建一个如下的页面:
HTML:

<div class="content">
    <img class="image" src="http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-480.png" alt="这里是图片" />
</div>

CSS:

html, * {
    margin: 0;
    padding: 0;
}
.content {
    width: 100%;
    marign: 0 auto;
}
.content img {
    display: block;
    width: 100%;
    max-width: 100%;
}

这个页面很简单,就是载入了一副480x480的图像

srcsetimg标签中的一个属性,这个属性里面是可以以逗号隔开的一个或者多个字符串列表,这些字符串用来代表一系列可能使用的图片,每个字符串的格式如下:

<img class="iamge" src="img/1-480.png" 
      srcset="img/1-480.png 480w, img/1-800.png 800w, img/1-1600.png 1600w" />

480w800w1600w就是尺寸描述符,一般是w结尾,也就是width的意思,以上声明了三张不同规格的图片,对于支持srcset的浏览器会怎么做呢?

在线预览查看源码

从效果上看浏览器会自动感知,这个感知包含一系列因素如屏幕大小、网速、dpr等,然后去选中一张合适的图片进行加载。我们先把界面变小,如下面所示:

我们可以看到,当我们加载到最大的图片后,然后再把屏幕缩小后,加载的图片却并没有改变,这是什么样的一个逻辑 呢?

这个逻辑就是:浏览器会根据屏幕的宽度和我们声明的图片的尺寸描述符去决定应该使用哪张图片,但是呢,因为我们浏览器已经在大的分辨率下加载了大的图片,那么浏览器会默认你的图片已经放在缓存中了,使用大的图片不会再有网络的消耗,那么当然是使用更大的图片更好的了,所以在我们再缩小屏幕的时候浏览器不会再去使用小图片了,这是浏览器的一个默认的行为。

第三种,sizes 解决 srcset坑

上面使用srcset让我们的开发变的非常的简单,只需要声明一下将几个图片和它的宽度,就可以让浏览器自动的去选择,那么如果我们只设置srcset的话,会有一个小小的坑,我们试的把这个content它的宽度设置成50%。看一下页面的效果,我还是把dpr设置为1,这样的话他会精确到像素,让我们看得更加的清晰。

设置成50%之后这个图片只是占了50%的宽度来显示,但是当我们去加宽这个宽度的时候,可以看到,当我们到480的时候,图片就会改变,但其实这个图片真实的宽度只有480/2,也就是只有240,只在240的时候这个宽度就改变了,这时候我们就会会发现这个图片,它的这个自动的加载变得不是那么智能了,那么我们图片的容器变小,但是图片还是按照百分之百的宽度逻辑进行缩放,这是什么样什么样一个原因呢?这是因为呢其实,srcset,这一个属性还需要配合一个属性来使用,就是刚才说到的sizes

<div class="content">
    <img class="image" src="http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-480.png" alt="这里是图片" 
            srcset="http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-480.png 480w,
                        http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-800.png 800w,
                        http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-1600.png 1600w" 
            sizes="100vw"/>
</div>

刚才我们没有设置设置这个sizes属性,其实,这个属性还是被设置了,它被设置成什么呢?它被设置成了一个默认的值,sizes属性,如果我们不去设置的话它的默认值是100vw,这里的vw代表视口的宽度,v就是viewportw就是widthvw是一个代表视口宽度的单位。vw就是这样一个缩写,那么同样的还有一系列这样的宽度,比如说vh,代表视口高度,这是和视口相关的一个相对的单位,有点类似于我们的em之类的相对单位,只是这个单位它的相对值是视口,这里的100不是代表100个,而是代表百分比100%,这里设置成100vw就代表图片的尺寸就是百分之百的视口的宽度,sizes属性他是告诉浏览器以什么样的尺寸来显示图片,如果不写的话默认是100vw的视口宽度,那么就是告诉浏览器在这里不管图片外边是怎么样去包围它的,图片实际的宽度是多少,它的参考值就是百分之百的视口的宽度尺寸,所以说在这里边我们不管图片外面的容器的宽度是多少,图片本身它的宽度是多少这个百分之百是宽度来去选择我们这里面声明的三张图片,一般来说这个选择的逻辑不是我们需要的,因为在各个分辨率下图片的宽度其实都是有可能不同的,比如说在这里我们content的宽度设置成50%,那图片就变小了,图片宽度变小怎么办呢?我们就可以来设置这个sizes属性,怎么样来设置呢?

sizes和这个srcset有点类似,他都是以逗号隔开的一个或者多个字符串,每个字符串里面的第一个值可以包含一个媒体条件,也就是媒体查询,第二个值代表图片他的一个预估的尺寸,也就是说,在这种媒体查询条件下,这个图片预估的尺寸是多少,注意这两个值是空格分开的,在这里我们先写一个五50vw看一下效果,50vw相当于图片的尺寸也就是我们预估的尺寸,因为第一个条件媒体查询是可以省略的,省略了媒体查询,只写一个50vw就是代表所有条件下这个图片的宽度预估都是50vw,也就是50%的视口宽度。

sizes属性里的单位还可以是px等其他任何的单位,在简单的情况下,比如说图片的宽度一直100%或者某个值,我们只需要给一个给定的数值就行了,比如50vw。但是在比较复杂的排版的情况下,这个sizes就可以起到比较大的作用,比如有一张图片,它的视口宽度超过800px的时候我们要求它的宽度最大是800px宽,如果视口的宽度小于800px的时候我们要求它的宽度是100%的视口宽度。那我们就可以这样写:

<img class="image" src="http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-480.png" alt="这里是图片" 
            srcset="http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-480.png 480w,
                        http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-800.png 800w,
                        http://sandbox.runjs.cn/uploads/rs/496/pkutja85/1-1600.png 1600w" 
            sizes="(min-width:800px) 800px, 100vw"/>
第四种,picture标签

一般来说对于仅仅是需要缩放的图片,我们在srcset属性里面列出图片的地址啊,像素宽度啊,用sizes属性来告诉浏览器这个图片在什么样的情况下它的宽度显示成怎么样,写出这些就足够了,但是有的时候我们会希望除了缩放之外还通过其他的方式进行图片的自适应,这个时候我们就要夺回一些我们选择图片的一个控制权,所谓的夺回控制权就是说浏览器的选择逻辑我们要把它拿过来一些,我们自己来控制,那么picture属性就该出场了,比如我们有一个天安门的全景图,宽宽的一个图片:

他在大屏幕下显示非常的效果非常的好,但是在小屏幕下这个全景图显示就显得太小了,如果我们可以在手机上能放大,能展现出来一个更突出的一个裁切后的全景图那样就更好了,那么如果我们在一个srcset中的引入一个裁切后的版本,那么我们就无法分辨他们何时应该去选用何时不应该去选用,而使用了picture我们就能明确的表达出我们的意图,比如说我们只在视口宽度超过800像素的时候加载宽的版本,比这个小的视口中我们总是加载裁切后的版本。代码如下:

<div class="content">
    <picture>
        <source media="(max-width:36em)"
                srcset="http://www.fansimg.com/uploads2011/05/userid280713time20110508081514.jpg 768w"/>
        <source srcset="http://www.fansimg.com/uploads2011/05/userid280713time20110508081514.jpg 1800w"/>
        <img src="http://www.fansimg.com/uploads2011/05/userid280713time20110508081514.jpg" alt="这里是图片"/>
    </picture>
</div>


在线演示源码

source中除了可以有媒体查询之外,还可以正对媒体查询设置多组不同格式的文件,比如source中还有type属性,可以是“image/svg+xml”等格式文件:

<source type="image/svg+xml" srcset="logo.svg 480w, logo.svg 800w, logo.svg 1600w "/>
<source type="image/webp" srcset="logo.webp 480w, logo-m.webp 800w, logo-l.webp 1600w "/>
第五种,svg

svg是可缩放的矢量图形,它是基于可扩展标记语言来生成的,svg图像可以用任何的文本编辑器来创建,但是一般人很少会去用文本编辑器来创建svg图片,因为这个非常的麻烦,除非你就只需要几条横线几个圆圈什么的。

什么是矢量图形呢?矢量图形就是这些图形,在说话的时候它都不会失真和变形,这些图形它不是基于像素的,而是基于一个绘制的规则,一旦我们确定了绘制的规则,比如说颜色、形状、轮廓、大小、屏幕位置等等,确定了这些规则,即使我们发生了缩放也不会影响这些图形的绘制,所以这些图像当然也不会发生失真,但是这些矢量图形的和位图比起来其实有缺点的,最大的缺点的就是它很难去表现色彩层次丰富和逼真的图像效果。比如让我们的一个照片变成一个矢量图形,那么这个这个矢量图形肯定要大了去了,要变的非常的非常的大,因为每个像素他可能就要有一个绘制的规则,所以根据我们的需要可以去选择你使用矢量图形还是要位图,一般来说针对一些logo,简单的图标,比如说回收站、消息等等这样一些图标,我们可以使用svg这种矢量图形。

说到矢量图形,肯定就要想怎么样去画它了,刚才说用文本编辑器肯定不行的,这样太麻烦了,那么其实大部分支持矢量图绘制的工具其实都支持绘制svg。

相比srcset和picture,picture对于IE浏览器是完全不兼容的,对于老的Safari浏览器和老的安卓浏览器也是不兼容的,对于srcset,比picture稍微好一点,对于老的IE和安卓浏览器也是不兼容的。而SVG兼容性就好的多了,除了IE8不支持外,其他的浏览器几乎都支持SVG。

使用第三方库

由于我们的广告的一看就是一个位图的图形,它的里边色彩比较丰富,所以我们使用svg不太合适,使用位图要实现响应式就要配合srcset或者picture,由于兼容性的问题一般在使用srcset或者picture的时候一般会使用一个polyfill。

什么是polyfill?它来自于一个家装产品,这个家装产品是一个刮墙的产品,中国人一般把这个产品的叫做腻子,就是刮墙的腻子,它可以把墙的一些裂缝给填平,比如说我们墙皮时间长了会出现一些裂缝,这个腻子可以把它给填平,让砖块变得更平滑,在js的世界里polyfill他的意思就是一个在浏览器上的一个腻子,就是填平浏览器上面的兼容性的一些东西,比如我们使用picture,如果浏览器支持那就没有问题,我们正常的使用就可以了,如果浏览器不支持那么就需要使用一个替代的方案来解决,polyfill就是填平了这些浏览器的兼容性的一些坑。

经常使用解决兼容性的一个polyfill,polyfill其实是一种想法,一种脚本,针对某些浏览器的兼容性就会有某些的polyfill出现,对于响应式图片最有名的polyfill就是picturefill库。

我们只需要引入这个库就行了(picturefill.min.js)

<div class="ad">
    <div class="owl-carousel owl-theme">
        <div class="item">
        <picture>
            <source srcset="img/ad001-l.png" media="(min-width:50em)">
            <source srcset="img/ad001-m.png" media="(min-width:30em)">
            <img src="img/ad001.png" alt="2015年度报告">
        </picture>
        </div>
        <div class="item">
        <picture>
            <source srcset="img/ad002-l.png" media="(min-width:50em)">
            <source srcset="img/ad002-m.png" media="(min-width:30em)">
            <img src="img/ad002.png" alt="新年红包">
        </picture>
        </div>
        <div class="item">
        <picture>
            <source srcset="img/ad003-l.png" media="(min-width:50em)">
            <source srcset="img/ad003-m.png" media="(min-width:30em)">
            <img src="img/ad003.png" alt="新手秘籍">
        </picture>
        </div>
    </div>
</div>

<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="js/vendor/OwlCarousel2/owl.carousel.min.js"></script>
<script src="js/vendor/picturefill.min.js"></script>

在线查看源码

跟多代码及完整响应式网页案例请查到这里查看。

13人推荐
随时随地看视频
慕课网APP