圖片相較於文字來說,是容量更大且更難處理的資源,而網站的圖片處理方式往往影響了網站的效能、使用者體驗,甚至是 SEO。

本篇來聊聊一些網站當中的圖片資源處理方式,如何在不影響原有使用者體驗的情況下,增進圖片處理的效能。

圖片格式

首先來談談一個重要的基礎概念:圖片格式。

影像格式百百種,而許多人不知道的是,格式是造成容量大小以及存取速度的因素之一。
因此要增進圖片的效能,最主要的方式就是從圖片的格式下手。

首先我們先來瞭解一下常見的幾種圖片儲存的格式有哪些:

BMP

點陣圖 (Bitmap),1986 年或更早之前誕生 (無確切資料)。是歷史悠久的圖片儲存格式,通常不會進行壓縮,可以儲存完整的影像細節,但是檔案肥大,因此通常不會用在網站上或者有其他流量及傳輸限制的用途,但用做儲存圖片的原始檔來說是一個不錯的選擇。

TIFF

標籤圖檔格式 (Tagged Image File Format),1986 年誕生。同樣也是歷史悠久的圖片儲存格式,屬於點陣圖的一種,不會有壓縮損失,因此通常用於列印以及工業用途的製作上,但容量同樣較大,比較適合用於非網路及傳輸環境。

GIF

圖片交換格式 (Graphics Interchange Format),1987 年誕生。採用 LZW 演算法進行壓縮,但只能處理8位元的顏色,因此色彩上會有大量失真。其特性為可以插入多重影格以達到動畫的效果。主要用於傳輸用途而非儲存用途,是網路上常見的圖片格式。

JPEG

也就是俗稱的 JPG 格式。

聯合圖像專家小組 (Joint Photographic Experts Group),1992 年誕生。穩居影像格式龍頭寶座,採失真壓縮,通常在壓縮後會產生肉眼可見的失真破壞,但可依照不同需求的程度來做壓縮,其壓縮方法相當適合用於儲存複雜的圖形以及色彩表現,是儲存攝影成品的好幫手,但不支援透明。與 PNG 同為網路影像格式的大宗。

PNG

可攜式網路圖片 (Portable Network Graphics),1996 年誕生。顧名思義就是專門開發來用作網路傳輸的格式。可以支援無失真壓縮及透明等特性,通常用於儲存還會進行後續編輯的圖片,與 JPEG 同為網路影像格式的大宗。

SVG

可縮放向量圖形 (Scalable Vector Graphics),2001 年誕生。有別於常見的影像格式,使用 XML 來描述及儲存影像,其檔案容量極小,但通常只用於儲存簡單的向量圖形(logo, icon),是次世代的影像儲存格式。

JPEG 2000

為 JPEG 的新版標準,2000 年誕生。被開發來取代 JPEG 用作圖像壓縮的用途,其壓縮比率比 JPEG 更高,但相較於 JPEG 所產生的失真以及塊狀模糊更少,也支援非破壞性資料壓縮以及更新的漸進式顯示技術,並同時支援了 JPG 所不支援的透明度。但目前大多數瀏覽器仍不支援 JPEG 2000 之圖像顯示,但在醫學影像的分析處理中已經佔有一定程度的應用比例,是次世代的影像儲存格式。

WebP

衍生自影像編碼格式 VP8,是 WebM 之姐妹專案,2010 年誕生,相當年輕。目前主要由 Google 做開發及標準管理。 WebP 被開發用於專門做圖片傳輸的用途,同時支援有損壓縮以及可逆壓縮,其特點就是極小的影像容量,非常適合用於網際網路環境。而目前主流的瀏覽器都已支援 WebP 格式的顯示,但在 Safari 14.0 之前的版本並沒有支援。是次世代的影像儲存格式。

在近代的網站當中,最常見的就是以 .jpg.png 當作最主要的圖片儲存格式。偶爾會使用 .gif 來儲存動態影像。但隨著技術的發展以及瀏覽器的更新,所謂的次世代影像儲存技術已漸漸普及。

本篇的重點就是專注在如何使用次世代的影像格式來達到圖片效能優化的目的。

次世代影像格式

上一節的介紹中有列出三種次世代的影像格式,分別爲 SVGJPEG 2000 以及 WebP

(本篇不會針對 SVG 做說明,因 SVG 主要用途為儲存向量圖形。)

上述提及的次世代影像格式,不論是在壓縮率、品質、失真、可縮放性上都贏過比較舊的影像格式。因此我們現在就採用次世代的影像格式來作爲目標吧!

簡易比較

首先我們先來看看,分別同個圖片轉換為不同格式時的圖片大小。

第一張圖片我挑了一張色彩及圖形較爲複雜的圖片:

Iron railings on the colorful painted wall

將上圖分別以不同格式的最大壓縮比率做轉換之後的結果如下:

Many format of images list in finder

這邊說明一下, 我選用的 JPEG 的壓縮比率約爲 10:1,JPEG 2000 壓縮比率約爲 64:1,也確實反映在檔案大小上面。
但如果設定兩者的壓縮比率相同時, JPEG 2000 不見得會比 JPEG 來的小。

(以上所有壓縮方式皆採用有損壓縮進行。)

接下來我們來看看第二張圖片:

A notebook, a pen, and three buttons on the desktop

其轉換結果為:

Many format of images list in finder

經過一番測試之後,我發現了幾件事情:

  1. JPEG 相較於 PNG 來說,壓縮後的大小更小,更適用於大部分的網站需求。
  2. JPEG 2000 比 JPEG 擁有更大的最大壓縮比率 (JPEG - 10:1, JPEG 2000 - 64:1)。
  3. WebP 在色彩及圖形複雜度簡單的情況下,容量明顯比 JPEG 和 JPEG 2000 來的小。

JPEG 2000 相較於 JPEG 來說,在檔案大小上不一定有優勢,依據圖片的結構及色彩複雜度不同會有不同的結果,但 JPEG 2000 在其他方面 卻比 JPEG 來的更有優勢 (Alpha 通道, 漸進性…等等)。

由此可知,如果我們能夠使用 JPEG 2000 以及 WebP 這兩種格式來儲存我們網站的圖片,就有機會能大幅降低網站內容的大小,增進讀取的速度以及使用者體驗。

那麼究竟該怎麼做呢?
依照上面的結果來看,我們直接選定 WebP 或者 JPEG 2000 作爲格式放到網站上面不就行了嗎?

事情沒有那麼簡單。

我們需要考量以下幾點來決定我們需要採用什麼影像格式:

  1. 圖片是否爲提供使用者下載高畫質的原檔?
  2. 圖片是否在後續需要進行編輯?
  3. 圖片是否需要透明度?
  4. 圖片需要多少解析度?
  5. 圖片是否被瀏覽器支援?

針對上面幾點問題,簡單幫助大家做選擇:

圖片是否提供使用者下載高畫質的原檔

如果是,那麼使用 JPEG 是非常好的選擇。因為 JPEG 能保有不錯的畫質,但又能夠兼顧使用者的裝置相容性。
沒錯,既然要提供給使用者下載,總不能給他一個有可能不被支援的圖片格式吧?

建議有下載需求的話,可以選擇失真較低的壓縮模式及比率。

圖片是否在後續需要進行編輯

如果是,那麼建議能夠以 PNG 的方式來儲存,雖然檔案大小較大,但是能夠保有更多影像資訊及細節,以便後續的使用者進行編輯。
當然,如果要兼顧到檔案大小的話,JPEG 也是可以考慮的。

圖片是否需要透明度

如果是,那麼勢必要捨棄掉 JPEG 這個選項,因為 JPEG 並不支援透明度(Alpha 通道),但是 JPEG 2000、PNG、WebP 等格式都可以支援有透明度的圖片。

圖片需要多少解析度

在使用圖片時,如果使用的地方只需要小張的圖片,那麼可以將圖片轉換爲較小的解析度,如此一來可以大幅度的降低圖片的容量,以提升效能。但是相對的如果預計圖片使用的場景是需要大的解析度支援,那麼千萬不要放太小的解析度,以免造成圖片在網站上看起來過度模糊。

圖片是否被瀏覽器支援

這個問題至關重要。比起放張太大或者模糊的照片,如果圖片不被瀏覽器支援,那麼使用者看到的就是…nothing,這無疑是最差的情況了…
因此我們需要在挑選圖片格式時謹慎注意。

但最近了瀏覽器已經大幅提高對於次世代格式的支援度了,這邊提供一個懶人包給大家:

  • WebP: FireFox, Chrome, Edge 皆有支援,唯 Safari 只有 14.0 以上的版本支援(需要 macOS Big Sur)。
  • JPEG 2000: 所有版本的 Safari 都支援,而其他瀏覽器皆不支援。
  • 其餘格式:基本上都被各大瀏覽器支援

如果想要查詢格式是否被支援,可以使用 Can I Use 做查詢。
懶人連結附上:WebP, JPEG 2000

不難發現,WebP 及 JPEG 2000 剛好是支援度互補的關係,同時提供這兩個選項,就能支援絕大多數的瀏覽器。

好,那麼依照上面的參考資料,我們可以來實際優化我們的網站了。

那這次我們就先把目標定爲:不需要考慮到使用者下載圖片的需求,只是傳統的文章及插圖。
這樣的情況下,該怎麼做呢?讓我們繼續看下去。

優化

下面我簡單整理了幾個優化圖片的要點,供大家參考。

產生需要的圖檔

選定格式

首先我們已經知道我們需要使用次世代的格式(WebP, JPEG 2000)作爲主要的圖片格式,那麼我們就需要將現有的圖片轉檔為我們需要的格式。

但爲了避免某些瀏覽器的支援度問題,我們還需要一個普遍被支援的備用格式,這邊我選擇 JPEG,原因就不贅述了,可以參考上面的內容來挑選你覺得適合的備用格式。

總之,我們需要的圖片格式有如下三種:

  • WebP
  • JPEG 2000
  • JPEG

轉檔方式

你可以使用自己慣用的轉檔工具或網路上的資源,但這邊我建議你可以試試看一個 CLI 轉檔程式: ImageMagick

ImageMagick 可以支援不同的作業系統,也提供了相當多種的圖片格式及轉檔的選項,是一個強大的轉檔工具。

其最簡易的轉檔指令如下,安裝完 ImageMagick 之後,在終端機當中輸入:

# 將 JPEG 轉換爲 JPEG 2000
magick image.jpg image.jp2

這個指令就可以快速的將圖片轉換成目標的格式。

但是實際使用上,我們還需要考慮到一些參數:

  • 大小(解析度)
  • 壓縮比率
  • 色彩
  • 漸進性
  • 銳利度

以下就針對不同的目標格式來說明。

解析度

首先我們要先將圖片依照需求縮小,因為過大的解析度在普遍使用的情形下只會增加網站載入的負擔,除非今天網站的目的本身就是有瀏覽高解析度照片的需求,否則一般我們可以直接將解析度做一定程度的減少來降低圖片容量。

架設我們今天需要的是寬度 1920px 的圖片(也是當今普遍的螢幕解析度)。

(在 Retina 螢幕普及的現在,寬度 1920px 仍有可能會不足,請依照自己的情境來選擇解析度)

magick input.jpg -resize 1920x output.jpg

上面我們傳入 -resize 參數來將圖片做縮放,而 1920x 表示我們的目標是轉換爲寬度 1920px 的圖片,而高度則是依照比例自動縮放。

如果你要指定高度的話:

# 只指定高度
magick input.jpg -resize x1080 output.jpg

# 高度寬度一併指定(注意影像寬高比以免產生影像變形)
magick input.jpg -resize 1920x1080 output.jpg

JPEG

接下來我們將原圖轉為 JPEG。
如果原圖已經是 JPEG 了,仍需要做轉換,以進行圖片的優化。

可參考轉換指令如下:

magick input.jpg -strip -interlace Plane -gaussian-blur 0.05 -quality 50 output.jpg

參數說明:

  • -strip: 移除圖片的內嵌資訊、註解、描述等等,減少文件的大小。
  • -interlace Plane: 將圖片轉換為漸進式圖片,以增加讀取速度及使用者體驗。
  • -gaussian-blur 0.05: 將圖片做高斯模糊處理,給減少容量大小。此選項可以自行決定要不要加。(某些圖片不適合使用模糊處理,請依照自己的需求增減此參數)
  • -quality 50: 設定圖片品質為 50%,範圍為 0 - 100,數字越高,圖片的容量大小越大,此參數就對映到 JPEG 的壓縮比 (1-10)。

Imagemagick 的 JPEG 轉檔指令可參考這裡

WebP

magick input.jpg -strip -gaussian-blur 0.05 -quality 40 output.webp

由於 WebP 提供了較佳的壓縮效率(失真小),所以我們這邊選定 -quality 40 作爲我們的參數。這個數值跟 JPEG 的壓縮比並不相同,而是屬於 WebP 的品質參數。

Imagemagick 的 WebP 相關轉檔指令可參考這裡

另一個 WebP 轉檔工具可以參考 Google 所開發的 cwebp

JPEG 2000

magick input.jpg -strip -gaussian-blur 0.05 -define jp2:rate=64 output.jp2

JPEG 2000 的壓縮比率需要如上方使用 -define jp2:rate=<比率> 來設定,這邊我們選用 64 作爲壓縮比率(64:1),以求較佳的容量大小,可以視情況自行增減數值。

Imagemagick 的 JPEG 2000 相關轉檔指令可參考這裡

使用腳本幫助轉換圖片

如果你的網站圖片很多,如果要手動輸入指令做轉換的話將會是一項大工程。因此我這邊提供一段指令以快速產生我們所需的三種格式的檔案:

#!/bin/bash

# Usage: geni [image name] [image format]

geni() {
    if [ -f "$1.$2" ]; then
        mv "$1.$2" "$1.temp.$2"
        magick "$1.temp.$2" -strip -gaussian-blur 0.05 -quality 40 "$1.webp"
        magick "$1.temp.$2" -strip -gaussian-blur 0.05 -define jp2:rate=64 "$1.jp2"
        magick "$1.temp.$2" -strip -interlace Plane -gaussian-blur 0.05 -quality 50 "$1.jpg"
        rm "$1.temp.$2"
    else
        echo "$1.$2 does not exists."
    fi
}

geni

你可以建立一個 shell script 如 geni.sh,並將上面的內容加入。
接著輸入下面指令,以執行腳本來將 image.jpg 一口氣轉換為 image.jpg, imgae.webp, image.jp2 三種格式。

./geni.sh image jpg

# 請注意:不是輸入 image.jpg,而是將 image 及 jpg 分開輸入

如果出現權限相關錯誤,可以輸入下面指令來解決:

chmod u+x ./geni.sh

你也可以將腳本內所定義的 geni() function 放到你的 .bashrc, .bash_profile.zshrc 當中:

# ~/.bash_profile

geni() {
    if [ -f "$1.$2" ]; then
        mv "$1.$2" "$1.temp.$2"
        magick "$1.temp.$2" -strip -gaussian-blur 0.05 -quality 40 "$1.webp"
        magick "$1.temp.$2" -strip -gaussian-blur 0.05 -define jp2:rate=64 "$1.jp2"
        magick "$1.temp.$2" -strip -interlace Plane -gaussian-blur 0.05 -quality 50 "$1.jpg"
        rm "$1.temp.$2"
    else
        echo "$1.$2 does not exists."
    fi
}

接著重啟終端機,就可以在終端機當中直接使用指令囉:

geni image jpg

# 簡潔有力

如果覺得轉出來的圖片不符合需求,請自行調整 function 裡面的參數哦。

使用 HTML 的 <picture> 來載入圖片

有了圖片資源之後,我們如何將其引入到我們的網站當中呢?

畢竟每張圖片我們現在都擁有三種不同的格式,我們的目的就是讓瀏覽器自動判斷該使用哪張圖片。

而貼心的是,HTML 提供了 <picture> 這個標籤來幫我們處理這件事,首先先來個 MDN 的說明

用法很簡單:

<!-- 我們的圖片名稱分別為 image.webp, image.jp2, image.jpg -->
<picture>
  <source srcset="image.webp" type="image/webp" />
  <source srcset="image.jp2" type="image/jp2" />
  <source srcset="image.jpg" type="image/jpg" />
  <img
    src="image.png"
    class="image-class-name"
  />
</picture>

在這裡 <source> 這個 tag 做的事情就是載入我們實際圖片的資源,而 <img> tag 你可以把它當作一位代理人,也就是說:由 <source> 載入的圖片會被放在 <img> 這個位置來顯示。

瀏覽器載入圖片的規則是:由上而下依序判定哪個資源是被瀏覽器所支援,接著就載入它,一旦載入成功,之後的 <source> 就不會再進行載入。而所有 <source> 都載入失敗之後,才會載入 <img> 當中指定的支援以作備用。

這邊測試一下,使用 Edge 瀏覽器(Chromium 版本)做測試。由於 Edge 跟 Chrome 一樣並不支援 JPEG 2000,所以我們預期他應該要使用的是 WebP 格式。但這邊我故意將 .jp2 的檔案擺在第一位:

<picture>
  <source srcset="colorful.jp2" type="image/jp2" />
  <source srcset="colorful.webp" type="image/webp" />
  <source srcset="colorful.jpg" type="image/jpg" />
  <img
    src="colorful.png"
    class="image"
  />
</picture>

接著打開網站,看見圖片正常顯示,但究竟我們載入的是哪張圖片呢?我們可以打開瀏覽器的開發者工具,選擇網路(Network)的頁籤,篩選影像的資源載入情形,可以看見:

The network detail of image loading

由此可知我們載入的圖片仍是 WebP 格式,而這是由瀏覽器自動判定的結果,第一順位的 jp2 格式不支援,因此順位到第二順位的 webp 格式來進行載入,而在之後的 .jpg 以及 .png 都沒有做載入。

那麼圖片在 DOM 當中圖片如何呈現呢?

我們可以將頁籤切換到元素(Element)去,找到我們位於網站上的 img.image 圖片元素,可以看見:

The element of image colorful

瀏覽器在頁面上仍然是用 <img> 作爲我們的圖片元件,src 欄位雖然寫著 colorful.png,但其指向 colorful.webp 這個圖片資源的。

如此一來我就不用怕瀏覽器不支援圖片格式的問題,同時也改善了圖片載入的速度並達到減輕瀏覽器負擔的目的。

Styling

這邊附加一個技巧,就是在使用 <img> 的情況下實現 background-position: center; + background-size: cover; 的效果:

很簡單,只要在 <img> 的 CSS 上加上:

.image {
  object-fit: cover;
  object-position: center;
}

這樣就可以囉!(光是這個問題我苦惱了好一陣子www)

實際案例測試

我的部落格在進行圖片效能的優化之前,載入時間相當久,於是我就對圖片資源做了最佳化之後,記錄前後的載入情形。

使用我的另一篇文章: 使用 Hugo 建立一個部落格並部署至 GitHub Page 來看看優化成果吧!

Loading time

首先來看看圖片載入時間:

  • Before
The network of a post

可以看到優化前載入時間耗時 3.27 秒,載入圖片總大小 4.2 MB。

  • After
The network of a post

優化後載入時間減少到 0.577 秒,總圖片大小只有 254 kB!

大小從 4.2 MB -> 254 kB,整整減少了 93% 的資源大小。

Lighthouse testing

那麼接下來我們使用 Lighthouse 來檢測一下網站的效能分數吧!

  • Before
The Lighthouse performance result

優化前的效能分數只有 37 分,慘不忍睹…
而下方的提示有說到 FCP(First Contentful Paint) 時間太久,最大面積的元素載入過慢 (Largest Contentful Paint) 等等的,都是因為圖片太過肥大所致。

那麼優化之後的分數呢?

  • After
The Lighthouse performance result

哇!這真是…驚人的結果!

從測試分數就可知道,光是簡易的圖片優化已經帶來非常不錯的效果了。更不用說我自己比較優化前後的載入速度,真的是體感差距明顯阿!

總結

做個總結吧:

根據研究統計,使用者在進入網站之後 3 秒內如果沒有看見想看的內容,就有大幅度的機率離開網站。也就是說,載入速度將會是使用者體驗的首要關卡,也是使用者對於網站的第一感受。強化網站的載入速度:透過減少圖片容量、採用漸進式載入等等方法來降低圖片的載入所需時間,將會是一把利刃,可以有效的提升效能。

但為什麼要先從圖片優化起手呢?那是因爲圖片通常是網站當中檔案大小最大的資源,如果可以減少圖片大小,那麼就可以大幅減少總體的網站載入大小。

雖然優化之後效能的部分還是有很多需要加強的地方,但是至少從選擇格式、減少解析度、使用 <source> 來讓瀏覽器自行選擇相容的格式等這些初步的方法來做優化,便可以看見令人滿意的成果。

當然圖片優化還有很多種進階的方法:

  • 針對不同大小的裝置來顯示不同大小的圖片
  • 使用圖片 CDN
  • 使用響應式圖片(Responsive images)

等等如上述舉例的進階方法,都可以再更進一步的幫助網站有更好的圖片效能。(有機會再來寫寫相關文章吧)

現在就開始優化你的網站吧,從最致命但效果也最大的目標開始:圖片。

提供參考資源