您的位置:首页 > 其它

用于可变像素密度的高dpi图像

2017-10-14 17:36 246 查看
原始文章:High DPI Images for Variable Pixel Densities

一,介绍

当今复杂设备的一个特点是屏幕像素密度非常大。有些设备的分辨率很高,而有些设备则落后。应用程序开发人员需要支持一系列像素密度,这很有挑战性。在移动互联网上,挑战因以下几个因素而加剧:

各种各样的具有不同形状因素的装置。

约束网络带宽和电池寿命。

在图像方面,Web应用程序开发人员的目标是尽可能高效地提供最优质的图像。 本文将介绍一些有用的技术,用于今天和不久的将来。

1,尽可能避免图像

在解开这团乱麻前,请记住,网络具有许多强大的技术,这些技术在很大程度上与分辨率和DPI无关。具体来说,因为web(通过devicePixelRatio)的自动像素缩放特性,文本、SVG和许多CSS将“正常工作”。

也就是说,你不能总是避免光栅图像。例如,您可能会获得在纯SVG / CSS中复制的资源,或者正在处理照片。虽然您可以自动将图像转换为SVG,但是矢量化的照片很有意义,因为放大的版本通常看起来不太好。

二,背景(background)

1,显示密度很短的历史

在早期,计算机显示器的像素密度为72或96dpi(每英寸点数)。

显示器在像素密度上逐渐提高,很大程度上是由移动用例驱动的,在这种情况下,用户通常把手机靠近他们的脸,使像素更加可见。到2008年,150dpi手机成为新的标准。显示密度增加的趋势持续了,今天的新款手机有300dpi显示(苹果公司称其为“Retina(视网膜)”)。

当然,圣杯是像素完全不可见的显示器。 对于手机外形,目前的Retina / HiDPI显示屏可能接近于理想状态。 但是像Glass Glass这样的新一代硬件和可穿戴产品可能会继续推动像素密度的提高。

在实践中,低密度图像在新屏幕上应该与旧屏幕相同,但与高密度用户习惯看到的清晰图像相比,低密度图像看起来很不和谐,像素化。 以下是1x图像在2x显示屏上的外观的粗略模拟。 相比之下,2x图像看起来相当不错。





2,web上的像素

当网络被设计时,99%的显示器是96dpi(或伪装是),在这方面几乎没有什么规定。由于屏幕尺寸和密度的变化很大,我们需要一种标准的方法来让图像在不同的屏幕密度和尺寸上看起来很好。

HTML规范最近解决了这个问题,通过定义一个制造商用来确定CSS像素的大小的参考像素。

建议参考像素为像素密度为96dpi,与阅读器距离为臂长的设备上的一个像素的视角。 对于28英寸的标称臂长度,视角约为0.0213度。

4000

使用参考像素,制造商可以确定设备相对于标准或理想像素的物理像素的大小。 该比率称为器件像素比。

3,计算设备像素比

假设智能手机具有物理像素大小为每英寸180像素(ppi)的屏幕。 计算设备像素比例需要三个步骤:

将设备保持的实际距离与参考像素的距离进行比较。

将距离比与标准密度(96ppi)相乘,以获得给定距离的理想像素密度。

idealPixelDensity =(28/18)* 96 =每英寸150像素(约)

取物理像素密度与理想像素密度的比值,得到设备像素比。

devicePixelRatio = 180/150 = 1.2



因此,当浏览器需要知道如何根据理想或标准分辨率调整图像大小以适合屏幕时,浏览器指的是1.2的设备像素比,它说,对于每一个理想的像素,这个设备有1.2个物理像素。在理想(由web规范定义)和物理(在设备屏幕上的点)之间进行的公式如下:

physicalPixels = window.devicePixelRatio * idealPixels


从历史上看,设备供应商倾向于绕过devicepixel比率(DPRs)。苹果公司的iPhone和iPad的DPR值为1,而他们的Retina(视网膜)相当于报告2。CSS规范建议这样做

像素单位是指最接近于参考像素的整个设备像素的数量。

圆比可以更好的一个原因是因为它们可能导致更少的亚像素伪像。

然而,设备的实际情况要多得多,Android手机的DPR为1.5。 Nexus 7平板电脑的DPR为〜1.33,通过类似于上述的计算得到。 将来可以看到有更多DPR的设备。 因此,您不应假设您的客户端将拥有整数DPR。

三,HiDPI图像技术概述

有许多技术可以解决显示最佳质量图像的问题,大致可分为两类:

优化单个图像

优化多个图像之间的选择

单个图像的方法:使用一个图像,但是做一些聪明的事情。这些方法有一个缺点,您将不可避免地牺牲性能,因为您将会下载HiDPI图像,即使在使用较低DPI的旧设备上也是如此。这里有一些针对单个图像的方法:

重压缩HiDPI图像

非常棒的图像格式

渐进式图像格式

多图像方法:使用多个图像,但做一些聪明的事情来选择要加载的图像。 这些方法为开发人员创造了相同资产的多个版本的固有开销,然后找出决策策略。

以下选项:

JavaScript

服务器端交付

CSS媒体查询

内置浏览器特性(image - set(),< img srcset>)

1,重压缩HiDPI图像

图像已经占用了高达60%的带宽,用于下载一个普通网站。通过向所有客户端提供HiDPI图像,我们将增加这个数字。它还能增长多少?

我运行了一些测试,生成了1x和2x图像片段的JPEG质量在90、50和20。下面是我使用的shell脚本(使用ImageMagick)生成它们:







从这个小而不科学的抽样中,似乎压缩大的图像提供了一个良好的质量与尺寸的权衡。对于我的眼睛来说,重压缩的2x图像实际上比未压缩的1x图像更好。

当然,为2x设备提供低质量,高度压缩的2x图像比提供更高质量的图像更糟糕,上述方法会导致图像质量下降。 如果您比较质量:90图像质量:20张图像,您将看到一个脆弱的下降和增加的颗粒感。如果高质量的图像是关键的(例如,照片查看器应用程序),或者不愿意妥协的应用程序开发人员,这些工件可能无法接受。

上面的比较完全是用压缩的jpeg做的。值得注意的是,在广泛实现的图像格式(JPEG、PNG、GIF)之间有许多权衡,这使我们……

2,非常棒的图像格式

WebP是一种很有吸引力的图像格式,在保持高图像保真度的同时压缩得很好。当然,它还没有在任何地方实现!

一种方法是通过JavaScript来检查WebP的支持。 您通过data-uri加载1px图像,等待加载或错误事件触发,然后验证大小是否正确。 Modernizr拥有这样一个特征检测脚本,可以通过Modernizr.webp获得。

然而,更好的方法是直接在CSS中使用image()函数。 因此如果你有一个WebP图像和JPEG后备,你可以写下列内容:

#pic {
background: image("foo.webp", "foo.jpg");
}


这种方法有一些问题。 首先,image()并没有得到广泛的应用。 其次,虽然WebP压缩将JPEG压缩在水中,但它仍然是一个相对渐进的改进——基于这个WebP gallery,大约减少了30%。因此,WebP本身不足以解决高DPI问题。

3,渐进式图像格式

像JPEG 2000,Progressive JPEG,Progressive PNG和GIF这样的进步图像格式在完全加载之前就可以看到图像的出现(有些争议)。他们可能会产生一些巨大的开销,尽管有一些相互矛盾的证据。Jeff Atwood声称,渐进式模式“增加了约20%的PNG图像大小,约占JPEG和GIF图像大小的10%”。然而,Stoyan Stefanov声称,对于大文件来说,渐进式模式更有效(在大多数情况下)。

乍一看,渐进式图像在尽可能快地提供最佳质量图像的上下文中看起来非常有希望。 这个想法是,一旦知道附加数据不会增加图像质量(即,所有的保真度改进都是子像素),浏览器就可以停止对图像的下载和解码。

虽然连接很容易终止,但重新启动通常很昂贵。 对于具有许多图像的站点,最有效的方法是保持单个HTTP连接活动,并尽可能长时间地重用它。 如果连接被过早终止,因为一个映像已经被足够的下载,那么浏览器然后需要创建一个新的连接,这在低延迟环境中可能真的很慢。

其中一个解决方案是使用HTTP范围请求,它允许浏览器指定要获取的字节范围。一个聪明的浏览器可以发出一个头部请求来获取头部,处理它,决定需要多少图像,然后取回。遗憾的是,HTTP范围在Web服务器中不太受支持,使得该方法不切实际。

最后,这种方法的一个明显的限制是,您不能选择要加载的图像,只能改变相同图像的保真度。 因此,这并没有解决“艺术指导”用例。

4,使用JavaScript来决定要加载的图像

决定要加载哪种映像的第一个也是最明显的方法是在客户端中使用JavaScript。 这种方法可以让您了解有关您的用户代理的一切,并做正确的事情。 您可以通过window.devicePixelRatio来确定设备像素比例,获取屏幕宽度和高度,甚至可能通过导航器连接或发出假请求进行某些网络连接嗅探(例如:如foresight.js库)。 收集所有这些信息后,您可以决定要加载的图像。

大约有一百万个JavaScript库执行类似上述操作,不幸的是没有一个是特别突出的。

这种方法的一个大缺点是,使用JavaScript意味着您将延迟图像加载,直到前面的解析器完成为止。这实际上意味着图像在页面事件触发之后才会开始下载。在Jason Grigsby的文章中有更多的内容。

5,在服务器上决定加载什么图像

您可以通过为您所服务的每个图像编写自定义请求处理程序将决策推迟到服务器端。这样的处理程序将检查基于用户代理的Retina(视网膜)支持(唯一的信息传递到服务器)。然后,基于服务器端逻辑是否要为HiDPI资产提供服务,您需要加载适当的资产(根据一些已知的约定命名)。

不幸的是,用户代理并不一定提供足够的信息来决定设备是否应该接收高质量或低质量的图像。而且,不用说任何与用户代理相关的东西都是黑客,如果可能的话应该避免。

6,使用CSS媒体查询

声明性地,CSS媒体查询可以让您表达您的意图,并让浏览器代表您做正确的事情。 除了最常用的媒体查询 - 匹配设备大小 - 您还可以匹配devicePixelRatio。 相关联的媒体查询是设备像素比,并且具有关联的min和max变体,如您所预期的。 如果要加载高DPI图像,并且设备像素比率超过阈值,则可以这样做:

#my-image { background: (low.png); }

@media only screen and (min-device-pixel-ratio: 1.5) {
#my-image { background: (high.png); }
}


它的所有供应商前缀混合起来变得复杂一点,特别是因为“min”和“max”前缀的位置差异很大:

@media only screen and (min--moz-device-pixel-ratio: 1.5),
(-o-min-device-pixel-ratio: 3/2),
(-webkit-min-device-pixel-ratio: 1.5),
(min-device-pixel-ratio: 1.5) {

#my-image {
background:url(high.png);
}
}


使用这种方法,您就可以重新获得前面的解析的好处,而这是与JS解决方案丢失的。您还可以灵活地选择响应性断点(例如,您可以使用低、中、高的DPI图像),这个是在服务器端方法中丢失的。

不幸的是,它仍然有点笨拙,并导致奇怪的CSS(或需要预处理)。 此外,此方法仅限于CSS属性,因此无法设置
<img src>
,并且您的图像必须都是具有背景的元素。 最后,通过严格依赖设备像素比例,您可以在高DPI智能手机在边缘连接上最终下载大量2x图像资源的情况下结束。 这不是最好的用户体验。

7,使用新的浏览器特性

最近有很多有关Web平台支持高DPI图像问题的讨论。 苹果最近闯入了这个空间,将image-set()CSS函数带到了WebKit。 因此,Safari和Chrome都支持它。 由于它是一个CSS函数,因此image-set()不会解决标签的问题。 输入@srcset,它解决了这个问题,但是(在撰写本文时)没有参考实现。 下一节更深入到 image-set和srcset中。

四,支持高DPI的浏览器特性

最终,决定采用哪种方法取决于您的特定需求。也就是说,要记住前面提到的所有方法都有缺点。然而,展望未来,一旦image-set和srcset得到广泛支持,它们将是解决这个问题的合适方法。现在,让我们谈谈一些能让我们尽可能接近理想未来的最佳实践。

首先,这两个不一样呢? image-set()是一个CSS函数,适合用作背景CSS属性的值。 srcset是一个特定于
<img>
元素的属性,语法类似。 这两个标签都可以指定图像声明,但srcset属性还可以根据视口大小来配置要加载的图像。

1,image-set的最佳实践

image- set()CSS函数可以作为- webkit-image- set()前缀来使用。语法非常简单,使用一个或多个逗号分隔的图像声明,其中包含一个URL字符串或url()函数,然后是相关的解析。例如:

background-image:  -webkit-image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x
);


这告诉浏览器有两张图片可供选择。其中一个为1x显示优化,另一个为2x显示。然后,浏览器就可以根据各种各样的因素选择加载哪一个,如果浏览器足够智能(目前还没有实现),那么它甚至可能包括网络速度。

除了加载正确的图像,浏览器也将相应地缩放。 换句话说,浏览器假设2张图像是1x图像的两倍,因此会将2x图像缩小2倍,以使图像在页面上的尺寸相同。

代替指定1x、1.5 x或Nx,您还可以指定dpi中特定的设备像素密度。

这很有效,除了在不支持image-set属性的浏览器中,它根本没有显示任何图像! 这显然很糟糕,所以你必须使用一个回退(或一系列的退路)来解决这个问题:

background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) <
c44b
span class="hljs-number">2x
);
/* This will be useful if image-set gets into the platform, unprefixed.
Also include other prefixed versions of this */
background-image: image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x
);


以上将在支持image-set的浏览器中加载适当的资产,否则返回到1x资产。 显而易见的注意事项是,当image-set()浏览器支持较低时,大多数用户代理将获得1x资产。

这个演示使用image - set()来加载正确的图像,如果不支持这个CSS函数,则返回到1x资产。

在这一点上,您可能想知道为什么不只是多填充(即,构建一个JavaScript shim)image - set()并调用它一天?事实证明,实现CSS函数的有效的多填充是相当困难的。(详细说明原因,请参见 www-style discussion)。

2,图像srcset

这是一个srcset的例子:

<img alt="my awesome image"
src="banner.jpeg"
srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">


您可以看到,除了image-set提供的x声明之外,srcset元素还采用与viewport大小相对应的w和h值,尝试提供最相关的版本。 以上将会将banner-phone.jpeg投放到视口宽度低于640px,banner-phone-HD.jpeg到小屏幕高DPI设备,banner-HD.jpeg到屏幕大于640px的高DPI设备,其他的一切都是banner.jpeg。

3,为图像元素使用image-set

由于img元素的srcset属性在大多数浏览器中都没有实现,所以使用
<DIV>
的背景和使用image-set的方法取代你的img元素可能很诱人。这将会奏效,需要注意。这里的缺点是< img >标记具有长期的语义值。 实际上,这对于网页抓取工具和可访问性的原因很重要。

如果最终使用-webkit-image-set,您可能会试图使用背景CSS属性。 这种方法的缺点是您需要指定图像大小,如果使用非1x图像,那么这是未知的。 而不是这样做,您可以使用CSS属性内容如下:

<div id="my-content-image"
style="content: -webkit-image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x);">
</div>


这将自动根据devicePixelRatio缩放图像。 请参阅这个示例,对于不支持image-set的浏览器,对url()进行额外的回退。

4,Polyfilling srcset

srcset的一个方便之处在于它具有自然回退功能。 在srcset属性未实现的情况下,所有浏览器都知道处理src属性。 此外,由于它只是一个HTML属性,因此可以使用JavaScript创建多维数据集

这个polyfill带有单元测试,以确保尽可能接近规范。此外,还会有一些检查,防止在执行srcset时执行任何代码的polyfill。

这是一个polyfill的演示

五,结论

对于解决高DPI图像的问题,没有灵丹妙药。

最简单的解决方案是完全避免图像,转而选择SVG和CSS。然而,这并不总是现实的,特别是如果你的网站上有高质量的图像。

JS,CSS和使用服务器端的方法都有自己的优点和缺点。 然而,最有希望的方法是利用新的浏览器功能。 虽然浏览器对image-set和srcset的支持仍然不完整,但是现在有合理的回退。

总而言之,我的建议如下:

对于背景图像,使用图像设置,为不支持的浏览器提供适当的回调。

对于内容图像,使用srcset polyfill,或者使用image - set。

在你愿意牺牲图像质量的情况下,考虑使用大量压缩的2x图像。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: