图片适配不同分辨率设备的思路

标题是什么意思?

不同设备拥有不同的分辨率,不同的窗口也有不同的大小。因此衍生出来两个前端工程师需要解决的问题:

  • 在Retina视网膜分辨率的设备上呈现Retina分辨率的高清图片;
  • 在不同分辨率的终端上应用不同分辨率的图片,节省服务器带宽和用户流量费用。

本站适配了Retina吗,核心问题是?

  • iNoodle.cc已经完成Retina全站适配(2015-11-20),全面兼容苹果和其他品牌的Retina高分辨率设备;
  • 目前的问题:由于是偏重图片展示,正文图片默认设备访问的尺寸都是中尺寸(1500px),但是手机访问时也会加载一样大的文件,而这个加载过程是很难控制的,这样机会产生不必要的流量;
  • 目前的另一个问题:更大尺寸设备访问本网站的时候,依然展示的是中尺寸的默认图片,而默认展示的图片的尺寸相对于这些设备来说太小,典型的情形是,一个5K iMac用Safari全屏浏览本站。
  • 难以解决的原因是CMS对于文章内容的调用是仅仅通过一个调用函数进行的,不能自定义;
  • 以下是本站@2560 x 1600分辨率的截图

screenshot20151121

别人适配视网膜的思路A

使用JS代码,在网页中嵌入JS代码以检测设备的类型、分辨率(DPI)和像素密度(PPI),然后对网页中图片的链接后缀进行修改,比如,将”.jpeg”修改为”@2x.jpeg”,完成高分辨率图片的加载。

优点:

  • 代码量比较小;
  • 方法相对来说比较简单,维护方便;

缺点:

  • 通常会用Jquery库,加载这个库需要时间,图片的加载会滞后;
  • 方法原理是通过修改图片链接完成高分辨率适配,因此在链接被修改之前浏览器会执行加载图片的动作,即同一处的图片,浏览器即会加载普通分辨率的片、也会加载高分辨率图片,带宽更加浪费;
  • 对于本站这样使用infinite-scrolling的网站来说,图片很有可能是动态加载的,这种方法的适应性不强;
  • 不能直接对Css进行操作,修改背景图片的大小的时候就会比较困难;

别人适配视网膜的思路B

使用Css的Media Query来为不同位置的图片加载高分辨率的图片,优点是不用加载两次图片,对现代设备和现代浏览器的支持比较好,该方法的典型代码:

device-pixel-ratio,-o-min-device-pixel-ratio,min--moz-device-pixel-ratio,-Webkit-min-device-pixel-ratio { …   }

优点:

  • 只需要加载一次图片;
  • 在现代浏览器中有比较好的支持;
  • 不依赖JS,速度最快,并且没有闪烁现象;

缺点:

  • 只能修改块(如Div)中的图片的背景,不能修改作为<img>插入到文中的图片的分辨率,而本站的大部分需要处理的图片都说不是块的背景。
  • 对于每个需要优化的图片,都需要单独写一条Css,并且这个Css还比较长,稍微麻烦

别人适配视网膜的思路C

默认使用一张Retina分辨率的图片,然后使用Css中的background-size属性控制图片呈现的分辨率,达成Retina适配的效果。这种方法同时适配普通屏幕和Retina屏幕,在两种屏幕上呈现的图片的大小是一样的,在Retina设备上具有Retina等级的清晰度。

优点:

  • 同时支持普通屏幕和Retina屏幕,大小一致;
  • 只需要上传一张图片即可,不用重复处理;

缺点:

  • 默认加载Retina高分辨率的图,对于手机这样的设备造成更大压力;
  • 每一站图片都需要设置Css的background-size属性,这样的方法可用性并不高;
  • 通过这种方法呈现的图片难以创建流体布局的网站,即不能随窗口大小改变图片大小;

别人适配视网膜的思路D

默认使用一张Retina分辨率的图片,使用<img>标签插入文章,在<img>标签中增加Height和Weight的属性规定,强制其在非Retina设备和Retina设备中呈现正常大小。

优点:

  • 非常简单,只需要HTML代码即可;
  • 对不同浏览器的支持性非常好,甚至支持到IE6;

缺点

  • 默认加载Retina高分辨率图,占用资源;
  • 每张图片均需要设定Height和Width的属性,一方面麻烦,另一方面,需要预先知道图片的大小的值,比较不可测;
  • 使用JS可以配合此方法,先检测图片的长宽值,然后自动设置Height和Width的属性,这样做基本上具有比较好的灵活性,但是在网速比较快的时候,用户就会看到一张网页上比较大的图片突然被自动缩小到1/4大小,如果图片是放在一定固定的排版中的,看起来就会非常乱,这种体验是不太好的。

别人适配视网膜、多分辨率的思路E

通过url或ajax请求形式将devicePixelRatio大小通知后台,然后服务器后端将正确大小的图片返回给用户,这种方法非常明显的优点是能够节省比较多的带宽,对设备的适应性也是最强的。

优点:

  • 这种方法就是adaptive image的概念,不依赖Css、JS,对不同设备的支持性是最好的,兼容IE6;
  • 可以返回很多种分辨率的图片,对于某些特殊情形,比如,Retina 5K iMac达成满屏需要加载的图片是Retina MacBook Pro需要加载的图片的4倍大小 ,采用这种方法可以分别对iMac和RMBP提供支持,对相应的设备提供不同的图片,在最大限度满足设备图片尺寸需求的同时节省服务器端的带宽;
  • 服务器后端具有比较大的控制权,如果有尺寸的修改或者新增尺寸(比如,5K、8K这样的分辨率),维护的时候往往只需要修改数据库数值即可,维护比较方便;

缺点:

  • 对服务器的相应性能提出了更高的要求。一个原本500ms加载完成的网页现在需要800ms甚至1000ms才能加载完了,碰到网络不太好的时候,可能就更加慢;
  • 在Retina的终端设备上,服务器发送Retina图片给设备,设备仍然需要重新设置图片呈现的视觉分辨率的大小,这样也就会出现类似D方法中出现的图片突然缩放产生的缩放现象。这个Bug可以通过服务器端语言设置解决。

本站区分终端分辨率加载图片方案

采用B方案中的Css device-pixel-ratio方法,对<img>标签中的图片进行操作。上文分析中,B方案是不能对<img>进行操作的,那么应该怎样实现呢,现设想如下:

  • 首先,在服务器端修改页面PHP源码,找到引用文章内容的循环中的图片引用部分,删除引用图片的<img>标签;
  • 然后,在原<img>创建一个<div></div>,作为原来图片显示的容器;
  • 随后,在<div>标签中写入样式,当然要写入流体布局样式让<div>充满上级容器的宽度;
  • 最后,设置media query和device-pixel-ratio,由此来设置<div>的背景图片,一次引用原图片的所有分辨率的图片的链接,写入到device-pixel-ratio的代码段中,这样就完成了。

优点:

  • 对B方案所不能操作的<img>标签进行变通后从而达到同样的效果,拥有B方案的所有优点;
  • 不依赖JS,显示速度快,无闪烁;
  • 对于手机等小屏幕终端也可以通过media query设置对应图片分辨率,对移动流量友好;
  • 可以设置多种分辨率加载的图片,理论上说,任何分辨率都可以,对iMac 5K这样分辨率超高的终端可以显示足够充满屏幕的图片;

缺点:

  • 代码比较难写;
  • 服务器端需要在第一次上传图片的时候就生成多种分辨率,占用处理器也占用储存空间。
  • 原图的链接已经明文写出来了,容易被盗图,是一个容易引起流量攻击的隐患。