[Day 29] Optimize Images

取自 Artifact Austin: Leaving Pixels Behind - Todd Parker,哪些图片适合使用 SVG?

在网页中,图片通常在所有资源大小中占了最多比例,最简单的优化方式就是缩小图片,但事实上在各式各样的设备中,不同的网路速度、图片处理效能、萤幕特性等等也是必须考量的因素,本篇文章整理了在品质和速度的权衡下进行图片优化的各种手段。

避免使用点阵图

使用 JPG、PNG 等等点阵图之前,请先考虑以下几点:

  • 避免以图片内嵌文字的方式显示文字
  • 能用 CSS 达到类似的效果?
  • 可以转换成 SVG 吗?

由於点阵图通常档案较大,且载入後会耗费较多浏览器效能和记忆体,有较大机率影响使用者体验,此时就需要进行更多优化手段。

使用 SVG

若一定得使用图片,优先考量 SVG:

  • SVG 是向量图,无论放大多少都不会模糊
  • 相较於点阵图,SVG 非常小
  • 传输时可以使用 Gzip、Brotli 等压缩格式

适合 SVG 的图片类型

  • 商标
  • 图示
  • 图表
  • 地图

适合转换为 SVG 的点阵图

  • 较小的图片
  • 由几何图形组成的图片
  • 不希望失真、模糊的图片

最小化 SVG(Minify)

SVG 档本身也可以透过工具如 SVGO 降低档案大小。

 

点阵图

优化图片最直接的方式就是压缩,压缩的方式主要分为两种:

  • Lossy(有损) – 如 JPG,使用近似或是只取部分像素资料的方式来压缩图片大小,降低大小後不可逆。
  • Lossless(无损) – 如 PNG,以重建的方式压缩大小,不影响图片品质。

图片适合的压缩方式要看网站的特性(是否接受失真、是否较关心档案大小),以下几点算是较为通用的部分:

  • 图片中可能存在额外资料如地点、相机资讯,可用工具移除。
  • 尽可能让图片大小和实际显示大小相符,浏览器在缩小大图时需要耗费大量效能。
  • 使用自动化工具例如 Webpack 来进行图片优化,整合开发流程。
  • 有损图片压缩工具能够调整压缩品质,可以尽量压低数字降低大小,通常视觉上的差异不会太大,不过无论哪一种压缩方式,最好的参数还是要经过反覆测试才能确定。

WebP

除了 IE 之外,常见的浏览器都支援 WebP 图片格式,网页使用基本上是首选:

  • 相较常见的 PNG、JPG, WebP 小很多
  • 支援动图,GIF 转 WebP(但还有更小的 WebM)
  • 支援透明(JPG 不支援)

图片优化工具

Imagemin

一款 Plugin based 图片压缩工具,可搭配 Webpack 使用,可依据想要压缩的图片格式和压缩方式安装插件、设定参数,来压缩专案中用到的图片。

url-loader

使用 Webpack + url-loader 插件来打包图片资源,当图片大小在 limit 之内时会转为 DataURL(Base64) 直接放入引入图片的档案里面,因为请求资源时需要额外一次 Round trip,花费的时间会超过直接把图片内容 Inline 到档案内多出来的一点下载时间。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
    ],
  },
};

 

动图(GIF)

说到动图首先想到的就是 GIF,不过 GIF 实在是非常大,不建议直接在网页中使用,可用 FFmpeg 等工具把 GIF 转成 MP4 或是 WebM(非常小,不需支援 IE 的首选)。

<video>

HTML 的 video 元素加上一些属性後就能做到:自动开始、循环、无声、避免全萤幕,和 GIF 没两样,只差在不能直接使用 img 元素。

<video autoplay loop muted playsinline>
  <source src="my-animation.webm" type="video/webm">
  <source src="my-animation.mp4" type="video/mp4">
</video>

利用 sourcetype 让不支援 WebM 的浏览器使用 MP4。

 

响应式图片(Responsive images)

img 加上一些属性让浏览器依据使用者萤幕大小自动判断最适合的图片,保持使用者体验的同时不浪费流量和效能,例如:

<img
  src="flower-large.jpg"
  srcset="flower-small.jpg 480w, flower-large.jpg 1080w"
  sizes="(max-width: 600px) 480px, 50vw"
/>

srcset

标示图片名称和图片的实际大小。

sizes

可以设定 Media query,告诉浏览器此图的 CSS 宽度(实际 CSS 还是要自己设),浏览器会依据 sizes 和 DPR、DPI 来决定要读取 srcset 中的哪一张图片。

注意可以使用各种单位、calc,但不能用 %。
实际测试:https://web.dev/codelab-specifying-multiple-slot-widths/

DPR、DPI

DPR 代表萤幕中显示一个 CSS pixel 所用的实际 Pixel 比例,例如 iPhone X 萤幕的 DPR 是 3,浏览器显示一个 CSS pixel 实际上是用了 3 * 3 = 9 个 Pixels。

DPI 代表每个每寸内的 Pixels 数量,由於 iPhone X 的 DPI 较高,每个 Pixel 靠得很近,如果 CSS 设定 10px 就真的只显示萤幕中的 10 个 Pixels 会让元素小到几乎看不到。

在此附上一个 Demo - Blur Canvas,当 Canvas 自身的宽度为 200,CSS 宽也设为 200px,在 DPR 为 2 的萤幕上显示时每个色点会放大 2 * 2 = 4 倍,看起来就会糊糊的,若把 Canvas 的宽度设为 400,以同样比例画图,再把 CSS 设为 200px,就能实际用上萤幕的所有 Pixels,显示清晰的图片。

src

当浏览器不支援 srcsetsizes 时会 Fallback 到 src,放在 src 的图片应该要能涵盖所有设备萤幕可用的大小。

工具

图片调整

  • ImageMagick – 知名的图片调整工具(CLI)
  • sharp – 速度更快,且有发布为 npm 套件,容易整合

图片 CDN

即时转换、优化图片并进行快取,搭配 CDN 能够让图片传输速度更快,可用网址输入图片转换的参数,例如利用以下网址把图片转为 300 x 200 的大小:

https://<thumbor-server>/300x200/原始图片网址.png

  • thumbor – 开源,可自架的 Server
  • Cloudinary – 功能更多、文件较齐全,但使用量大时需要付费

 

Art Direction

有些图片不适合行动设备中使用,直接等比例缩小到手机宽度的话会让图片变得很丑,因此可以用到 Art direction 的技巧在不同萤幕宽度显示不同的图片。

在手机中显示剪裁过的图片

source

使用 picture + source 会回传第一个 Media query 是 true 的图片,否则 Fallback 到 img,如果在 source 用了 Media query 就不需要 sizes 了。

<picture>
  <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg">
  <source media="(min-width: 800px)" srcset="elva-800w.jpg">
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva">
</picture>

实际测试:https://web.dev/codelab-art-direction/

 

常见问题

为什麽不用 JavaScript 来做响应式图片?

浏览器的 HTML parser 还没解析完 JavaScript、CSS 就能开始下载图片,这也是为什麽需要 srcsetsizes 等详细大小资讯。

那需要多少张图?

通常会以 3 到 5 为准,以使用者体验和效能来说是越多越好,但需要更多 Server 储存空间和撰写更多 HTML。

怎麽决定图片的 sizes

若能够设定多种 sizes 区间能够让使用者体验最好,更进一步还能根据网站使用者的萤幕宽度来决定,可参考 GA 搭配 https://screensiz.es/。

若真的只想使用一张图片,且图片是使用相对宽度,也必须确保该图片够大,刚好涵盖大部分的使用者萤幕宽度。
 

Credits

https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction
https://web.dev/fast/#optimize-your-images


<<:  [Day27]快快乐乐一起打靶机

>>:  [DAY 28] 复刻 Rails - Routing 威力加强版 - 2

Day 23 - p5的WebGL应用 3D 设定

3D场景的基础 基础的要素:物体、光源、材质与摄影机 基础几何形状 平面 plane() 长方体 b...

Day 18 Flask 错误处理与回应

如果在网页中输入了非预期的 URL,或是做出非预期的动作时,正常会出现 404 Not Found ...

Day25-D3 基础图表:折线图+ d3.bisector( )与 d3.defined( )

本篇大纲:d3.line( )、d3.bisector( )、d3.defined( )、范例图表...

Day14 - 解决状态大爆炸 - 2: Hierarchical States (阶层式状态)

同样的例子。 假设这次我希望某些状态是依赖於某些状态的! 比如说有输入有效(valid)跟输入无效(...

GitHub 操作介面介绍 - 让初学者轻松上手

本篇文章同步发布於个人部落格 (後续更新皆会以部落格为主):GitHub 操作介面介绍 上一篇文章我...