探索Web上图片使用方式

发布于 大漠

图片在Web上的使用占比已经非常的高,特别是在手淘互动这样的环境之下,我们每一个项目的图片使用量都非常的大。如果能把图片用好,的确是件不容易的事情,而且面对的挑战也不小。经常会碰到有关于图片如何加载,如何适配,如何优化等等。

另外在移动端开发中,很多同学在Web上使用图片的方法一般都是通过<img>标签和CSS的background-image属性来处理。也正因如此,很多同学却忘记了这两者应该如何?怎么使用又是最优的。甚至有很多前端同学都已经忘记了这两者的差异是什么?

加上Web的技术不断革新,事实上除了前面提到的加载图片的方式之外,还有其他的方式,比如HTML5的<picture>(虽然这个元素标签曾经一度废弃过,但后来又添加回来了)。就算还是使用img标签,也有了新的优化,比如imgsrcset属性。

那么面对这么多的变化,以及使用的场景,我们应该怎么来选择,才是最优的选择。今天这篇文章,我们就来一起探讨一下这些东西,希望大家会喜欢。

Web引入图片的姿势

Web上如何引入图片,其实前面已经简单的提到过。这也是探索Web上图片最基础的部分。所以花点时间来简单的描述一下。一般在Web上引入图片,大体分为两种类型,其一是通过 HTML元素引入图片,其二是通过 CSS样式引入图片

HTML元素引入图片

通过HTML元素在Web上引入图片最常见的方式是通过<img>元素来引入。比如:

<img src="logo.png" alt="w3cplus" />

img元素是通过src来引入图片资源(图片的路径),除此src属性之外,还有其属性,比如上例中的alt属性提供有意义的描述(当图片加载失败的时候,alt的内容将会显示在Web上。这些描述有助于提高您的网站的可访问性,因为它们能提供语境给屏幕阅读器及其他辅助性技术),widthhegiht等常用属性。在HTML5中还给img元素添加了两个新属性 srcset 和**sizes**。

srcset 属性增强了 img 元素的行为,让您能够轻松地针对不同设备特性提供多种图片文件。类似于 CSS 原生的 image-set 函数srcset 也允许浏览器根据设备特性选择最佳图像,例如,在 2x 显示屏上使用 2x 图像,将来甚至允许在网络带宽有限时对 2x 设备使用 1x 图像。

在不支持 srcset 的浏览器上,浏览器只需使用 src 属性指定的默认图像文件。 正因如此,无论设备能力如何,一定要始终包含一个在任何设备上都能显示的 1x 图像。如果 srcset 受支持,则会在进行任何请求之前对逗号分隔的图像/条件列表进行解析,并且只会下载和显示最合适的图像。 比如,你现在可以换个姿势使用<img>来根据不同的环境加载不同的图片:

<img
    srcset="quilt_2/detail/large.jpg  1920w, 
            quilt_2/detail/medium.jpg  960w,
            quilt_2/detail/small.jpg   480w"
    src="quilt_2/detail/medium.jpg"
    alt="Detail of the above quilt, highlighting the embroidery and exotic stitchwork.">

另外,imgsizes属性也非常的强大。该属性可以使图片资源尺寸的值被用来指定图像的预期尺寸。当srcset使用w描述符时,用户代理使用当前图像大小来选择srcset中合适的一个图像URL。被选中的尺寸影响图像的显示大小。如果没有设置srcset属性,或者没值,那么sizes属性也将不起作用。来看一个简单的例子:

<img src="lighthouse-200.jpg" sizes="50vw"
    srcset="lighthouse-100.jpg 100w, lighthouse-200.jpg 200w,
            lighthouse-400.jpg 400w, lighthouse-800.jpg 800w,
            lighthouse-1000.jpg 1000w, lighthouse-1400.jpg 1400w,
            lighthouse-1800.jpg 1800w" alt="a lighthouse">

在上面的例子中,渲染了一张宽度为视窗宽度一半(sizes="50vw")的图像,根据浏览器的宽度及其设备像素比,允许浏览器选择正确的图像,而不考虑浏览器窗口有多大。

上面我们看到的仅是srcsetsizes属性的冰山一角,有关于更详细的东西我们不在这里阐述,如果你感兴趣的话,可以观注后续的更新,因为我最近正在探讨如何借助这两个特性在移动端不同的设备上加载所需要的图像资源。

在HTML5中新增了一个元素标签:<picture>。该元素可以通过若干个<source>,而且浏览器会自动匹配<source>typemediasrcset等属性,来找到最适合当前布局、视窗宽度、设备像素密度的图像资源。其次在该元素里面还可以内嵌一个img标签,用来作为其降级处理,一旦浏览器不支持picture元素,会自动引入内嵌的img引入的图片。比如下面这个示例:

<picture>
    <source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x">
    <source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x">
    <img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood">
</picture>

上面的例子中,如果浏览器宽度至少为 800px,则将根据设备分辨率使用 head.jpghead-2x.jpg。如果浏览器宽度在 450px800px 之间,则将根据设备分辨率使用 head-small.jpghead-small-2x.jpg。对于屏幕宽度小于 450px,且不支持 picture 元素向后兼容的情况,浏览器将渲染 img 元素,因此要始终包含该元素。

是不是看上去picture有点类似于具备了srcsetsizes属性的img

CSS引入图片

除了熟悉的img元素之外,在Web上还有另一种大家熟悉的方式是通过CSS的background-image属性来引入图片。其使用方式非常的简单:

.logo {
    background-image: url(logo.png);
}

该属性除了可以给一个元素设置一个背景图像之外,还可以通过用逗号隔开引用多个ulr(),给元素设置多个背景图像。图像在绘制时,以z轴方向堆叠的方式进行。先指定的图像会在之后指的图像上面绘制。因此指定的第一个图像最接近用户。

在CSS中我们可以通过一些相关的属性来控制background-image引入的图像的展示方式。比如background-positionbackground-repeatbackground-sizebackground-clip等。除此之外,还可以使用CSS的混合模式属性,比如background-blend-mode处理一些特殊效果。

在CSS中除background-image之外,CSS Images Module Level 4提供了一个image-set()函数,可以帮助我们根据不同的dpr显示不同的图像:

.logo {
    background-image: image-set(url(logo@1x.png) 1x,url(logo@2x.png) 2x);
}

当然,也可以在image-set()前面引入正常使用background-image的方式,给image-set()做降级处理:

.logo {
    background-image: image-set(url(logo@1x.png);
    background-image: image-set(url(logo@1x.png) 1x,url(logo@2x.png) 2x);
}

在HTML中的img或者picture元素,我们可以通过srcsetsizes等特性为不同的状态(比如不同的视窗宽度)加载不同的图像。时至今日,CSS可以借助媒体查询特性,显示不同的图像。比如媒体查询根据设备像素比规则,让你实现类似image-set()的特性,针对2x1x显示不同的图像。

@media (min-resolution: 2dppx),(-webkit-min-device-pixel-ratio: 2){
    .logo {
        background-image: url(logo@2x.png)
    }
}

Chrome、Firefox 和 Opera 都支持标准的 (min-resolution: 2dppx),Safari 和 Android 浏览器则均需要不带 dppx 单位的旧版供应商前缀语法。请谨记,这些样式只有在设备与媒体查询匹配时才被加载,且必须为基础案例指定样式。 这样也能够确保当浏览器不支持分辨率特有的媒体查询时,一些内容仍可以得到渲染。

.logo {
    background-image: url(logo@1x.png);
}

@media (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) {
    .logo {
        background-image: url(logo@2x.png);
    }
}

您也可以使用 min-width(或max-width) 语法根据视口大小显示备用图像。 此方法的好处是,如果媒体查询不匹配,则图像不会被下载。 例如,只有在浏览器宽度等于 500px 或更大时,bg.png 才会被下载,然后应用于 body

@media (min-width: 500px) {
    body {
        background-image: url(bg.png);
    }
}

图像处理方式

这里所说的图像处理方式并不像图像处理软件中一样的处理图像。这里所说的处理方式主要指的是图像样式的处理,其涵盖:效果处理和适配处理两个部分。

图像的效果处理

先不管是哪种方式引入的图像,在Web上总是会遇到图像效果的处理。最直接的,比如说处理图像的尺寸。在img中我们可以使用该元素自备的属性widthheight来处理。当然也可以通过CSS的widthheightmax-width/heightmin-width/hegiht来处理。但要处理尺寸之外的一些效果,我们就只能通过CSS的属性来处理。

到目前为止,通过CSS来处理图像的效果只要有CSS的 混合模式滤镜相关的属性。对于imgpicture元素引入的图像,可以使用mix-blend-modefilter。比如下面这个mix-blend-mode处理的图像效果

再来看一看filter处理图像的效果,@una写的一个 CSS滤镜工具

事实上,filter不仅仅可以用于img上,还可以运用于任何的HTML元素上。另外,在CSS滤镜规范中还提供了一些高级滤镜的属性,比如backdrop-filterfilter()属性,这些属性可以帮助我们实现一些更特殊的效果,比如iOS上的毛玻璃效果

除此之外,CSS的混合模式的另一属性background-blend-mode常用于使用了背景图像的元素上,达到处理图像的效果:

@bennettfeelyCodepen上写了一个很优秀的Demo

如果你觉得CSS的混合模式和滤镜很强大,希望深入的去探索相关的知识,建议你花点时间阅读下面相关的教程:

上面我们看到的是CSS混合模式和滤镜怎么处理图像,但有的时候我们需要有一些特殊形状之类的效果,那么此时就需要CSS Masking Module Level 1提供的mask-*clip-path属性。比如这样的效果:

上图是使用mask-image实现的一个效果。clip-path就更强大了:

有关于CSS maskclip-path效果,可以在Codepen上找到很多优秀案例。除了案例,互联网上优秀的教程也不少:

图像适配

图像适配估计让不少的同学头痛和蛋疼,特别是面对移动端开发的时候。在我的印象中,第一次接触图像适配的处理是在做响应式网站的时候。自从有了响应式网站设计,那么对于图像的处理,也有一个对应的专业术语:Responsive Image,对于响应式图像的处理也有很多优秀的文章:

虽然响应式图像很强大,但我们平时面对的还是要想办法怎么灵活的处理图像。针对于这方面,我更喜欢把这些称为**Flexible Images**。而且平时也经常碰到同学会问,怎么让图像能更适合或适配容器,比如最常见的全屏图像这样的效果。

时至今日,灵活处理图像有很多种方式,老旧一点的方案是使用max-width

img {
    max-width: 100%;
}

让一张img能较为灵活的适合容器。先进一点的是object-fit这样的属性,让你的图像能较灵活的适配容器:

感兴趣的同学,可以在下面的Demo体验一下下:

前面提到过,引入图像除了img还有background-image,那么在CSS中还有一个background-size属性类似于object-fit属性,可以帮助灵活的处理背景图像适配元素尺寸。

有关于Flexible Images更详细的介绍,可以阅读早期整理的相关文章《Flexible Images》。

另外一个场景是,图像能根据容器的长宽比来显示:

WICG的讨论上,有人提出来了原生的长宽比属性aspect-ratio。例如,给定一个容器元素它的widthheight都设置为auto,并且aspect-ratio的值为2/1max-height:200px。一个容器宽度为500px时,元素首先会设置width:500px,然后根据aspect-ratio比例将height设置为250px。这个时候其实违反了max-height的约束。相反,容器大小会变成height: 200pxwidth:400px。另外,如果元素的max-width是450px时,长宽比将会完全被忽视,因为无法满足。

这是未来的一个CSS属性,虽然离我们很远,但庆幸的是,有很多替代方案可以实现,最常见的就是padding来模拟:

.aspectration { 
    position: relative; /*因为容器所有子元素需要绝对定位*/ 
    height: 0; /*容器高度是由padding来控制,盒模型原理告诉你一切*/ 
    width: 100%; 
} 
.aspectration[data-ratio="16:9"] { 
    padding-top: 56.25%; 
} 
.aspectration[data-ratio="4:3"]{ 
    padding-top: 75%; 
}

如果你要走时髦的话,你可以使用CSS自定义属性、calc()vw,甚至CSS的Grid。我们来看其中一个示例吧:

有关于其他的方案或更详细的介绍,可以花点时间阅读下面的文章:

Web图片加载与渲染规则

前面我们花费了不少的篇幅介绍了Web中怎么加载图片,如何处理图片以及图片的适配。除了关注这几点之外,我们还应该关注图片加载与渲染的规则,因为只有掌握了这些才能更好的理解Web图像的运用。也能给用户更好的体验。那么接下来,咱们将花一点时间来了解一下图片加载和渲染规则。

如果要对图片加载和渲染规则有较好的理解,就需要具备一些基础知识,比如说浏览器的工作原理。不过这部分已经超出这篇文章的范围,如果你感兴趣,可以花一些时间阅读下面几篇文章:

如果你阅读完上面这些文章,我相信你对浏览器渲染的工作原理有所了解,那么后续的内容就不会有什么难度了。

先上一张图:

Web浏览器先会把获取到的HTML代码解析成一个DOM树,HTML中的每个标签都是DOM树中的一个节点,包括display: none隐藏的标签,还有JavaScript动态添加的元素等。浏览器会获取到所有样式,并会把所有样式解析成样式规则,在解析的过程中会去掉浏览器不能识别的样式。

浏览器将会把DOM树和样式规则组合在一起(DOM元素和样式规则匹配)后将会合建一个渲染树(Render Tree),渲染树类似于DOM树,但两者别还是很大的:渲染树能识别样式,渲染树中每个节点(NODE)都有自己的样式,而且渲染树不包含隐藏的节点(比如display:none的节点,还有</head>内的一些节点),因为这些节点不会用于渲染,也不会影响节点的渲染,因此不会包含到渲染树中。一旦渲染树构建完毕后,浏览器就可以根据渲染树来绘制页面了。

简单的归纳就是浏览器渲染Web页面大约会经过六个过程:

  • 解析HTML,构成DOM树
  • 解析加载的样式,构建样式规则树
  • 加载JavaScript,执行JavaScript代码
  • DOM树和样式规则树进行匹配,构成渲染树
  • 计算元素位置进行页面布局
  • 绘制页面,最终在浏览器中呈现

是不是会感觉这个和我们图像加载渲染没啥关系一样,事实并非如此,因为imgpicture或者background-image都是DOM树或样式规则中的一部分,那么咱们套用进来,图片加载和渲染的时机有可能是下面这样:

  • 解析HTML时,如果遇到imgpicture标签,将会加载图片
  • 解析加载的样式,遇到background-image时,并不会加载图片,而会构建样式规则树
  • 加载JavaScript,执行JavaScript代码,如果代码中有创建img元素之类,会添加到DOM树中;如查有添加background-image规则,将会添加到样式规则树中
  • DOM树和样式规则匹配时构建渲染树,如果DOM树节点匹配到样式规则中的backgorund-image,则会加载背景图片
  • 计算元素(图片)位置进行布局
  • 开始渲染图片,浏览器将呈现渲染出来的图片

上面套用浏览器渲染页面的机制,但图片加载与渲染还是有一定的规则。因为,页面中不是所有的<img>(或picture)元素引入的图片和background-image引入的背景图片都会加载的。那么就引发出新问题了,什么时候会真正的加载,加载规则又是什么?

先概括一点:

Web页面中不是所有的图片都会加载和渲染

根据前面介绍的浏览器加载和渲染机制,我们可以归纳为:

  • <img><picture>和设置background-image的元素遇到display:none时,图片会加载但不会渲染
  • <img><picture>和设置background-image的元素祖先元素设置display:none时,background-image不会渲染也不会加载,而imgpicture引入的图片不会渲染但会加载
  • <img><picture>background-image引入相同路径相同图片文件名时,图片只会加载一次
  • 样式文件中background-image引入的图片,如果匹配不到DOM元素,图片不会加载
  • 伪类引入的background-image,比如:hover,只有当伪类被触发时,图片才会加载

感兴趣的同学,不仿自己写写Demo验证一下。

Web图像优化

Web图像通常占据了网页上下载字节的大部分,通常也占据了大量的视觉空间。因此,优化图像通常可以最大限度地减少下载的字节数以及提高网站性能:浏览器需要下载的字节越少,占用客户端的带宽就越少,浏览器下载并在屏幕上渲染有用内容的速度就越快。

图像优化既是一门艺术,也是一门科学:说它是一门艺术,是因为单个图像的压缩并不存在明确的最佳方案,说它是一门科学,则是因为有许多发展成熟的方法和算法都能够显著缩减图像的大小。找到图像的最佳设置需要在许多方面进行认真分析:格式能力、编码数据的内容、质量、像素尺寸等。

@Ilya Grigorik在《图像优化》一文中做了详细的介绍。下面几点是他给大家做图片优化时提供的参考意见:

  • 首选矢量格式:矢量图像与分辨率和缩放无关,这使它们成为多设备和高分辨率情况的完美选择。
  • 缩小和压缩 SVG 资产: 大多数绘图应用程序生成的 XML 标记往往包含可以移除的多余元数据;确保您的服务器配置为对 SVG 资产采用 GZIP 压缩。
  • 挑选最佳光栅图片格式:确定您的功能要求并选择适合每个特定资产的格式。
  • 通过试验为光栅格式找到最佳质量设置:不要害怕调低“质量”设置,调低后的效果通常很不错,并且字节数的缩减很显著。
  • 移除多余的图像元数据:许多光栅图像都包含多余的资产元数据:地理信息、相机信息等。请使用合适的工具删除这些数据。
  • 提供缩放的图像:调整服务器上的图像尺寸,并确保图像的“显示”尺寸尽可能接近其“自然”尺寸。尤其要密切注意较大的图像,因为在调整尺寸时,它们占用的开销最大!
  • 自动化、自动化、自动化:投资购置自动化工具和基础设施,这样可以确保您的所有图像资产始终得到优化。

结论

Web图片在Web中已经是很重要的一部分,理解或掌握了怎么在Web中使用图片是很重要的。可以让你的网站更具有体验性,或者从利益上说,他直接可以帮助你省不少银子。这篇文章,从怎么引入Web图片方式入手,以及如何处理图片,优化图片,甚至从浏览器的加载和渲染机制的基础上进行更深一步的判断,在实际使用中如何选择。比如是使用imgpicture还是background-image。当然,使用图片不仅仅这些东西,涉及到的方方面面还是很多,只有彻底了解清楚这些东西,你才能更好的使用好图片。如果你感兴趣,不仿花一些时间阅读阅读《Images guide》一文。 我想你会喜欢上这篇指南的。FILA Luminous Pack