Graceful Degradation Background

昨天解了一個其實困擾蠻久的問題,就是在蘋果開始支援 Retina Display(HiDPI)之後,網站上使用的圖片也跟著要提升解析度,一般網頁會使用的圖片基本上就是<img />標籤的圖片和用 CSS 設定的背景圖,對於<img />標籤來說,提供高解析度的圖片並不會有向下相容的問題,當然瀏覽器和作業系統不支援的,抓了比較大的圖下來是比較浪費,也為此有<picture>標籤、srcset屬性和 Client Hints 等標準來處理這個問題,不過我的需求只要圖片都能正常且正確的顯示。

<img />標籤只要有設定寬高,圖片顯示出來就會是預期的大小,但是background-image就不是了,在background-size出來前,背景圖多大,它在網頁上就會照那樣的尺寸下去畫,所以在不支援background-size的瀏覽器(IE6-8)上用 HiDPI 的背景圖就會很悲劇,雖然可以用 media query 來處理,不過解析度相關的 media query,如果要考慮到以前的瀏覽器,變化有點多,從早期的 device pixel ratio 到 dppx 到現在 resoulution 用的 dpi,還要加上 vendor prefix,自己寫起來有點痛苦,而且原始碼變醜很多,當然,如果有用 PostCSS 就可以用 autoprefixer 解決,不過其實我不太喜歡寫 Media Query,所以都是能少則少(針對 CSS 的 polyfill 也是不太喜歡用),並且有些舊的專案沒有 PostCSS,所以就一直有這個問題,以前的處理方法就是幾種:

  • 真的寫 Media Query
  • <img />標籤模擬背景圖,也很麻煩(也可以用 polyfill,不過個人不想用)
  • 大家一起用標準解析度的圖

這些方法對我來說都算是 workaround 的方式,一直想找個漂亮乾淨一點的解法,直到昨天終於想到了,首先整理一下我的需求:

  • 可以有兩張圖不同解析度的圖
  • 不要用 Media Query
  • 有機會支援 HiDPI 的環境就用 HiDPI 的圖
  • 舊瀏覽器顯示正確

其實這些需求可以簡化成:**不支援background-size的就乖乖看低解析度的圖片吧**,其中隱含著一個現實狀況是,不支援background-size的瀏覽器都只能在不支援 HiDPI 的環境下跑,實做上基本的構想是先寫好一個基本背景的定義,然後再加上一個只有新瀏覽器看的懂得定義,這構想有了很久,卻到昨天才想到怎麼寫,結果如下:

background: #123456 url('the-bg.png') center center;
background: #123456 url('the-bg@2x.png') center center/32px 32px;

用了background可以把全部背景相關屬性寫在一起,而且如果解析失敗就整條失效的特性,以前因為我都會background-size單獨寫(有另外的原因),這種寫法就落入我的盲區,所以過了這麼久才想到,不過這方法不是萬能,還有些限制和使用時機:

  • 有需要支援 IE6-8 才需要,IE9 以上就有支援background-size
  • 不考慮 HiDPI 時其實不需要background-size,例如固定大小區塊的背景、用背景的 icon、有重複排列的背景等
  • background-size: cover;依然需要靠 polyfill 之類的來處理

想出來之後,有開了 IE8 來測試,結果一如預期,這樣應該是 IE6-7 也會正確吧。