前言
当我们在做前端性能优化的时候,总是会离不开图片
,尤其在首次内容绘制(FCP)
和最大内容绘制 (LCP)
中,图片显得格外关键,而我发现关于图片格式的文章,一般不全,或者是偏旧。
所以这篇文章,我将把我学过的关于图片
的知识,分享给大家
首先我们可以通过HTTP Archive: Page Weight来直观看一下页面的加载体积
总的 web 资源的平均请求体积是这样的
图片资源,平均请求体积是这样的
我们发现图片在所有资源中所占的比重,是非常惊人的,所以我们来系统地学习一下图片
图片格式
理解像素、颜色和二进制的关系
无论是通过扫描仪、数码相机还是其他图像捕捉设备,图片首先被转换成像素(picture elements)
的阵列。每个像素代表图片中的一个小区域,并且拥有特定的颜色信息。
在计算机中,像素
用二进制
数来表示。不同的图片格式中像素与二进制位数之间的对应关系是不同的。一个像素对应的二进制位数越多,它可以表示的颜色种类就越多,成像效果也就越细腻,文件体积相应也会越大。
一个二进制位
表示两种颜色
(0|1 对应黑|白),如果一种图片格式对应的二进制位数有 n 个,那么它就可以呈现 2^n 种颜色。
颜色
通常通过红绿蓝(RGB)
模型来表示,每个颜色通道可以有从0到255的值。在二进制中,一个字节(byte)有8位,足以表示0到255的任何值(因为2^8 = 256)。因此,每个颜色通道可以用8位二进制数来表示,例如,红色通道的值255可以表示为二进制的11111111
。
图片格式
一旦图片被数字化并分配了颜色值,它就可以被保存为不同的文件格式
,那么我们常见的图片格式有哪些,使用场景是什么,区别是什么
这里我们首先可以看MDN
提供的图像文件类型与格式指南 - Web 媒体技术 | MDN (mozilla.org)
然后我们稍微整理一下,方便大家观看
缩写 | MIME类型 | 文件扩展名 | 摘要 | 压缩 | 透明度 | 动画 |
---|---|---|---|---|---|---|
ANPG | image/apng | .apng | 是无损动画序列的良好选择(GIF 性能较差)。AVIF 和 WebP 性能更好,但浏览器支持较少。 | 无损压缩 | 支持 | 支持 |
AVIF | image/avif | .avif | 由于其性能高,且无需版税,是图像和动画图像的理想选择。与 PNG 或 JPEG 相比,它的压缩效果更好,支持更高的色深、动画帧、透明度等。请注意,在使用 AVIF 时,应包含浏览器支持更好的回退格式(也就是说,要使用 <picture> 元素)。 | 无损压缩 | 支持 | 支持 |
GIF | image/gif | .gif | 是简单图像和动画的不错选择。无损和有索引的静态图像首选 PNG,动画序列可考虑 WebP、AVIF 或 APNG。 | 无损压缩 | 支持 | 支持 |
JEPG | image/jpeg | .jpg、.jpeg、.jfif、.pjpeg、.pjp | 静态图像有损压缩的理想选择(目前最流行)。如果需要更精确地再现图像,则首选 PNG;如果需要更好的再现效果和更高的压缩率,则首选 WebP/AVIF。 | 有损压缩 | 不支持 | 不支持 |
PNG | image/png | .png | 与 JPEG 相比,PNG 能更精确地再现源图像,或在需要透明的情况下更受青睐。WebP/AVIF 可提供更好的压缩和再现效果,但浏览器的支持比较有限。 | 无损压缩 | 支持 | 不支持 |
SVG | image/svg+xml | .svg | 矢量图像格式;适用于用户界面元素、图标、图表等,必须以不同尺寸精确绘制。 | 可以在传输过程中使用 HTTP 压缩技术进行压缩,也可以在磁盘上以 .svgz 文件的形式进行压缩。 | 支持 | 支持 |
WebP | image/webp | .webp | 是静止图像和动画图像的绝佳选择。WebP 的压缩效果比 PNG 或 JPEG 好得多,而且支持更高的色深、动画帧和透明度等。AVIF 的压缩效果稍好,但在浏览器中的支持度不高,而且不支持渐进式渲染。 | 无损和有损压缩 | 支持 | 支持 |
图片格式详解
我们梳理一下每个格式的优缺点,然后说一下常见的使用场景
这里我更想对比着说,看着更直观
GIF和ANPG
首先GIF
诞生于1987年,APNG
相对新一些,是Mozilla在2004年推出的
APNG
实际上是PNG
的拓展(看名字就看出来了),APNG 新增了三种区段
:acTL、fcTL、fdAT
,这三个区段不打算细讲,可能讲了大家也不能很好理解
那么可能有人会问,如果设备或者软件不支持
ANPG只支持PNG
,那怎么办呢
答案是不用担心,ANPG
第一帧储存方式
和普通的PNG
一样在IDAT
区段中
在压缩上
:GIF
采用的是LZW
(据说之前GIF的LZW算法有专利限制),而APNG
采用的是Deflate
,经测试,在经过各种优化手段进行压缩后,ANPG的体积
是小于GIF的
在颜色和透明度上
:GIF
最多支持256种
颜色,进而导致导致了GIF
的画质
都比较差,同时GIF
虽然也支持透明
,但是他只支持将某个颜色
标记成透明,也就是说他不支持半透明
,只支持完全透明或者完全不透明
,所以会出现杂边
问题,这个问题我正好在一篇ANPG的文章中看到了,正好解释一下
而反观ANPG
,则是完全不受限制的
那么ANPG的缺点是什么呢,可能只有兼容性了和普适性问题了吧
关于使用场景,其实因具体情况而异,但是二者基本都用在动态显示
上
JPEG/JPG
有小伙伴可能一直不清楚JPG和JEPG
的区别,这里咱们就稍微说一下
JPEG
(全称Joint Photographic Experts Group)和JPG
(全称Joint Photographic Experts Group format)实际上是指同一个图像格式,没有本质区别。"JPEG"通常是指这个格式的组织和标准,而"JPG"则是这种格式的文件扩展名。
JPG的优点较为明显:当我们把图片体积压缩至原有体积的 50% 以下时,JPG 仍然可以保持住60%的品质。同时JPG格式以24位
存储单个图,可呈现多达1600万
种颜色,这也是压缩后,我们肉眼依旧难以察觉的原因
同时JPG/JEPG
兼容性非常好,所以经常在网页背景图片,轮播图,广告和宣传材料等显眼位置看到它的身影
但是它也有较为明显缺点,有损压缩,在压缩过程中会丢失一部分图像数据。虽然这种损失对于肉眼可能不明显,但在多次编辑和保存后,图像质量的下降可能会变得更加明显,以及在处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩导致的图片模糊会相当明显
同时它不适合用于存储简单图形、图标或文字,因为这些类型的图像通常不需要复杂的颜色信息,而JPG的压缩方式可能会导致这些类型的图像模糊
再者它还不支持透明度处理,所以就需要配合别的格式进行使用
PNG-8 与 PNG-24
我们主要用PNG
来呈现小的Logo、颜色简单且对比强烈的图片或背景等
说到PNG
,基本上就是说PNG-8与PNG-24
,说PNG-32
的较少那我们就来说说二者的区别
首先PNG
后面的8、24数字,意思是指该格式最多可以索引
和存储的颜色值
。
PNG-8
:最多存储256种颜色,也就是2^8 = 256,2的8次方。
PNG-24
最终的,意思是最多存储1600万种颜色,即2^24 = 16 777216种颜色,也就是2的8次方。
再者,二者的透明度
不同
PNG-8
:仅支持1位的布尔透明通道,不支持半透明效果
PNG-24
:支持alpha半透明过渡效果,也就是可以完美呈现从不透明到完全透明的渐变过渡。
最后,二者的压缩率
不同,这也导致最终的体积不同,PNG-24
最终的体积是比PNG-8
要大出不少的,所以PNG-24
不适合存储照片等颜色较多、细节丰富的图片,多用于存储需要保留透明度、颜色单一的图片,比如LOGO、图片水印
等文件应使用PNG-24
存储。
SVG
SVG
和上面的JPG和PNG
等有些不同,SVG-可缩放矢量图形
,为什么它叫做矢量图形
呢
这是因为它的工作原理与传统的位图图形(如JPEG、PNG等)截然不同。矢量图形的核心特点在于它使用数学公式和几何形状来定义图像,而不是像素点的集合。
缩放性
:SVG图像
是基于数学公式的,这意味着它们可以在任何尺寸下无损放大或缩小。无论图像被缩放到多大或多小,都能保持清晰和锐利,不会出现像素化或模糊,1
张SVG
足以适配n
种分辨率
。
小文件尺寸
:SVG文件
通常比位图
文件小,因为它们不需要存储每个像素的颜色信息。相反,它们只存储定义图形的数学方程式和属性,这使得文件尺寸更加紧凑。
文本和编码性
:SVG文件
是文本文件
,使用XML格式
编写。这意味着SVG图像可以被编辑器打开和编辑,就像编辑文本文件一样。可以像写代码一样定义 SVG
,把它写在 HTML
里、成为 DOM
的一部分,也可以把对图形的描述写入以 .svg
为后缀的独立文件
,这种文本基础的特性也使得SVG文件可以被搜索、索引和脚本化。
交互性
:SVG
支持CSS
和JavaScript
,这意味着可以给SVG元素添加样式和行为
,从而创建动态和交互式的图形。例如,可以响应用户的操作,如鼠标悬停、点击等事件。
例如:假设我们有一个简单的SVG图标,我们想在鼠标悬停时改变它的颜色
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="blue" class="hoverable">
<title>Hover over me!</title>
</circle>
</svg>
<style>
.hoverable:hover {
fill: red; /* 鼠标悬停时变为红色 */
cursor: pointer; /* 鼠标悬停时变为手指形状 */
}
</style>
又或者:通过JavaScript为同一个SVG圆添加点击事件,使其在被点击时改变大小
<svg width="100" height="100" id="mysvg">
<circle cx="50" cy="50" r="40" fill="blue" class="clickable">
<title>Click on me!</title>
</circle>
</svg>
<script>
var svg = document.getElementById('mysvg');
var circle = svg.querySelector('.clickable');
circle.addEventListener('click', function() {
var currentRadius = parseInt(circle.getAttribute('r'));
var newRadius = (currentRadius == 40) ? 60 : 40; // 切换半径大小
circle.setAttribute('r', newRadius);
});
</script>
再或者:将SVG
写入独立文件后引入HTML
:
<img src="文件名.svg" alt="">
以上就是简单地举例,目的是为了体现出SVG
的灵活性
在实际开发中,我们更多用到的是后者。很多情况下设计师会给到我们 SVG 文件,就算没有设计师,我们还有非常好用的 iconfont-阿里巴巴矢量图标库。对于矢量图,我们无须深究过多,只需要对其核心特性有所掌握、日后在应用时做到有迹可循即可。(这段是修言说的,我觉得很好,就摘抄了~)
但是我在使用的时候,也发现了一些SVG的问题:虽然
SVG
通常比位图图像小,但在描述的矢量元素几何复杂度较高
时,基于文本的SVG文件可能比二进制编码的栅格化图像要大(但是的确不能算缺点,只能说不是特别适用)
Base64
Base64
其实不是一种图片格式,而是一种编码方法,Base64
是为了减少加载网页图片时对服务器的请求次数,从而提升网页性能,它可以将二进制数据
(如图片)转换为ASCII字符串
。但是会增加页面的总体大小,因为Base64编码后的字符串通常比原始的二进制数据要大(这也是Base64不适用于大图,适用于小图的原因
)
这种感觉有点类似于雪碧图
,但是原理不一样,雪碧图
是一种将多个小图片合并到一张大图片中的技术,通过CSS的background-image
和background-position
属性来引用大图中的特定小图片,这样减少了HTTP请求次数
,提升了网页加载速度
,并且可以提前加载
可能需要用到的小图片,减少页面闪烁
二者更像是相辅相成的关系,它们都可以用于优化网页性能,但选择哪一种技术取决于具体的应用场景和需求
WebP和AVIF
二者都是10年后诞生的,我们称之为现代图片格式,它的优点明显,都能覆盖传统格式(JPG、PNG等)的动态图片
、无损压缩
等功能和特性,传统格式特性较为单一,现代格式支持动态图片、无损压缩等特性更多,并且体积变小
,相较于jpg
格式,webp
格式的图片一般能减少10%左右的体积,avif
格式更是能减少40%以上的文件体积。
缺点基本就是兼容性
(其实WebP还好了,早些年的确兼容性的确不行,现在基本都支持了),这里我们二者进行对比
工作原理上
:
WebP
:基于VP8
编解码器,使用有损和无损压缩方法的组合来实现更小的文件大小,有损压缩与JPEG 相似
,但增加了优化功能,从而提高了图像质量。它的无损压缩使用一种称为预测编码的技术,该技术可预测相邻像素的值,并只对实际值和预测值之间的差值进行编码。AVIF
:使用AV1编解码器
,在不影响图像质量的情况下减小文件大小,具有内部预测
(根据邻近块预测块的内容)和自适应量化
(根据图像的复杂程度调整压缩级别)的功能,既是最先进的又是免版税的。
色彩上
:
WebP
:支持8位和10位色深AVIF
:支持8位和10位以及12位色深,可见AVIF这个后起之秀,在色彩准确度和鲜艳度方面更胜一筹
这样看来AVIF
尤其适用于高质量图像(如照片这种的),而如果需要的是一种通用
的图像格式,并能获得广泛的浏览器支持
,那么 WebP 就是您的不二之选
用现代图片格式优化性能以及体验
我们经过上面的学习发现,如果能使用webp,avif
等现代格式,对解决前端应用图片类资源体积较大,加载耗时较长、CDN开销较高的痛点会有明显优化。那么我们具体怎么做呢?
回答是:<picture>
元素
我们看一下MDN对于picture
元素的定义吧
看起来很懵,什么意思呢
<picture>
元素允许同时引入多个图片格式的子元素,并根据浏览器的兼容性
,按先后顺序、自适应加载其中1个格式的图片,实现所有用户根据自身兼容性,获取到最优图片格式。
我写一段代码,大家就明白了
<picture>
<!-- WebP格式的图片,具有无损和有损压缩 -->
<source srcset="image.webp" type="image/webp">
<!-- AVIF格式的图片,具有更高的压缩率 -->
<source srcset="image.avif" type="image/avif">
<!-- JPEG格式的图片,作为备选 -->
<source srcset="image.jpg" type="image/jpeg">
<!-- 如果浏览器不支持上述任何格式,就使用这个img标签作为最后的备选 -->
<img src="fallback-image.jpg" alt="Description of Image">
</picture>
<picture>
元素是一个容器元素,元素中包含了2个<source>
元素和1个<img>
元素。- 每个
<source>
元素都有一个srcset
属性,它指定了图片的URL
,以及一个type
属性,它描述了图片的MIME类型
。浏览器会根据这些信息和自身的兼容性来决定加载哪个图片。 - 如果所有的
<source>
元素都不被支持,那么<img>
元素将作为最后的备选方案。这是一个包含JPEG
图片(别的格式也可以,选兼容性好的即可)的<img>
标签,相当于一个兜底
操作
通过这种方式,可以确保所有用户都能加载到他们设备支持的最佳格式的图片,从而提供更好的网页体验和性能,而大家也不要小瞧这种方式,现在其实是很常用的。
结尾
本文带着大家过一遍常见的图片格式
,包括内容
,优缺点
等,希望带着大家了解关于图片的具体内容,其实本来是想着把关于图片的优化
都写在这一篇的,奈何实在是过于散乱,配合这篇基础文章显得不合适,所以只介绍了一种较为简单,还能承接上文的<picture>
元素的方式给大家
其实关于什么雪碧图
之类的网上已有全面的教程,所以不打算写了,而关于什么懒加载
的实际上是交互
上的优化内容,所以也不会去写
在之后的文章中会接着写一下关于图片的知识点,图片优化实战
方面的内容,比如写一下辅助函数配合CDN
啦,压缩图片
具体操作啦等内容,蟹蟹大家支持~