图片响应式解决方案

作者: RichardBillion | 来源:发表于2016-04-28 13:37 被阅读3278次

    背景

    众所周知,retina屏的设备像素比是2,而普通屏幕像素比为1,对于图片这种位图像素已定的资源,如果不加以处理的话,要么全都用1倍图,在retina屏上观看就会模糊;要么都是用2倍图,那么在普通屏幕上浏览的时候就会白白浪费流量消耗下载速度。

    简单介绍下css像素和位图像素的区别:

    • 无论是retina屏还是普通屏幕,图片展示的区域大小是相同的,这也就是css像素与设备无关。
    • retina屏一个1x1的css像素区域对应着2x2的物理像素,也就是每个css像素宽的区域,在retina屏上是可以再分成两半来显示的,即dpr=2的retina屏上的最小css像素分辨率是0.5px。

    理论上,一个位图像素是对应一个物理像素的时候展示完美:

    1、假如在retina屏使用1倍图:也就是1个位图大小的区域去用4个物理像素渲染,被分割的位图只能就近取色,就会导致图片显示模糊;
    2、同样的,如果在普通屏幕上使用2x图,那么就会一个物理像素对应4个位图像素,就会通过一定算法,给该物理像素一个近似的值,(downsampling过程),图片不会模糊,但会缺失一些锐度。

    目前主流方案(使用二倍图):

    img{
        max-width:100%;
        height:auto;
    }
    

    而图片展示的情况在如今也是应用的越来越多,所以要找到两者完美匹配的方法,也就是——响应式图片。

    CSS解决方案——媒体查询

    最大缺点:只能用于css,所以也就限定了只有background中的图片可以使用此方法。
    简介一下:

    @media 
    only screen and (-webkit-min-device-pixel-ratio:2),
    only screen and (-min-moz-device-pixel-ratio:2),//版本低于16的Firefox
    only screen and (min-resolution:2dppx),
    only screen and (min-resolution:192dpi){
    ...
    }
    

    像七牛这样专业的图片处理应用都可以根据需求生成一倍图,这样对于不同分辨率的显示器,也可以使用不同分辨率的图片。另外的不足是使用媒体查询多了不少代码,个中利弊,具体情况下再权衡吧。

    resolution :定义设备的分辨率。


    resolution兼容性

    dppx:也是设备像素比,和dpr一样。
    dpi:(Dots Per Inch)每英寸点数。
    1dppx=96dpi
    【小科普:1参考像素即为从一臂之遥看解析度为96DPI的设备输出即1英寸96点)时,1点(即1/6英寸)的视角。 】

    另外,在最新的以webkit为内核的浏览器中,支持支持CSS4的background-image新规范草案image-set,在移动端也勉强可以接受吧。

    image-set属性兼容性
    selector {
      background-image: url(no-image-set.png);
      background: image-set(url(foo-lowres.png) 1x,url(foo-highres.png) 2x) center;
    }
    

    不支持image-set的浏览器会解析background-image中的背景图像;支持image-set的浏览器就会根据是否为retina屏选择相应的背景图,因此这个方案是可以实现向下兼容的。

    HTML解决方案——srcset+sizes+w标识符

    这是HTML5推出的属性,srcset可以根据显示器分辨率智能加载最佳显示的图片。

    srcset属性兼容性
    <img class="image" src="mm-width-128px.jpg" 
    srcset="mm-width-128px.jpg 128w, mm-width-256px.jpg 256w, mm-width-512px.jpg 512w" 
    sizes="(max-width: 360px) 340px, 128px">
    

    srcset :指向提供的图片资源,为用户提供了一种内嵌简单的分辨率媒体查询功能;

    sizes : 指定图片宽度,不能使用百分比,可使用:
    px,
    vw(100vw就是占满父容器宽度,所以要求图片居中宽度为百分比的地方可以使用vw单位,如 sizes=80vw),
    calc运算(适用于两边距离固定的情况,如sizes="calc(100vw-20px)"),
    媒体查询(如sizes="(min-width:360px) 340px,128px")。

    而且最重要的是,sizes属性产生的初衷就是可以在html中实现简单的媒体查询功能,来适应越来越大规模的响应式网站开发。

    那么w是个啥?
    w是一个衡量宽度的标识符,一定要对应图片的真实宽度,这会使得浏览器正确的选择图片,如果w值和图片宽度不对应时,实际渲染是会有问题的。

    拿这段代码来说:

    <img class="image" src="test-240.jpg"   
        srcset="test-240.jpg 240w, test-480.jpg 480w, test-720.jpg 720w" 
        sizes="240px"> 
    

    sizes=240px,也就是图片宽度设置为了240px,那么:
    当该屏幕dpr==1时,就会选用test-240.jpg;


    dpr==1

    dpr==2时,可渲染的位图像素宽度就变为了480px,也就选用了test-480.jpg;

    dpr==2

    dpr==3时,能渲染的位图像素宽度变为了720px,那么浏览器去选择最适合的图片,也就是test-720.jpg;

    dpr==3

    关于w值设置如果不正确,会出现什么后果,我在这篇文章中写了详细的案例

    现代浏览器对该属性的支持是越来越多了,这个方案应该会是个潮流。
    在移动端andriod browser的支持度实在是太差劲了,PC端对于一些fashion的网址试一试。

    javascript解决方案

    <div class="hisrc">
     <img src="placehold.it/200x100.png" data-1x="placehold.it/400x200.png" data-2x="placehold.it/800x400.png">
    </div>
    

    然后调用hisrc的方法

    $(document).ready(function(){
      $(".hisrc img").hisrc();
    })
    

    官方文档是这样介绍HiSRC如何工作的:正常情况下会直接加载src中的资源;如果网速较好就会加载data-1x中的资源替代原来src的文件;如果设备像素比又比较高的话,就会加载data-2x中的资源代替原来的src中的图片。

    它还提供选项允许我们设置一个网速基准。这个html的结构让我不由得想起了lazyload的解决方案,这俩真的是太相似了,我们完全可以给src中放一个统一的占位图,然后再去选择加载适合浏览器展示的图片。

    另外还有用于rails的gem包:hisrc-rails.
    所以也可以写成这样

    responsive_image_tag("http://placehold.it/100x100", :'1x' => "http://placehold.it/200x200", :'2x' => "http://placehold.it/400x400")
    

    对于这个方案,个人觉得在工程上应用是可以期待的,因为图片都放在七牛那儿,可以图片上传成功后从2倍图中处理出1倍图,然后再向img标签中添加data-1x,data-2x这样的属性,不过呢,这好像把工作几乎全部添加给了后台的哥们儿,想到这儿,好像应用的难度瞬间增大了呢。。。

    • picturefill方案,依赖picturefill.js这个脚本文件,并且它还对结构有一定的要求,对格式有特定的要求,最开始这个来自于对<picture>的支持。
    <picture>
         <source srcset="smaller.jpg, smaller_retina.jpg 2x" media="(max-width: 768px)"> 
         <source srcset="default.jpg, default_retina.jpg 2x"> 
         <img srcset="default.jpg, default_retina.jpg 2x" alt="My default image">
    </picture>
    
    <picture> element兼容性

    看到该结构要写这么多代码,多少就会产生一点心理抗拒,但是呢本着技术进步的态度,还是再进一步研究下。
    由于picture元素是html5的新产物,兼容性上还不是特别好,要想大规模使用可以配合picturefill.js,另外现在picturefill也支持有srcset属性的img。
    这里有picturefill在应用<picture>的页面中存在的一些问题
    //主要是IE9和安卓2.3上存在一些问题,不过IE9通过hack方法也是可以挽救的。

    最后,两句话介绍一下服务端解决方案:
    Adaptive Images:最大缺点基于PHP和Apache。它是拦截通过服务器的图片请求来生成图片,如果图片是通过第三方的分网网络的也就用不上了。

    综上

    不过既然picturefill也支持srcset,那么比较srcset属性和picture元素,浏览器对srcset属性的支持是更好的。所以srcset+sizes+w的img元素配合picturefill.js效果应该会不错。只是很可惜,这样的应用案例还没有找到。不过即使picturefill.js不能完美配合srcset方案,仅仅使用srcset+sizes+w就可以应付主流现代浏览器了,重要的是,这个方案完全也是向下兼容的啊。

    参考:

    http://www.tuicool.com/articles/ZraMfa
    http://www.zhangxinxu.com/wordpress/2014/10/responsive-images-srcset-size-w-descriptor/
    http://www.w3cplus.com/responsive/understanding-responsive-web-design-how-to-manage-images.html
    http://scottjehl.github.io/picturefill
    https://www.ze3kr.com/2015/08/using-srcset/#section-6

    相关文章

      网友评论

      • 牧羊童鞋:为什么没有人去做这么细,个人觉得应该是现在4G的普及和流量越来越便宜,所以直接简单粗暴2x上去,毕竟这样维护成本最小
        RichardBillion:对,无论哪种方案,都需要或者前端或者后端人员多做一些工作,虽然这些其实是一劳永逸的事儿,但是想在工作中纯粹追求技术,仍然不是那么现实。而且确实像你说的,直接2X,简单省事儿,毕竟多数网站也没必要在图片上追求极致。
        牧羊童鞋:文章写得方案都不错:+1:

      本文标题:图片响应式解决方案

      本文链接:https://www.haomeiwen.com/subject/ptwcrttx.html