菜单

银河官方网站:高dpi图片对于不同设备的适配方案,web图像的常见应用策略与技巧

2019年11月22日 - 银河官方网站
银河官方网站:高dpi图片对于不同设备的适配方案,web图像的常见应用策略与技巧

高dpi图片对于不同设备的适配方案

2017/02/18 · CSS ·
dpi

本文作者: 伯乐在线 –
亚里士朱德
。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

英文:html5rocks

原文链接:

介绍

在当今日益复杂的设备领域,屏幕的可用像素密度已经变得非常广泛。既有非常高分辨率的显示设备,也有远远落后的设备。应用程序开发人员需要支持一系列像素密度的显示设备,这可能是相当具有挑战性的。

在移动web端,情况变得更加复杂:

本文介绍一些关于响应式图像的适配应用策略,回退原理,SVG的换色技巧,雪碧图的百分比定位计算公式等相关的一些小知识点,目的在于帮助一部分同学快速的理清图像应用思路,以及一些web图像的应用技巧。

如果可能,尽量避免使用图片

打开这个蠕虫之前,请记住,Web有许多强大的技术,主要是分辨率和DPI独立。
具体来说,由于web的自动像素缩放功能(通过devicePixelRatio),文本,SVG和大多数CSS将“只工作”。

也就是说,你不能总是回避使用图片。
例如,当你在处理一些图片资源的时候,很难用纯svg或css来处理。
把图片自动转为svg并无太大意义,因为只是把图片简单的放大,看起来会比较模糊。

1.响应式图像的应用与回退

高DPI图片技术概览

有许多技术用于解决尽可能快地显示最佳质量图像的问题,大致分为两类:

单图片解决方案:对一张图片进行巧妙地处理。
缺点就是不可避免地牺牲在某些设备上的性能,因为即使在具有较低DPI的旧设备上也将下载高DPI的图片。
包含以下几种实现方式:

多图片解决方案:使用多张图片,选择最优的图片进行显示。这种方式会额外增加开发人员的工作量,因为针对每个图片都要创建多个版本,并使用最优的选择策略。一些可选的方式:

特点:应用简单,上手容易,性能表现良好

高压缩的高DPI图片

图片资源通常占网站下载带宽的60%,如果提供高DPI图片给所有客户端,这一占比将继续扩大。那么具体情况如何?

我用了一些测试脚本来生成图像质量分别为90%,50%,20%的1x图和2x图:

银河官方网站 1

从这个小的不太科学的样本来看,似乎压缩大图像提供了一个良好的质量尺寸权衡。
对于我们的眼睛,高压缩比的2x图像实际上看起来比未压缩的1x图片更好。

当然,向2x设备提供低质量,高压缩比的2x图片远不如提供高质量的图片,并且上述方法将导致图像质量损失。
如果你比较90%图像质量和20%图像质量的图片,你会感到明显的失真和颗粒感。
在对图片质量有较高要求的情况下(例如,照片查看器应用程序),或者对于不愿意妥协的应用程序开发人员来说,这些图片是不可接受的。

上述比较使用了未压缩的JPEG图片。值得注意的是,在广泛使用的图像格式(JPEG,PNG,GIF)之间还需要进行更多的折衷和取舍,这使我们选择了另一种处理方式…

难点:lazyload实现

webp图片格式

WebP是一个非常引人注目的图像格式,压缩非常好,同时保持高图像保真度。
当然它并不是所有情况下可用!

一种方法是通过JavaScript检查是否支持WebP。
通过data-uri加载1x图像,等待加载或错误事件触发,然后验证大小是否正确。
Modernizr附带了这样的功能检测脚本,可通过Modernizr.webp获得。

更好的实现方式是在css中使用image()函数。如果你有webp或者jpeg格式的图片,可以写成:

CSS

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

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

这种方法有一些问题。
首先,image()没有被广泛实现。
第二,虽然WebP压缩打破了JPEG的压缩极限,它仍然是一个相对性的改进 –
体积减少不到30%。
因此,单独的WebP不足以解决高DPI问题。

根据不同设备,不同分辨率,不同像素比使用的响应式图像,常用的有两种场景:

渐进式图片格式

渐进图像格式,如JPEG 2000,Progressive JPEG,Progressive
PNG和GIF的好处就是在图片完全加载之前能看到图片。
这可能会产生一些额外的开销。 Jeff
Atwood声称渐进式图片“增加了约20%的PNG图像的大小,和约10%的JPEG和GIF图像的大小”。
然而,Stoyan
Stefanov声称,对于大文件,渐进式图片更高效(在大多数情况下)。

乍一看,渐进图像看起来非常有前途,能尽可能快地提供最好的质量图片。
现实是,浏览器一旦知道额外的数据无法提高图片质量(所有保真度的改进是基于子像素的),可能停止继续下载和解码图片。

虽然连接容易终止,但是重新启动它们通常是昂贵的。
对于具有许多图像的站点,最有效的方法是保持单个HTTP连接活动,尽可能长时间地重复使用它。
一张图片下载完毕,浏览器将关闭当前连接,然后再创建新的连接,这在弱网络环境中可能真的很慢。

对此的一种解决方法是使用HTTP
Range请求,它允许浏览器指定要提取的字节范围。
智能浏览器可以做出HEAD请求来获取标题,处理它,决定实际需要多少图片然后获取。
不幸的是,HTTP Rang在Web服务器中支持不足,使得这种方法不切实际。

最后,这种方法的一个明显的限制是,你不能选择图像的分辨率,只能选择相同图像的不同的保真度。因此不能满足“艺术级别”的用户体验。

1.1固定尺寸图像

用javascript选择图片进行加载

最明显的方法是在客户端中使用JavaScript来决定加载哪一种图片。
这种方法需要获取浏览器的信息来进行判断。
可以通过 window.devicePixelRatio
获取设备像素比率,获取屏幕宽度和高度,甚至可能通过navigator.connection或发出假请求,如foresight.js库做一些网络连接嗅探。
收集所有这些信息后再决定要加载哪个图片。

有大约一百万个JavaScript库做上面的事情,不幸的是没有一个是特别好用的。

这种方法的一个大缺点是,使用JavaScript意味着将延迟图像加载,直到前瞻解析器完成。
这实质上意味着在pageload事件触发之前,图片甚至不会开始下载。

基于设备像素比选择,很多网站logo就是固定宽度图像的一个例子,不管viewport的宽度如何,始终保持相同的宽度。

由服务端选择图片

可以通过为每个图片编写自定义请求处理程序来处理。
这样的处理程序将基于User-Agent(中继到服务器的唯一信息)检查Retina支持。
然后,根据服务器端逻辑决定是否要提供高DPI图片来加载适当的资产(根据一些已知的惯例命名)。

不幸的是,用户代理不一定提供足够的信息来决定设备是否应该接收高质量或低质量的图像。
此外,与User-Agent相关的任何内容都可能成为被攻击的漏洞,应该尽量避免使用。

在dom里图像与在css里的图像写法如下面的例子

用css媒体查询

CSS媒体查询可以让浏览器知道你的意图并加载正确的的代码。
除了最常见的媒体查询使用 – 匹配设备大小 – 还可以匹配devicePixelRatio。
相关联的媒体查询是device-pixel-ratio,并且有min和max值可以设置。
如果要加载高DPI图片,且设备像素比率超过阈值,则可以执行以下操作:

CSS

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

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

使用这种方法,可以重获前瞻性解析的好处,而JS解决方案失去了它。
还可以灵活地选择响应断点(例如,可以加载低,中和高DPI的图片),当某些图片请求出错的时候。

不幸的是,它仍然有点笨拙,还需要编写且看起来奇怪的css。
此外,此方法仅限于CSS属性,因此无法设置。所有的图片必须都是背景元素。

background-image:image-set(url(test.jpg)1x,url(test2.jpg)2x);

使用新浏览器特性

最近有很多关于web平台支持的高DPI图片问题的讨论。
苹果最近把image-set()这个CSS函数添加到了WebKit。
因此,Safari和Chrome都支持它。
由于它是一个CSS函数,image-set()没有解决标签的问题。
srcset属性解决了这个问题, 下面将更深入地讨论image-set和srcset。

1.2不固定尺寸图像

image-set

image-set 函数使用非常简单,在webkit下需要添加前缀:

CSS

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

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

它将告诉浏览器有两张图片可选。一张是1x图,一张是2x图。然后浏览器会根据一系列因素来自动选择合适的图片加载。

另外浏览器将会子自动缩放对应的图片大小进行加载。

除了设置 1x,1.5x或Nx之外,还可以指定其它设备像素密度。

这种方式比较理想,除开在那些不支持
image-set函数的浏览器上(将不显示任何图片!这太悲剧了,所以需要备用策略)。

CSS

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

1
2
3
4
5
6
7
8
9
10
background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);
 
background-image: image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

支持image-set函数的浏览器将选择图片进行加载,那些不支持的将加载1x图片。
明显的缺陷就是在不支持image-set函数的浏览器上只会加载1x图片。

与内容相关的图片,在需要响应式的时候,它们的大小往往并不是不变的,会随viewport改变,对于这类图像,也有两种常用的处理方式

srcset

XHTML

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

1
2
3
<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元素还接受对应于视口大小的w和h值,试图提供最相关的版本。
上面将为banner-phone.jpeg提供视口宽度在640像素以下的设备,banner-phone-HD.jpeg到小屏幕高DPI设备,banner-HD.jpeg到高DPI设备,屏幕大于640px,以及banner.jpeg
到一切。

因为img元素的srcset属性在大多数浏览器中没有实现,所以你可能很容易想到用带有背景的

替换img元素,并使用image-set函数。这种方式可以正常显示,但缺点就是,标签是具有语义的,使用div降低了爬虫的可访问性。

1.2.1 我们使用srcset搭配w描述符以及sizes属性 。

结论

没有解决高DPI图片问题的银弹。

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

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

打赏支持我写出更多好文章,谢谢!

打赏作者

w描述符告诉浏览器列表中的每个图象的质量。sizes属性是一个包含两个值的,由逗号分隔的列表。根据最新规范,如果srcset中任何图像使用了w描述符,那么必须要设置sizes属性。

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

银河官方网站 2
银河官方网站 3

1 赞 2 收藏
评论

sizes属性有两个值:第一个是媒体条件;第二个是源图尺寸值,在特定媒体条件下,此值决定了图片的宽度。

关于作者:亚里士朱德

银河官方网站 4

微信公众号“web学习社”;js全栈工程师,熟悉node.js、mongoDB。开发者头条top10专栏作者慕课网签约讲师个人博客:yalishizhude.github.io

个人主页 ·
我的文章 ·
19 ·
    

银河官方网站 5

比如:

768.jpg 768w,

1200.jpg 1200w,

1920.jpg 1920w”

sizes=”

(max-width: 360px) 100vw,

(max-width: 768px) 90vw,

(max-width: 1980px) 80vw,

768px”

src=”360.jpg”alt=””>

我们来逐条读这一个img标签的信息

srcset,我们给浏览器准备了四个质量的图像,分别为360 768 1200 1920

sizes,我们来告诉浏览器,在不同的环境下图像的宽度

当视口不大于360的时候,图像显示宽度为100vw,当视口不大于768的时候,图像显示宽度为90vw,以此类推。

最后一个src作为默认图像url引入,并且是天然的回退方案,当浏览器不认识以上属性的时候,直接读取src渲染。

这样说不够直观,我们看个demo

银河官方网站 6

在iphone4(320)下,图像宽度和我们设置的100vw一致,但是为什么浏览器选择了768的图像而没有选择360的?因为4的dpr是2呀^_^,浏览器很智能的选择了质量最合适的768.

再看一下6p(414),很听话的按照我们的设置,显示了90vw。因为他的dpr更高,浏览器聪明的选择了1200质量的图像。

银河官方网站 7

这里我们可以欺骗一下浏览器:

360.jpg1200w,

1200.jpg9999w

我们把360的图像,骗浏览器说这是1200的,然后把原本1200的扔天上去

银河官方网站 8

浏览器果然上当了,他把360的图当成1200的来用了。这里可能有些疑问,图像的宽度为什么不是90vw了哪?因为浏览器被骗了但是自己却不知道,他依然按照1200的图像,去适配dpr。414*90%*(360/1200)约等于111.7。这种方式很智能,浏览器去根据你的sizes,从w列表里选择最适合的图像来调用显示。正因为他太智能了,在实际操作中可控性较差,有些我们想精确控制的图像显示,有时候并不能如意。而且在做lazyload的时候要处理的东西也比较复杂。

这个时候可以考虑另外一种方式。

1.2.2.picture元素,可精确把控

picture元素就像是图像和其源的容器。浏览器仍然需要img元素,用来表明需要加载图片,如果没有img,那么什么都不会渲染。source为浏览器提供了要显示图像的供选版本。

适用场景为:在一个精确特定的转效点(breakpoint)需要显示一个特定的图像时。使用picture元素选择图像,不会有歧义,理解起来也更直观。

在本例中,当viewport大于960像素时,会加载图像960的图像。当viewport宽度大于768像素时,浏览器会加载768的。而当宽度小于768像素时,加载默认图像360。

而且这个写法的懒加载非常好处理,只需要在传统的lazyload策略上稍加改进

data-src

data-srcset

在加载到的时候更换为

src

srcset

就轻松解决了。

http://snghr.tencent.com里面使用较多

他也不需要去特意做回退处理,当浏览器不支持的时候就直接读取img标签。对于懒加载的回退……我选择判断IE
7-8…直接塞url给他…..。

2.特殊格式的图像应用与回退

特点:体积优化效果显著

难点:兼容性掌控

上面picture元素还可以提供基于图片格式选择。

有一些图像格式在较小的文件大小情况下保证了较好的图片质量。听起来还不错,但残酷的事实是没有一个新格式被所有浏览器支持。谷歌的WebP表现不错,但只有Chrome和Opera原声支持JPEG-XR,最初被称为高清照片,是微软发布的一个专有图像格式,仅Internet
Explorer支持

source的type属性用来指定每个图像的MIME类型,浏览器会选择第一个含有其支持的MIME类型的源。源的顺序是至关重要的,如果浏览器无法识别所有的图象类型,它会回退至原来的img元素。

但是目前这些格式的支持多数不会直接这么做,因为代码会有些冗余难看,有判断浏览器ua输出不同dom或者样式的,也有服务端直接输出的。服务端直接输出,或者CDN做特殊处理,进行无感知格式切换,同时预留url和拒绝的接口,处理起来更灵活,省时省力,例如我们的:

银河官方网站 9

银河官方网站 10

服务端根据浏览器的请求头,返回不同的图像格式,对于X5内核还可以支持sharpP。

3.SVG应用

难点:变色方案,响应式定位计算

上面这个source的type属性还支持另一种我们更常用的图像格式,SVG。

说起SVG,这是个出现频率比webp更高的图像格式了,他有着比iconfont更多的优点,所以现在web上正在大量的应用。

优点:

1.SVG提供的功能集涵盖了嵌套转换、裁剪路径、Alpha通道、滤镜效果等能力,它还具备了传统图片没有的矢量功能,可适配任何高清屏。

2.可读性好,有利于SEO与无障碍

与iconfont对比

1.渲染方式不同

关于渲染方式,之前欧文同学的文章已经讲述的很清楚,这里不多做叙述(https://isux.tencent.com/svg-icon-part-one.html),无论黑白渲染,灰度渲染,次像素渲染,还是DirectWriteGDI渲染,既然iconfont他是一个字体,就难逃出现锯齿的命运,特别是在一倍屏幕下的渲染。

银河官方网站 11

2.icon font只能支持单色

icon
font做为字体无法支持多色图形,这就对设计造成了许多限制,因此这也成为了icon
font的一个瓶颈。

3.icon font可读性不好

icon
font主要在页面用Unicode符号调用对应的图标,这种方式不管是浏览器,搜索引擎和对无障碍方面的能力都没有SVG好

在对比完之前,可能有同学就会问,SVG和iconfont对比有个致命的缺点,就是换色.

比如hover换色,iconfont只要写个颜色就好了,SVG是不是需要做两个颜色的图?这也是SVG图像应用我们解决的一个难点之一

SVG换色,最初我试过三个方案

一是mask-image属性,他的优点是简单粗暴,直接用css来mask这个svg图形来进行换色,缺点很明显就是兼容性了,除去兼容性,还是很好用的。

demo:

background:#ff6600;

-webkit-mask:centerno-repeat;

-webkit-mask-image:url(qq-logo.svg);

查看demo

二是通过SVG滤镜来实现,优点是效果更好,缺点除了兼容性,还需要额外的脚本配合。关于滤镜换色的详细说明在我上一篇文章里有详细介绍以及demo

查看文章

三是我们最终选择的底层无感知换色的方案,把修改颜色的脚本集成到了我们的工作流里,我们在写css的时候,遇到svg需要换色的地方,只需要

background-image:url(test.svg?fill=#ffffff)

加一句换色参数,工作流在底层会自动生成你所需要的svg图片并合并到雪碧图里。

SVG应用的另一个难点,就是作为背景图响应式渲染,雪碧图的background-position和background-size
的计算,这个其实也是其他图像都会存在的一个难点。

我的导师 wenju 之前发过这个计算公式相关的文章:

百分比值()是背景图相对于背景定位区(background positioning
area)的百分比,可以控制在容器元素内仅显示Sprites图的部分内容。比如下图中,Sprites图是由四张图像拼成的,要想在容器内仅显示第一张图像,background-size的值应该多少呢?

银河官方网站 12

我们仅需要Sprites图的1/4显示在容器内,那么Sprites图与容器的比例应该是4:1,计算公式为:
background-size : ( Sprites width / image width) (Sprites height / image
height)

银河官方网站 13

如何计算background-position

我们已知的信息如下:

容器元素的尺寸:elW * elH

单张图片的尺寸:imgW * imgH

Sprites图片的尺寸:spritesW * spritesH

单张图片在Sprites图上的位置:imgPosX, imgPosY

我们假设:

点的位置为 (x, y)

容器上的(x, y)点与容器左上角的距离为 cX, cY

Sprites图上的(x, y)点与本张图片左上角的距离为 sX, sY

如果要把某张图片完全显示在容器元素内,我们可以推导出:

elW = imgW, elH = imgH

cX = sX, cY = sY

根据上面的信息,我就可以计算出具体的(x, y)值了,下面以 x% 为例:

cX = elW * x

sX = spritesW * x – imgPosX

elW * x = spritesW * x – imgPosX

解方程后就得到计算公式了:

x = imgPosX / (spritesW – elW) = imgPosX / (spritesW – imgW)

y = imgPosY / (spritesH – elH) = imgPosY / (spritesH – imgH)

如果你每次都手动计算的话会被累死吧?所以这一步我们还是集成到了工作流里,在所有合并雪碧图的地方用这个公式自动计算出位置。

银河官方网站 14

而关于SVG的回退方案,已经是老生常谈

比如

svg标签方式,缺点必须指定宽高,没有图片的保持款高比例特性,优点兼容性好,兼容所有主流浏览器

或者

在支持的浏览器里使用SVG,在不支持的浏览器里显示PNG,优点是type灵活,可用于SVG,WEBP等,而且保持了img标签的特性,方便做布局操作。缺点兼容性要求高,ios9+,安卓5+,微软Edge+

当然这个兼容性说的是source type的兼容,并不是SVG本身的兼容。

对于css里的SVG
的应用与回退策略,比较简单,也已经成熟,一般情况下都是这种用法

background-image:url(fallback.png);

background-image:url(image.svg),none;

利用的技术是CSS3多背景,浏览器只要支持了多背景,几乎无一例外支持SVG

再或者

background-image:url(fallback.png);

background-image:image-set(“test.png”1x,”test-2x.png”2x,

“test-print.png”600dpi);

通过image-set来筛选和回退。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图