高dpi圖片對于不同設備的適配方案
在當今日益復雜的設備領域,屏幕的可用像素密度已經變得非常廣泛。
既有非常高分辨率的顯示設備,也有遠遠落后的設備。
應用程序開發人員需要支持一系列像素密度的顯示設備,這可能是相當具有挑戰性的。
在移動web端,情況變得更加復雜:
- 各種各樣的設備具有不同的外形尺寸。
- 受限的網絡帶寬和電池壽命。
在圖片方面,Web應用程序開發人員的目標是盡可能高效地提供最佳質量的圖像。
本文將介紹適用于現在和不久將來的有效技術來達到這一效果。
如果可能,盡量避免使用圖片
打開這個蠕蟲之前,請記住,Web有許多強大的技術,主要是分辨率和DPI獨立。 具體來說,由于web的自動像素縮放功能(通過devicePixelRatio),文本,SVG和大多數CSS將“只工作”。
也就是說,你不能總是回避使用圖片。
例如,當你在處理一些圖片資源的時候,很難用純svg或css來處理。
把圖片自動轉為svg并無太大意義,因為只是把圖片簡單的放大,看起來會比較模糊。
高DPI圖片技術概覽
有許多技術用于解決盡可能快地顯示最佳質量圖像的問題,大致分為兩類:
- 單張圖片進行質量優化
- 多張圖片選擇性展示
單圖片解決方案:對一張圖片進行巧妙地處理。
缺點就是不可避免地犧牲在某些設備上的性能,因為即使在具有較低DPI的舊設備上也將下載高DPI的圖片。
包含以下幾種實現方式:
- 高壓縮的高DPI圖片
- webp圖片格式
- 漸進式的圖片格式
多圖片解決方案:使用多張圖片,選擇最優的圖片進行顯示。這種方式會額外增加開發人員的工作量,因為針對每個圖片都要創建多個版本,并使用最優的選擇策略。一些可選的方式:
- Javascript
- 服務端轉發
- css媒體查詢
- 利用瀏覽器內置特性(image-set(), sercset)
高壓縮的高DPI圖片
圖片資源通常占網站下載帶寬的60%,如果提供高DPI圖片給所有客戶端,這一占比將繼續擴大。那么具體情況如何?
我用了一些測試腳本來生成圖像質量分別為90%,50%,20%的1x圖和2x圖:
從這個小的不太科學的樣本來看,似乎壓縮大圖像提供了一個良好的質量尺寸權衡。
對于我們的眼睛,高壓縮比的2x圖像實際上看起來比未壓縮的1x圖片更好。
當然,向2x設備提供低質量,高壓縮比的2x圖片遠不如提供高質量的圖片,并且上述方法將導致圖像質量損失。
如果你比較90%圖像質量和20%圖像質量的圖片,你會感到明顯的失真和顆粒感。
在對圖片質量有較高要求的情況下(例如,照片查看器應用程序),或者對于不愿意妥協的應用程序開發人員來說,這些圖片是不可接受的。
上述比較使用了未壓縮的JPEG圖片。值得注意的是,在廣泛使用的圖像格式(JPEG,PNG,GIF)之間還需要進行更多的折衷和取舍,這使我們選擇了另一種處理方式…
webp圖片格式
WebP是一個非常引人注目的圖像格式,壓縮非常好,同時保持高圖像保真度。 當然它并不是所有情況下可用!
一種方法是通過JavaScript檢查是否支持WebP。
通過data-uri加載1x圖像,等待加載或錯誤事件觸發,然后驗證大小是否正確。 Modernizr附帶了這樣的功能檢測腳本,可通過Modernizr.webp獲得。
更好的實現方式是在css中使用image()函數。如果你有webp或者jpeg格式的圖片,可以寫成:
#pic {
background: image("foo.webp", "foo.jpg");
}
這種方法有一些問題。
首先,image()沒有被廣泛實現。
第二,雖然WebP壓縮打破了JPEG的壓縮極限,它仍然是一個相對性的改進 - 體積減少不到30%。
因此,單獨的WebP不足以解決高DPI問題。
漸進式圖片格式
漸進圖像格式,如JPEG 2000,Progressive JPEG,Progressive PNG和GIF的好處就是在圖片完全加載之前能看到圖片。
這可能會產生一些額外的開銷。 Jeff Atwood 聲稱漸進式圖片“增加了約20%的PNG圖像的大小,和約10%的JPEG和GIF圖像的大小”。
然而, Stoyan Stefanov 聲稱,對于大文件,漸進式圖片更高效(在大多數情況下)。
乍一看,漸進圖像看起來非常有前途,能盡可能快地提供最好的質量圖片。
現實是,瀏覽器一旦知道額外的數據無法提高圖片質量(所有保真度的改進是基于子像素的),可能停止繼續下載和解碼圖片。
雖然連接容易終止,但是重新啟動它們通常是昂貴的。
對于具有許多圖像的站點,最有效的方法是保持單個HTTP連接活動,盡可能長時間地重復使用它。
一張圖片下載完畢,瀏覽器將關閉當前連接,然后再創建新的連接,這在弱網絡環境中可能真的很慢。
對此的一種解決方法是使用HTTP Range請求,它允許瀏覽器指定要提取的字節范圍。
智能瀏覽器可以做出HEAD請求來獲取標題,處理它,決定實際需要多少圖片然后獲取。
不幸的是,HTTP Rang在Web服務器中支持不足,使得這種方法不切實際。
最后,這種方法的一個明顯的限制是,你不能選擇圖像的分辨率,只能選擇相同圖像的不同的保真度。因此不能滿足“藝術級別”的用戶體驗。
用javascript選擇圖片進行加載
最明顯的方法是在客戶端中使用JavaScript來決定加載哪一種圖片。
這種方法需要獲取瀏覽器的信息來進行判斷。
可以通過 window.devicePixelRatio 獲取設備像素比率,獲取屏幕寬度和高度,甚至可能通過navigator.connection或發出假請求,如foresight.js庫做一些網絡連接嗅探。
收集所有這些信息后再決定要加載哪個圖片。
有大約 一百萬個JavaScript庫 做上面的事情,不幸的是沒有一個是特別好用的。
這種方法的一個大缺點是,使用JavaScript意味著將延遲圖像加載,直到前瞻解析器完成。
這實質上意味著在pageload事件觸發之前,圖片甚至不會開始下載。
由服務端選擇圖片
可以通過為每個圖片編寫自定義請求處理程序來處理。
這樣的處理程序將基于User-Agent(中繼到服務器的唯一信息)檢查Retina支持。
然后,根據服務器端邏輯決定是否要提供高DPI圖片來加載適當的資產(根據一些已知的慣例命名)。
不幸的是,用戶代理不一定提供足夠的信息來決定設備是否應該接收高質量或低質量的圖像。
此外,與User-Agent相關的任何內容都可能成為被攻擊的漏洞,應該盡量避免使用。
用css媒體查詢
CSS媒體查詢可以讓瀏覽器知道你的意圖并加載正確的的代碼。 除了最常見的媒體查詢使用 - 匹配設備大小 - 還可以匹配devicePixelRatio。 相關聯的媒體查詢是device-pixel-ratio,并且有min和max值可以設置。 如果要加載高DPI圖片,且設備像素比率超過閾值,則可以執行以下操作:
#my-image { background: (low.png); }
@media only screen and (min-device-pixel-ratio: 1.5) {
#my-image { background: (high.png); }
}
使用這種方法,可以重獲前瞻性解析的好處,而JS解決方案失去了它。
還可以靈活地選擇響應斷點(例如,可以加載低,中和高DPI的圖片),當某些圖片請求出錯的時候。
不幸的是,它仍然有點笨拙,還需要編寫且看起來奇怪的css。 此外,此方法僅限于CSS屬性,因此無法設置。所有的圖片必須都是背景元素。
使用新瀏覽器特性
最近有很多關于web平臺支持的高DPI圖片問題的討論。 蘋果最近把image-set()這個CSS函數添加到了WebKit。 因此,Safari和Chrome都支持它。 由于它是一個CSS函數,image-set()沒有解決標簽的問題。 srcset屬性解決了這個問題, 下面將更深入地討論image-set和srcset。
image-set
image-set 函數使用非常簡單,在webkit下需要添加前綴:
background-image: -webkit-image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x
);
它將告訴瀏覽器有兩張圖片可選。一張是1x圖,一張是2x圖。然后瀏覽器會根據一系列因素來自動選擇合適的圖片加載。
另外瀏覽器將會子自動縮放對應的圖片大小進行加載。
除了設置 1x,1.5x或Nx之外,還可以指定其它設備像素密度。
這種方式比較理想,除開在那些不支持 image-set函數的瀏覽器上(將不顯示任何圖片!這太悲劇了,所以需要備用策略)。
background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x
);
background-image: image-set(
url(icon1x.jpg) 1x,
url(icon2x.jpg) 2x
);
支持image-set函數的瀏覽器將選擇圖片進行加載,那些不支持的將加載1x圖片。
明顯的缺陷就是在不支持image-set函數的瀏覽器上只會加載1x圖片。
srcset
<img alt="my awesome image"
src="banner.jpeg"
srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">
如上所示,除了image-set提供的x聲明之外,srcset元素還接受對應于視口大小的w和h值,試圖提供最相關的版本。
上面將為banner-phone.jpeg提供視口寬度在640像素以下的設備,banner-phone-HD.jpeg到小屏幕高DPI設備,banner-HD.jpeg到高DPI設備,屏幕大于640px,以及banner.jpeg 到一切。
因為img元素的srcset屬性在大多數瀏覽器中沒有實現,所以你可能很容易想到用帶有背景的
替換img元素,并使用image-set函數。這種方式可以正常顯示,但缺點就是,標簽是具有語義的,使用div降低了爬蟲的可訪問性。
結論
沒有解決高DPI圖片問題的銀彈。
最簡單的解決方案是完全避免圖像,選擇SVG和CSS。 但是,這并不現實,特別是如果網站上有高品質的圖片。
JS,CSS和使用服務器端的方法都有自己的優點和缺點。 然而,最有希望的方法是利用新的瀏覽器功能。 雖然瀏覽器對image-set和srcset的支持仍然不完整。
來自:http://yalishizhude.github.io/2017/02/16/dpi-images/