前言:
在开发中,当我们的网站存在图片尺寸大的问题时,我们会想到用低质量图片占位或者占位符这些方式去规避堵塞空白,从而优化网站的表现,今天来介绍下利用渐进式图片的方式来优化这个问题。
什么是渐进式图片?
普通图片的加载是随着下载图片数据的完成度,逐渐从上至下显示完成的。如果浏览器只收到了图片文件数据的一半,那么它只会显示图片的上半部分。
而渐进式图片的加载流程则是先显示图片整体的一个模糊效果,随着下载数据的增多,逐渐细化图片中的各个细节,使得图片分辨率逐渐提高,最终还原出完整的图像内容。
jpeg渐进加载
原理:
普通jpeg图片是采用从左至右、从上到下的方式逐行进行压缩的,而渐进式jpeg图片的压缩方式则是根据小波变换分层存储的,先存储低频(轮廓)内容,然后再存储高频(细节)内容,这样在拉取图片的过程中,就是一个逐渐清晰的过程。
jpeg结构:
JPEG文件内部分为若干块,这些块由ff开头(截图中已标出部分),ff后紧跟的一个标记符指明了这个块的类型。 这些分块按出现顺序分别是:
JPEG是以ffd8开头,ffd9结尾,ffda代表一个帧的开头
ffd8...ffda...ffda...ffda...ffd9
普通jpeg 里面只有一个ffda
渐进式 jpeg 里面含有多个ffda
区别: 普通jpeg图片是以像素形式存储图片的,而渐进式jpeg是用频率系数来代表图像,这样并不会增加文件的大小,因为这两种渲染方式的图片数据是一致的,只不过渐进式jpeg用了一种更易用的方式排序,就像是一系列预定义的样式,以任意顺序混合都能可以重新构建原图。
png和gif渐进加载
png和gif也有类似于渐进jpeg的视觉加载效果,称为交错GIF、交错PNG。交错式图片会进行 1 到 7 次隔行扫描,每一次都是跳着部分像素点进行扫描的,先扫描到像素点可以先渲染,每多一次扫描,图片就会更清晰,到最后一次扫描时就会扫描完所有像素点,进而渲染出完整的图片。如下图
原理:
adam7隔行扫描算法:本质上是将一张 png 图片拆分成多张 png 小图,然后对这几张 png 小图进行普通的逐行扫描解析,最后将解析出来的像素数据按照一定的规则进行归位即可。
拆图:
我们以一张 10*10 大小的 png 图片来举例,下面每个数字代表一个像素点,数字的值代表这个点在第几次扫描时被扫描到,在第一次扫描时我们会扫描到 4 个像素点,我们把这 4 个像素点单独抽离出来合在一起,就是我们要拆的第一张小图,其他小图依此类推。
归位:
我们按照上面拆图的规则拿到7张小图后,对这些小图的像素进行归位,也就是填回去。
区别:png的逐行扫描和隔行扫描都是以像素形式存储图片的,而隔行扫描因为要进行跳像素扫描,整张图片会存储更多额外数据而导致图片大小会稍微变大。这里的额外数据就是指过滤类型。原本的 png 大图拆成小图后,扫描行的数目就会蹭蹭蹭往上涨,每个扫描行的第一个字节都是用来存储过滤类型的,所以行数增加的越多,额外数据就会越多。
渐进式图片的优缺点
优点:
-
可针对移动网络的场景优化流量,即用户可以只下载渐进式图片的一部分,以达到降低图像分辨率和减少流量的目的。
-
减少等待时间,用户可以先看到图片的轮廓再逐步补充细节
缺点:
-
图片需要转换成为渐进式格式,有一定的成本,因为现有图片大多是普通格式压缩的
-
终端的支持程度不够,现在还有一些落后的浏览器(如IE8)对渐进式格式的支持较弱。不过随着时间推移,这部分浏览器会逐渐被淘汰。
将图片转换成渐进式图片
-
本地图片转换:
jpg 使用 js-mozjpeg,
png 使用 worker 将图片按照 Adam7算法(github仓库)进行拆合来转换
-
网络图片转换:如果是上传到阿里云oss 的图片,可以通过简单的 RESTful 接口,在任何时间、任何地点、任何互联网设备上对图片进行格式转换和显示控制。其他云服务器也有类似的处理。
验证图片是否是渐进式图片
Mac检查图片是否是渐进式图片
首先利用homebrew 安装 imagemagick $ brew install imagemagick
然后输入待检查的图片 $ identify -verbose file.jpg | grep Interlace
如果返回 Interlace: JPEG / PANE(老版本为PANE)图片是渐进式 ,如果返回 Interlace: None 图片不是渐进式
应用:C2C图片传输机制
传输机制流程
- 发送方图片预处理,将其它图片格式转换成渐进式JPEG格式;
- 发送方组织发送的数据流,将发送方UIN、接收方UIN,图片总长度,最小传输单元长度等元信息组织在数据流的最前面,渐进式图片数据组织在数据流的后面。
- 发送方发送数据,中转服务器接收数据,并且不断向发送方确认已接收到的数据长度,同时解析数据流前半部分的元信息,得到收发双方的UIN,图片总长度,以及最小传输单元长度。 含有图片大小的图片头优先级极高,因为浏览器需要尽早知道大小来布局页面,避免多次重排浪费资源,而且图片头很小,早于其他数据发送它没有坏处。
- 当中转服务器收到最小传输单元长度时,认为图片接收成功,将已收到的数据落地,并立刻向发送方返回图片唯一标识字段。
发送方收到该字段后,认为图片发送成功,立刻向用户展示发送成功,然后转到后台继续发送图片剩余数据。发送方用户会体验到发图时间缩小了,发图过程变快了。
- 如果发送方此时网络正常,图片数据将被全部发送完毕。如果发送方此时网络不稳定导致无法发送更多数据,发送过程结束,并不用记录任何状态,后续也不再重发这张图片。
- 中转服务器在返回字段的同时,立刻通过信令通道通知接收方收到一张新图片。
- 接收方在收到新图片通知后,如果用户立刻查看新图片,将看到最小传输单元长度对应的图片。如果用户没有立刻查看,而是过一段时间后查看图片,服务器可能已收到更多甚至完整的图片数据,用户将看到比最小传输单元更清晰的图片。
接收方在展示图片时,无需等到接收到完整图片才展示给用户,收到多少就展示多少,用户无需等待,直接看到一个从模糊到清晰变化的图片,大大缩小收图等待时间。
关键技术点
- 将图片转换成渐进式图片
- 使用流式传输
- 计算最小传输单元
在实际应用中,传输一个完全看不清楚的图片是没有意义的,因此对渐进式图片传输的数据有一个最低要求,保证收到的图片基本可用。
我们称这个长度为图片最小传输长度(MinimalTransport Size),MTS与原图片总大小(TS,Total Size)的比值,称为传输因子Q。
公式为:MTS = TS×Q
传输因子Q可根据多种因素来调节。包括终端所在网络类型、信号强度、传输目标图片的尺寸和质量等。
在这个传输系统中,发送方只要发送图片的最小传输长度,即可向用户展示发送成功,然后转到后台尽量把完整数据发送完毕。
在条件不允许(网络中断、没电、用户切换APP等)情况下,可以不用发送更多数据。