iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 29
1
Modern Web

你所不知道的各種前端 Debug 技巧系列 第 29

[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


上一篇
[Day 28] Device Simulation & Remote Debugging
下一篇
[Day 30] Tips for Lazy Loading Images
系列文
你所不知道的各種前端 Debug 技巧30

尚未有邦友留言

立即登入留言