本文是系列第二篇。系列文章:
圖片資源,在我們的業務中可謂是佔據了非常大頭的一環,尤其是其對頻寬的消耗是十分巨大的。
對圖片的效能最佳化及體驗最佳化在今天就顯得尤為重要。本文,就將從各個方面闡述,在各種新特性滿頭飛的今天,我們可以如何儘可能的對我們的圖片資源,進行效能最佳化及體驗最佳化。
適配不同的螢幕尺寸及 DPR
下一個模組,我們來看看圖片資源如何更好的適配不同的螢幕尺寸。
這裡首先會涉及一個預備知識,螢幕的 DPR 值,那麼,什麼是 DPR 呢?要了解 DPR,又需要知道什麼是裝置獨立畫素 以及 物理畫素。
裝置獨立畫素
以 iPhone6/7/8為例,這裡我們開啟 Chrome 開發者工具:
這裡的 375 * 667
表示的是什麼呢,表示的是裝置獨立畫素(DIP),也可以理解為 CSS 畫素,也稱為邏輯畫素:
裝置獨立畫素 = CSS 畫素 = 邏輯畫素
如何記憶呢?這裡使用 CSS 畫素來記憶,也就是說。我們設定一個寬度為 375px 的 div,剛好可以充滿這個裝置的一行,配合高度 667px ,則 div 的大小剛好可以充滿整個螢幕。
物理畫素
OK,那麼,什麼又是物理畫素呢。我們到電商網站購買手機,都會看一看手機的引數,以 JD 上的 iPhone7 為例:
可以看到,iPhone7 的解析度是 1334 x 750
,這裡描述的就是螢幕實際的物理畫素。
物理畫素,又稱為裝置畫素。螢幕是由一個個物理畫素點組成的,1334 x 750
表示手機分別在垂直和水平上所具有的畫素點數。透過控制每個畫素點的顏色,就可以使螢幕顯示出不同的影像,螢幕從工廠出來那天起,它上面的物理畫素點就固定不變了,單位為pt。
裝置畫素 = 物理畫素
DPR(Device Pixel Ratio) 裝置畫素比
OK,有了上面兩個概念,就可以順理成章引出下一個概念。DPR(Device Pixel Ratio) 裝置畫素比,這個與我們通常說的視網膜屏(多倍屏,Retina屏)有關。
裝置畫素比描述的是未縮放狀態下,物理畫素和裝置獨立畫素的初始比例關係。
簡單的計算公式:
DPR = 物理畫素 / 裝置獨立畫素
我們套用一下上面 iPhone7 的資料(取裝置的物理畫素寬度與裝置獨立畫素寬度進行計算):
iPhone7’s DPR = iPhone7’s 物理畫素寬度 / iPhone7's 裝置獨立畫素寬度 = 2
750 / 375 = 2
或者是 1334 / 667 = 2
可以得到 iPhone7 的 dpr 為 2。也就是我們常說的視網膜螢幕。
視網膜(Retina)螢幕是蘋果公司"發明"的一個營銷術語。 蘋果公司將 dpr > 1
的螢幕稱為視網膜螢幕。
在視網膜螢幕中,以 dpr = 2 為例,把 4(2x2) 個畫素當 1 個畫素使用,這樣讓螢幕看起來更精緻,但是元素的大小本身卻不會改變:
OK,我們再來看看 iPhone XS Max:
<img width="300" alt="螢幕快照 2019-07-08 下午8 02 00" src="https://user-images.githubusercontent.com/8554143/60808691-7f682a00-a1bb-11e9-8300-294443443a9d.png">
它的物理畫素如上圖是 2688 x 1242
,
它的 CSS 畫素是 896 x 414
,很容易得出 iPhone XS Max 的 dpr 為 3。
為不同 DPR 螢幕,提供恰當的圖片
那麼,DPR 和圖片適配有什麼關係呢?
舉個例子,同樣的 CSS 畫素大小下,螢幕如果有不同 DPR,同樣大小的圖片渲染出來的效果不盡相同。
我們以 dpr = 3
的手機為例子,在 300 x 389
CSS 畫素大小的範圍內,渲染 1倍/2倍/3倍 圖的效果如下:
實際圖片所佔的物理畫素為 900 x 1167。
可以看到,在高 DPR 裝置下提供只有 CSS 畫素大小的圖片,是非常模糊的。
因此,為了在不同的 DPR 螢幕下,讓圖片看起來都不失真,我們需要為不同 DPR 的圖片,提供不同大小的圖片。
那麼,有哪些可行的解決方案呢?
方案一:無腦多倍圖
假設,在移動端假設我們需要一張 CSS 畫素為 300 x 200
的影像,考慮到現在已經有了 dpr = 3 的裝置,那麼要保證圖片在 dpr = 3 的裝置下也正常高畫質展示,我們最大可能需要一張 900 x 600
的原圖。
這樣,不管裝置的 dpr 是否為 3,我們統一都使用 3 倍圖。這樣即使在 dpr = 1,dpr = 2 的裝置上,也能非常好的展示圖片。
當然這樣並不可取,會造成大量頻寬的浪費。
現代瀏覽器,提供了更好的方式,讓我們能夠根據裝置 dpr 的不同,提供不同尺寸的圖片。
方案二:媒體查詢
方案二,我們可以考慮使用媒體查詢。到今天,我們可以透過相應的媒體查詢,得知當前的裝置的 DPR 值,這樣,我們就可以在對應的媒體查詢中,使用對應的圖片。
像是這樣:
#id {
background: url(xxx@2x.png)
}
@media (device-pixel-ratio: 2) {
#id {
background: url(xxx@2x.png)
}
}
@media (device-pixel-ratio: 3) {
#id {
background: url(xxx@3x.png)
}
}
這個方案的缺點在於:
- 要寫的程式碼可能太多了,而且,可能存在一些介於 1~2,2~3 之間的 DPR 值,不好窮舉出所有場景
- 需要注意語法需要的相容性,需要新增字首,譬如
-webkit-min-device-pixel-ratio
,當然這個可以由autoprefixer
輔助解決
方案三:CSS 配合 image-set 語法
image-set
屬於 CSS background 中的一種語法,image-set()
函式為裝置提供最合適的影像解析度,它提供一組影像選項,每個選項都有一個相關的 DPR 宣告,瀏覽器將從中選擇最適合裝置的影像進行設定。
什麼意思呢,來看看程式碼:
.img {
/* 不支援 image-set 的瀏覽器*/
background-image: url('../photo@2x.png');
/* 支援 image-set 的瀏覽器*/
background-image: image-set(
url('./photo@2x.png') 2x,
url('./photo@3x.png') 3x
);
}
這樣一看,作用應該很清晰了。對於支援 image-set
語法的瀏覽器:
- 如果其裝置對應的 DPR 為 2,會選取這條
url('./photo@2x.png') 2x
記錄,也就是最終生效的 URL 是'./photo@2x.png'
; - 如果其裝置對應的 DPR 為 3,會選取這條
url('./photo@3x.png') 3x
記錄,也就是最終生效的 URL 是'./photo@3x.png'
;
其中的 2x
,3x
就是用於匹配 DRP的。
使用 image-set
的一些痛點與媒體查詢方案類似。程式碼量與相容性語法,而且難以匹配所有情況。
方案四:srcset 配合 1x 2x 畫素密度描述符
簡單來說,srcset 可以根據不同的 dpr 拉取對應尺寸的圖片:
<div class='illustration'>
<img src='illustration-small.png'
srcset='images/illustration-small.png 1x,
images/illustration-big.png 2x'
>
</div>
上面 srcset
裡的 1x,2x 表示 畫素密度描述符,表示
- 當螢幕的 dpr = 1 時,使用
images/illustration-small.png
這張圖 - 當螢幕的 dpr = 2 時,使用
images/illustration-big.png
這張圖 - 如果不支援
srcset
語法,src='illustration-small.png'
將會是最終的兜底方案
方案五:srcset 屬性配合 sizes 屬性 w 寬度描述符
上面 1x,2x 的寫法比較容易接受易於理解。
但是,上述 3 種方案都存在統一的問題,只考慮了 DPR,但是忽略了響應性佈局的複雜性與螢幕的多樣性。
因此,規範還推出了一種方案 -- srcset 屬性配合 sizes 屬性 w 寬度描述符。
srcset
屬性還有一個 w 寬度描述符,配合 sizes
屬性一起使用,可以覆蓋更多的面。
sizes
屬性怎麼理解呢?它定義影像元素在不同的視口寬度時,可能的大小值。
以下面這段程式碼為例子:
<img
sizes = “(min-width: 600px) 600px, 300px"
src = "photo.png"
srcset = “photo@1x.png 300w,
photo@2x.png 600w,
photo@3x.png 1200w,
>
解析一下:
sizes = “(min-width: 600px) 600px, 300px"
的意思是:
- 如果螢幕當前的 CSS 畫素寬度大於或者等於 600px,則圖片的 CSS 寬度為 600px
- 反之,則圖片的 CSS 寬度為 300px
也就是 sizes 屬性宣告瞭在不同寬度下圖片的 CSS 寬度表現。這裡可以理解為,大螢幕下圖片寬度為 600px,小螢幕下圖片寬度為 300px。
需要注意的是,這裡大屏、小屏下圖片具體的寬度表現,還是需要藉助媒體查詢程式碼,經由 CSS 實現的
srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w
裡面的 300w,600w,900w 叫寬度描述符。
那麼,怎麼確定當前場景會選取哪張圖片呢?
當前螢幕 dpr = 2 ,CSS 寬度為 375px。
當前螢幕 CSS 寬度為 375px,則圖片 CSS 寬度為 300px。分別用上述 3 個寬度描述符的數值除以 300。
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
上面計算得到的 1、 2、 4 即是算出的有效的畫素密度,換算成和 x 描述符等價的值 。這裡 600w 算出的 2 即滿足 dpr = 2 的情況,選擇此張圖。
當前螢幕 dpr = 3 ,CSS 寬度為 414px。
當前螢幕 CSS 寬度為 414px,則圖片 CSS 寬度仍為 300px。再計算一次:
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
因為 dpr = 3,2 已經不滿足了,則此時會選擇 1200w 這張圖。
當前螢幕 dpr = 1 ,CSS 寬度為 1920px。
當前螢幕 CSS 寬度為 1920px,則圖片 CSS 寬度變為了 600px。再計算一次:
- 300 / 600 = .5
- 600 / 600 = 1
- 1200 / 600 = 2
因為 dpr = 1,所以此時會選擇 600w 對應的圖片。
具體的可以試下這個 Demo:CodePen Demo -- srcset屬性配合w寬度描述符配合sizes屬性
此方案的意義在於考慮到了響應性佈局的複雜性與螢幕的多樣性,利用上述規則,可以一次適配 PC 端大螢幕和移動端高畫質屏,一箭多雕。
嗯,總結一下,在實現響應式影像時,我們同時使用 srcset
和 sizes
屬性。它們的作用是:
srcset
:定義多個不同寬度的影像源,讓瀏覽器在 HTML 解析期間選擇最合適的影像源sizes
:定義影像元素在不同的視口寬度時,可能的大小值
有了這些屬性後,瀏覽器就會根據 srcset/size 來建立一個解析度切換器的響應式圖片,可以在不同的解析度的情況下,提供相同尺寸的影像,或者在不同的檢視大小的情況下,提供不同尺寸大小的影像。
本章總結
本章節一共列舉了 5 種實現響應式圖片,適配不同螢幕大小,不同 DPR 的方式,它們分別是:
- 無腦多倍圖的方式
- DRP 媒體查詢
- CSS Background 中的使用
image-set
- srcset 配合 1x 2x 畫素密度描述符
- srcset 屬性配合 sizes 屬性 w 寬度描述符
合理使用它們,可以有效的為不同螢幕,提供最為恰當的圖片資源,在保證使用者體驗的同時,儘可能節省頻寬。
它們各有優缺點,可以根據自己實際的業務場景,選取合適相對成本最低的方案,並且適當的配合 Autoprefixer 以及一些 PostCSS 等工具,簡化程式碼量。
當然,本文只是現代圖片效能最佳化及體驗最佳化指南的第二篇,後續將給大家帶來圖片在:
- 圖片的寬高比、裁剪與縮放展示
- 懶載入/非同步影像解碼方案
- 可訪問性以及圖片資源的容錯及錯誤處理
等相關知識的介紹,感興趣的可以提前關注。
最後
OK,本文到此結束,希望本文對你有所幫助 :)
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。