前端必備技術之Web圖像優化
前端優化有很多,圖像優化也是其中的一部分。無論是漸進增強還是優雅降級,圖像優化成為了開發上不可忽視的一部分。
知其然,須知其所以然
圖像優化的前提是需要了解圖像的基本原理。常規的圖像格式分為矢量圖和位圖。
原理:
-
矢量圖形使用線、點和多邊形來表示圖像。
-
光柵圖形,也可以成為位圖,通過對矩形格柵內的每個像素的值進行編碼表示圖像。
矢量格式適用于簡單形狀圖形,并且變換顏色方便,僅通過 CSS 中的 fill 屬性便可以改變顏色。并且在多大的縮放下都能保證清晰,矢量格式不能滿足復雜的圖像,例如照片,高清圖。這時候我們就需要位圖,位圖的格式有很多:
-
GIF
-
PNG
-
JPEG
-
JPEG-XR
-
WebP
-
Bpg
其中 Webp 是比較流行的圖像格式方案,目前移動端 Android 4.0 以上、PC 端 chrome 10+(14 ~ 16 有渲染 bug )、opera 11+ 均支持 webp 格式圖片,相比 jpg 體積減少了 65%,但編碼解碼速度慢了很多,雖然 webp 會額外增加解碼時間,但由于體積小了,縮短了加載時間,實際上文件的渲染速度反而快了。
另外如果考慮到更全的兼容性問題,還是得回歸到 jpg 和 png 上,常規的的選擇會用 jpg 作為背景圖,png 作為小塊的圖片,當然都需要經過壓縮,服務端可以使用 Gzip ,上傳圖片前還能使用工具進行一遍壓縮,比如使用 ps,或者在線壓縮
TinyPNG 或者客戶端工具 ImageOptim。
壓縮可分為有損壓縮和無損壓縮。
-
使用有損壓縮處理圖像,是去除某些像素數據。
-
使用無損壓縮處理圖像,是對像素數據進行壓縮。
壓縮的方案可以根據需求選擇。
優化策略
常見的優化方案:
- 使用 Data URI 即(base64)編碼代替圖片:適用于圖片大小于 2 KB,頁面上引用圖片總數不多的情況,原理是將圖片轉換為 base64 編碼字符串 inline 到頁面或 CSS 中,可以減少 HTTP 請求。
- 合并雪碧圖(sprite):移動端多圖情況下,可以將多圖合并到一個圖中,通過 CSS 定位背景圖的形式來引用圖片,可以有效減少 HTTP 請求。
- 使用 CSS、svg、canvas 或者 iconfont 代替圖片:適用于移動端或高級的瀏覽器,而且繪制的圖片比較簡單。
- 懶加載圖片(lazyload)
- 使用 cdn 提供訪問圖片的入口。
響應式圖片
響應式圖片可以結合懶加載的形式,這樣可以加強網頁的體驗。很多網站 logo 就是一個固定寬度的圖像的例子,不管瀏覽器視口的寬度如何,始終保持相同的寬度。
然而在移動端,往往需要不固定的圖像,不同視口,不同的分辨率,需要展示不同的圖像大小,圖雖視口的改變而改變。
這個時候我們需要考慮使用響應式圖片:
- <img srcset="360.jpg 360w, 768.jpg 768w, 1200.jpg 1200w, 1920.jpg 1920w" sizes="(max-width: 360px) 100vw,
- (max-width: 768px) 90vw,
- (max-width: 1980px) 80vw"
- src="360.jpg" alt="">
- srcset:我們給瀏覽器準備了四個質量的圖像,分別為 360 768 1200 1920
- size:我們來告訴瀏覽器,在不同的環境下圖像的寬度
當視口不大于 360 時,圖像的寬度為 100vw,當視口大于 768 時,圖像顯示為 90vw,以此類推。
***的 src 是作為默認圖像 url 引入,是一個回退方案,當然瀏覽器不認 srcset 和 sizes 屬性時,直接讀取 src 渲染。
demo:
iphone4(320)下,圖像寬度和我們設置的 100vw 一致,而瀏覽器選擇的是 768 圖像沒有選擇 360 圖,因為 iphone4 的 dpr 是 2,瀏覽器智能地選擇了合適的 768。
iphone6p(414)下,由于 6p 的 drp 更高,瀏覽器選擇了 1200 質量的圖像,顯示了 90vw。
這時我們可以欺騙一下瀏覽器:
360.jpg 1200w
1200.jpg 9999w
這時瀏覽器把 360 的圖當成了 1200 來用了。這里可能有些疑問,圖像的寬度為什么不是90vw 了哪?因為瀏覽器被騙了但是自己卻不知道,他依然按照 1200 的圖像,去適配 dpr。414 * 90% *(360 / 1200)約等于 111.7。這種方式很智能,瀏覽器根據你的 sizes,從 w 列表中選擇最合適的圖像來調用顯示。
如果我們需要更精確的控制瀏覽器在什么視口大小下顯示多大的圖像,可以使用 picture 元素。
- <picture>
- <source media="(min-width: 960px)" srcset=960.jpg">
- <source media="(min-width: 768px)" srcset="768.jpg">
- <img src="360.jpg" alt="">
- </picture>
當視口大于 960 像素時,會加載 960.jpg。大于 768 像素時,會加載 768.jpg。視口小于768,則加載默認圖像。雖然不是每個瀏覽器都支持 picture 元素,還可以使用 Picturefill polyfill。
加載以及顯示策略
多圖渲染的情況下,結合懶加載,又要保證圖像的渲染速度,類似知乎的渲染效果,我們可以使用 progressive-jpg。
相比 baseline-jpg 一行一行的掃描并顯示圖片,當然都是從弱網角度考慮,這種顯示可能更合適。但還是有不足。參考了下知乎和 medium 等網站的示圖效果,可以進行模擬:
- 先創建一個為圖片占位的預留塊,在這個塊中會展示圖片。塊中有另外一個塊會先設置一個 padding-bottom 來撐起塊的高(即保證需要加載圖像也是這個寬度高度的比例)。這樣防止圖片在加載時發生重排。
- 加載一個輕量版的圖片。這個時候會先請求一個圖片的縮略圖。并使用模糊 blur 效果
- 等滾到到可視區域,加載高質量圖,加載完畢后取消模糊效果。
medium 下的實現方式更為復雜點,是在縮略圖加載完畢后,繪制到 canvas 畫布,再通過一個自定義的模糊函數,類似于 StackBlur,同時請求高質量圖。等到請求完,再隱藏畫布。
簡單的例子可參照 “https://codepen.io/SitePoint/pen/VPVEZm”。