object-fit

在一些情境下,網站的圖片來源可能無法和版面很契合,例如新聞網站,新聞的照片可能有直的橫的,甚至有不同的寬高比,但是網站的版面配置不可能配合所有的可能性,如果想要有個封面故事,又要個滿版的照片,又或者是提供給 Facebook 的照片,它的縮圖都是正方形的,但是大部分的時候,文章的照片都不會是這種比例,最理想是有個 server 端的程式可以幫忙把圖片轉成想要的大小,例如 Facebook 其實是有個程式來作這件事,包括調整大小、重新壓縮、快取,大概連雲端分散式儲存的部份等等問題都一口氣處理掉了。

如果要純前端處理,其實目前最好用的作法是用 CSS3 Backgroundbackground-size: cover;,這個樣式會讓標籤的背景圖調整成剛好可以填滿元素大小的程度,當然也考慮好寬高比了,這個方法最主要的缺點在於把圖片從 HTML 文件中抽掉了,在語意上不太好,像是搜尋引擎之類的,對於<img>和 CSS 背景的處理應該還是會有差異的,例如 Google 圖片搜尋我就沒印象有找到 CSS 的背景圖過。

要維持<img>標籤的存在,又要不管大小和寬高比都可以滿版,在現在是只能用 JavaScript 來輔助,實際上的邏輯也不會太複雜,其實就是比較一下版面的寬高比和圖片的寬高比,然後決定用外框的寬還是高當基準,接著維持圖片的寬高比縮放到計算的大小,定位到讓圖片置中,寫成 jQuery plugin 大概像是下面這樣:

$.fn.cover = (selector) ->
  $(@).each ->
    $outer = $(@)
    ow = $outer.width()
    oh = $outer.height()
    or = ow / oh
    $outer.find(selector).each ->
      $item = $(@)
      iw = $item.width()
      ih = $item.height()
      ir = iw / ih
      if ir < or
        w = ow
        h = ow / ir
        l = 0
        t = (oh - h) / 2
      else
        h = oh
        w = oh * ir
        t = 0
        l = (ow - w) / 2

      $item.css(width: w, height: h, top: t, left: l)

使用範例:

$('.cell').cover('img')

不過實際上還要考慮的問題不少,像是執行的時間點,上面這個範例可以運作的時間不只要圖片讀好,有正確的寬高之外,.cell或是說$outer也要在頁面上顯示,有 render 過,才能夠取得它的寬高,整個函式才能夠正確的運作,結果就是訪客其實會看到一瞬間圖片調整好大小位置前的樣子。

用 JavaScript 加上<img>這個方案的缺點除了上面說的之外,還有一個是會需要多一層的標籤,不過目前還是很多人會使用這個方案,也有不少 Library 在處理這個問題,像是 fit.js

這個問題,其實在未來就不存在了,CSS3 Image 裡面有個新的樣式定義就是為了處理這個問題,叫做 object-fit,可以想像成讓<img>標籤可以用 background-image 的方式來操控裡面的圖面了,而對應background-size的,就是object-fit了,未來可以這樣寫 HTML:

<img src="http://blah.com/blah.png" class="cover" width="300" height="300" />

配上 CSS:

img.cover {
  object-fit: cover;
}

就可以同時達到不用多一層標籤,又有實際的<img>,加上不用 JavaScript,不用考慮 resize 圖片的時機,非常的完美,不過這個新標準目前實作的瀏覽器不多,只有 Chrome 和換成 WebKit 前的 Opera,Opera 的 blog 也有發表過一篇文章介紹這個新屬性:CSS3 Object Fit Object Position,裡面也有不少範例可以用 Chrome 開來試試看。

要說有什麼缺點的話,其實還是有的,就是這個 object-fit 不是排版用的,只能用在圖片上,不像 JavaScript library 基本上是什麼元素都可以調整,沒有受限。