/ 响应式设计

响应式图片容器:响应式图片的发展方向

距上一次我写这类的文章已经近一年了,但通过一种“魔幻”的图像格式来解决世界饥饿问题 和/或 响应式图片问题(不管哪个先实现)的梦想一直在我脑海中。几周前,我开始思考是否存在一种图像格式能够解决art direction和分辨率切换问题。

就如何做到这一点我有很多想法,于是我创建了一个原型来证明它的可行性。该原型现在已经可用了,并且可以进行修改更新。在本文中,我会阐释该原型做了什么,它不能做什么,它是如何工作的, 还有它相对与标记解决方案(markup solutions)的优缺点。我也尽力用通俗的语言阐释响应式图片格式的概念,让它更具体和浅显易懂。

“举几个反对标记解决方案(Markup Solutions)的例子?”

不,我不会这么做!发自内心的!我最好的朋友中有些就是使用标记解决方案的。

我加入响应式图片社区已经有段时间了, 在那里做原型、促进和展示标记解决方案。现在的标记解决方案(picture标签 和srcset属性)非常好并且重要的响应式图像用法案例都采用了它。 对我来说, 我倾向于投票支持在所有浏览器上开始推广picture标签和srcset属性 (即它的分辨率切换版本)。

不过采用基于标记的解决方案存在一些缺陷。下面就是谈到响应式图片的标记解决方案时我常听到的对它的批评。

太冗余了

标记解决方案需要通过详细的定义,因为他们必须列举所有资源。当涉及到art direction时, 他们还必须列出所有的断点,这又增加了冗余。

表现和内容混合

标记解决方案在展示面向方向的页面时候需要在标记内保存断点。它混合了表现和内容,意味着布局改变了标记也要跟着改变。

关于如何解决它的讨论已经进行了很多次。特别是媒体查询的出现,不过它不是必须的,特别是当它可以被定义和实现的时候。

定义基于视口的断点

这通常由开发人员提出。考虑性能的问题,基于标记的解决方案是基于视口大小, 而不是图片的大小。 当浏览器开始抓取图像的时候,布局中图像的大小是未知的,所以不能依靠它来确定要抓取的资源。

这意味着开发者可能需要在服务端存放一些视口表和图像大小表,或者记在脑子里,使得在特定的视口和布局下显示尺寸理想的图片。

大多数情况下,额外增加的步骤可以解决这个问题, 不过当一个单一组件以不同的大小尺寸被用于多个页面时,它也会变得复杂起来。

某些情况下导致过度下载

好,这一点是我听到最多的。

从性能角度来看, 所有为不同屏幕尺寸和分辨率提供单一资源的解决方案都需要重新下载整个图像或者更换到高分辨率版本。因为大多数的图像数据很可能已经在浏览器内存中存储,重新下载一次太伤用户的心了(中国用户表示流量烧不起啊~)。

上面讲到的东西让我感慨(再一次),如果我们已经有了一个基于文件格式的解决方案,那么这个世界该多么的美好啊。

为什么文件格式更好?

基于文件格式的解决方案有如下好处:

  • 负载放在了图像编码器端。标记依然和现在的保持一致:一个标签携带一个资源。

  • 通过这种响应性图片解决方案将网站自动转化会更容易些,因为自动化层只关心图像本身,而不是页面标记和布局。

  • 改变图像布局(例如视口大小的改变)只会下载当前图像夫人不同版本或是它的高分辨率的版本, 而不会重新下载已加载过的图像。

  • Web开发者不再需要维护一个图像的多个版本,尽管他们可能因为内容排版原因需要保留该图片的一个非响应式版本。

我尝试通过一种基于文件格式的简单解决方案来缓解web开发人员的繁重工作,避免没用的图片被下载 (即使在情况改变的时候),同时保证预加载能正常工作。

为什么不是渐进式JPEG(Progressive JPEG)?

渐进式JPEG能够实现分辨率切换,但它的要求十分严格。 它严格限制了图像的最低质量,在我看来,它太流线化(data-heavy)了。另外,分辨率的范围是受限的,也没有对更好的编码提供足够的控制权。所以说,渐进式JPEGF根本不能解决art direction问题。

它应该是什么样子的?

我们讨论的是响应式图像容器, 内部包含的层可以是WebP或JPEG-XR或其他未来的格式 。它使用调整大小和裁剪操作覆盖了分辨率切换和art direction用例。

编码器(例如浏览器)会只下载它需要的层来显示合适的图像。每个层的质量都比它之前层高, 如果需要显示合适的尺寸或者更高的分辨率就把相应的数据传给编码器。

它怎么工作?

  1. 编码器获得原始图像,包含了需要输出分辨率的描述信息,还可以附带art direction的指令。
  2. 然后编码器逐层输出像素,直到展示出最终完美的图像。
  3. 每一层都展示了与上一层的差异图像数据。这样解压器可以一个个的构建层,每一次都用上一层来重建当前层,最终生成一个高分辨率的图像。

很明显它支持分辨率切换,如果要支持art direction也可以通过在当前层定位上一层来给予它合适的尺寸。
我们来看一下案例。

ART DIRECTION

下面是一张讨论art direction例子时常用的图片:

请输入图片描述

它最小的时候看起来应该是这样:

请输入图片描述

这只是原图经过裁剪的一个版本,没什么特别的。

下面,在它(图片裁剪版本)上面的一层:

请输入图片描述

你会发现灰色块区域外正常显示, 实际上灰色块区域是上一层(图片裁剪版本)的等价层,并且包含了层与层之间的区别。

下面是第三个图片,最后(最上面)一层:

请输入图片描述

分辨率切换

苹果手机的一个高品质图片

请输入图片描述

下面是它的第一层,显著压缩版本:

请输入图片描述

第二层展示中等大小图片版本和上一层(即第一层)图片“拉伸”版本的不同部分:

请输入图片描述

第三层展示了原始图像和经过“拉伸”的上一层的不同部分:

请输入图片描述

如果你想知道更多细节,可以访问repository

“但是通过ART DIRECTION我需要更多”

我看过的图像旋转和重定位的例子都需要art direction, 通常是根据视口的尺寸,在图片的周围添加一个标识。

这种情况下使用CSS可能更好。CSS transforms可以处理旋转, 而CSS定位和Media-Specific背景图片结合,也能解决其他的问题。

它是如何匹配的?

这就是让人感觉棘手的地方。因为必须创建一个特殊的抓取机制获取这种特殊的图像类型。我不能打包票说我已经解决了这个问题,不过我非常了解它的工作原理。

我推荐依赖HTTP ranges的机制,类似与<video> 元素的获取。

具体来说:

  • 逐步获取的资源应该要逐步得被标记。一个可能的方法是添加一个先进的属性到元素中,用于描述资源。
  • 一旦浏览器检测到这个带有先进属性的图像时,它将获取该资源的初始请求范围。初始请求范围应该是:

1.相对所有图像,体积相对较小,大小固定的;(例如8KB)
2.由作者指定的。(例如一个先进的属性的值);
3.一些探测
4.基于清单(稍后我们会讲到);

  • 浏览器在检测到的同时可以抓取到这个初始范围,就像现在浏览器抓取资源一样,甚至可以比现在更快,因为范围已经预加载的情况下,资源(例如css和js)的请求数就降低了。

  • 一旦浏览器下载到图像的初始范围,它包含文件的外边框 。也就是说浏览器一旦计算完页面的布局,它会知道那些字节范围是需要的,用来正确显示图片。

  • 浏览器会自动判断当前显示的图层是否合适,在后续层更合适的情况下,它会主动获取后续层(即更高分辨率),甚至在它知道合适的尺寸之前就已经获取了。

  • 一旦浏览器页面布局渲染完毕,它会抓取所有需要的图像层。

上面的机制会导致HTTP请求数量的增加,在HTTP/1.1下面还会导致些许的延迟。

这个机制可以通过定义清单文件来优化,清单文件描述了图像资源的字节范围。添加清单的想法由Cyril Concolato 在去年的W3C技术全体/顾问委员会会议中提出 ,这很有意义,借鉴了视频流的经验。它避免了浏览器娶到一个不确定的初始范围(至少在它下载了一次清单文件后)。

添加一个清单可以避免在布局完成后的额外的请求,并且这有助于阻止请求(使用探测器),甚至在布局完成之前。

创建清单文件可以很容易的由开发工具或服务端完成,所以开发者们就不需要手动处理这些细节了。

“难道我们不能简单的重置连接?”

理论上,我们可以解决这个问题。通过抓取整个图像,然后一旦浏览器获得所有需要的数据就立刻重置连接,但这很可能会引入严重的性能问题。

在浏览器会话期间重置TCP连接有以下问题:

  • 它会中断现有连接,准备TCP连接, 这很耗性能并且该连接可能会被用于未来的资源。
  • 它至少会发送一次数据在往返的管道中,这花费的时间用来将浏览器发出的重置信息传到服务器。这些数据浏览器是不会读取的,这就浪费了带宽并导致了加载缓慢。

这种方法的缺点

  • 它需要接触和修改许多浏览器的堆栈,这意味着它的标准化和实现是很痛苦并且需要花一定的时间。
  • 单色和打印的图片例子不能采用这种解决方案。
  • 编码算法涉及到前置层(per-layer)提升时,可能会导致进程负载。因此,解码性能表现可能会成为一个问题。将它交给GPU处理也许可以缓解,不过我不知道那方面是否容易判断。如果你对此有想法,你的回复我会很感激。
  • 引入一个新文件格式是一个长期的过程。就像我们前面介绍的图像格式一样,缺乏客户端机制的支持使得web开发者很痛苦。因为新的文件格式刚开始只支持部分浏览器,所以从服务端机制上必须要支持(希望是基于accept头,而不是UA头)。
  • 如上所述,它可能增加请求数量,在HTTP/1.1下会造成些许延迟。
  • 该方案不能解决“pixel-perfect” 图片,因为处理这种图片需要提高解码速度 。即使能够解决,也不能确定解码速度能通过它受益。
  • 对HTTP 抓取范围机制的依赖可能导致中间缓存服务问题。

那么,我们应该倾向于使用基于标记的解决方案吗?

一点也不。这里的原型展示了通过这样的一个容器如何实现大多数的响应式图片案例。

让这个解决方案成为业界共识,在可交互方面对它进行详细的设计和实现会是一个长期的过程。在HTTP/1.1网站下的性能影响和解码速度方便仍需要更多的探索。

我相信它会成为未来简化响应式图片的一种方式,但我不觉得我们应该干等着那个理想的方案出现。

总结

如果你一开始就跳到了这,没关系。这篇文章的确很长。

综上所述,我展示了(连带一个模型)一种响应式图像格式的工作原理和它能够解决的大多数响应式图片案例。 在通过添加一些比特标记来使它成为一个可行性方案方面,我也阐述了许多细节。

我认为这将成为一个长期的解决方案,因为在它变得实用之前,一些关键问题需要解决。在我看来,主要的问题在于解码性能,紧随其后的则是HTTP/1.1的下载性能影响。

继续探索这个方向是值得的,但是现在我们不要坐以待毙。响应式图片在浏览器、现实生活中的解决方案在(两年前的)今天就应该出现了,而不是二年后的今天。