【譯】使用Mobify.js讓你的圖片自動響應化

在Web開發社區,響應式圖片已經成爲最大的挫敗之一。緣由也很簡單:頁面平均大小產品能從去年的1MB達到了驚人的1.5MB。其中圖片大小的增加比例就佔了頁面大小增加的60%或更多,而且這個比例還在不斷攀升。javascript

絕大多數的頁面是能夠下降頁面大小的,若是你藉助基於設備寬度、像素密度和現代圖像格式(例如WebP)等優化條件的話。這些減重的方法能夠加快載入時間,讓用戶參與更多、停留更長時間。不過這裏不是爭論是否應該爲不一樣的設備優化圖像,而是關於應該怎麼去作。css

理想狀況下,咱們應該繼續使用img標籤,而後瀏覽器會下載適配設備寬度、適配頁面佈局的圖片。然而,這樣的功能其實並不存在。實現接近這種功能的方法之一是在javascript執行過程當中改變img元素的src屬性,但超前的預解析器(或預加載)扼殺了它的可能性。html

克服這個問題的第一步是建立一個基於標記的解決方案,該方案容許基於設備的功能來替換圖像的來源。隨着W3C響應式圖片交流社區創造的picture元素(儘管目前尚未瀏覽器實現了它)的引入,這個問題已經被解決了。java

不過,picture元素的引入也帶來了新問題:
開發人員如今必須在每個斷點爲每個圖片生成一個獨立的asset。而開發者真正須要的是一個可以將一張高分辨率圖片自動轉化爲適配設備的小圖片的方案。理想狀況下,這個自動化解決方案可以讓每張圖片只請求一次,而且充分語義化和向後兼容。 Mobify.js的圖像API提供了該方案。git

<picture> 元素成爲將來的最佳實踐

picture元素是當前代替img元素的先行者,由於它使得開發者可以爲不一樣的屏幕分辨率指定不一樣的圖片,解決了性能和art direction(儘管新提出的srcN提議值得考慮)問題。典型的設置包括定義斷點,爲不一樣斷點生成圖像,而後爲圖像寫入picture標記。咱們看看如何通使下面的圖片變得響應性:github

請輸入圖片描述

咱們使用了320、5十二、1024和2048像素基線。web

首先,咱們須要爲每張圖片生成不一樣的分辨率的副本,能夠經過命令行工具,例如 Image Optim或使用Photoshop的「存儲爲web所用格式」功能。接下來,咱們使用下面的標記:chrome

<picture>
        <source src="responsive-obama-320.png">
        <source src="responsive-obama-512.png" media="(min-width: 512px)">
        <source src="responsive-obama-1024.png" media="(min-width: 1024px)">
        <source src="responsive-obama-2048.png" media="(min-width: 2048px)">
        <noscript><img src="responsive-obama-320.png"></noscript>
    </picture>

上面的代碼有個問題,在它的配置中,咱們的圖片可能不會對移動設備優化。下面是一張縮放到320像素寬的圖片:segmentfault

請輸入圖片描述

圖片中的人物已經很難區分。爲了更好的適應小屏幕,咱們須要藉助art direction的力量,
爲小屏幕裁切這張圖片。後端

請輸入圖片描述

由於這個文件不是原始圖片的簡單縮放版本,須要給它一個特殊的命名結構(因此,用responsive-obama-mobile.png替代了responsive-obama-320.png

<picture>
    <source src="responsive-obama-mobile.png">
    <source src="responsive-obama-512.png" media="(min-width: 512px)">
    <source src="responsive-obama-1024.png" media="(min-width: 1024px)">
    <source src="responsive-obama-2048.png" media="(min-width: 2048px)">
    <noscript><img src="responsive-obama-512.png"></noscript>
</picture>

可是若是咱們要支持高DPI(點每英寸)設備呢?
picture 元素的規範中有一個srcset屬性,該屬性能讓咱們很容易爲不一樣分辨率指定不一樣的圖片。如下是咱們使用picture元素後的代碼:

<picture>
<source srcset="responsive-obama-mobile.png 1x, responsive-obama-mobile-2x.png 2x">
<source srcset="responsive-obama-512.png 1x, responsive-obama-1024.png 2x" media="(min-width: 512px)">
<source srcset="responsive-obama-1024.png 1x, responsive-obama-1024.png 2x" media="(min-width: 1024px)">
<source srcset="responsive-obama-2048.png 1x, responsive-obama-4096.png 2x" media="(min-width: 2048px)">
<noscript><img src="responsive-obama-512.png"></noscript>

這裏引入了一對新文件(esponsive-obama-mobile-2x.pngresponsive-obama-4096.png),它們也必須生成。到目前爲止,咱們擁有同一個圖片的6個不一樣版本的副本。

讓咱們更進一步。若是咱們想使用現代化的格式,例如WebP來讀取咱們的圖像,須要經過判斷瀏覽器是否支持嗎?忽然,咱們須要生成的文件數量從6個增長到12個。老實說,沒人願意由於不一樣的分辨率而給每一個圖片生成多個版本的圖片副本,而且須要在代碼標記上更新這些圖像版本。
咱們須要讓它自動化!

理想的響應式圖片工做流程

理想的工做流程是這樣的,它容許開發者上傳分辨率儘量高的圖片同時仍然使用img元素來達到自動爲不一樣的瀏覽器重置圖片大小和壓縮圖片的目的。img元素很偉大,這個簡單的標籤解決了簡單的問題:爲互聯網的用戶展現圖片。理想狀況下,咱們能經過一種高效的方法繼續沿用這個元素而且向後兼容。而後,當art direction arises 和衡量圖片下限的需求不能知足時,咱們可使用picture元素,它語法中內建的分支邏輯很完美。

這個理想的工做流能夠經過Mobify.js的響應式圖像API來實現。Mobify.js是一款改善響應式網站的開源庫,提供了響應式圖片、javascript和css的優化,自適應模板等等。它的圖像API可以自動重置大小和壓縮imgpicture元素,必要狀況下,在後端甚至不須要改變任何一行標記代碼。簡單的上傳高分辨率資料而後讓API去關心接下來的事情。

不修改後端代碼自動讓圖片響應化

響應式圖片之因此成爲一個難以解決的問題是因爲超前的預解析器,它阻止咱們經過javascript改變img元素的src屬性。預解析器是瀏覽器的一個功能,經過生成一個獨立的線程(獨立於渲染線程以外),定位資源並使其並行下載,從而讓資源儘快的開始下載。預解析器的工做在響應式設計出現以前頗有意義,但在如今的多設備的狀況下,代碼標記的圖像不必定是咱們但願用戶看到的;所以,咱們須要思考一種容許開發者控制資源加載的同時不犧牲預加載的好處的API。在這個問題上,若是想要了解更多細節,能夠考慮看看teve Souders的「I <3 Image Bytes.」

不少開發者爲了不預加載採用的一種方法是經過img的data-src屬性手動改變src屬性,巧妙地讓預加載器忽略了這些圖像,而後使用javascript將src屬性的值用data-src的替換。

經過Mobify.js 的 捕獲 API,咱們避免了上面的方法,讓咱們在保證性能的狀況下又不失語義(無需 <noscript>data-src )。捕獲技術阻止了初始頁面資源的預加載,但這並不阻礙並行下載。使用Mobify.js的圖像API和捕獲技術相結合,咱們能經過一個javascript標籤實現圖像的自動響應化。

下面是調用API的的代碼:

Mobify.Capture.init(function(capture){
    var capturedDoc = capture.capturedDoc;
    var images = capturedDoc.querySelectorAll('img, picture');
    Mobify.ResizeImages.resize(images, capturedDoc) 
    capture.renderCapturedDoc();
});

它獲取了頁面的全部圖片,而後重寫的src的值以下:

http://ir0.mobify.com/<format><quality>/<maximum width>/<maximum height>/<url>

例如,若是該API在安卓最新版的chrome下運行,設備的css像素爲320,像素比例爲2,那麼圖片就會從

<img src='cdn.mobify.com/mobifyjs/examples/assets/images/forest.jpg'>

變爲

<img src='//ir0.mobify.com/webp/640/http://cdn.mobify.com/mobifyjs/examples/assets/images/forest.jpg'>

forest圖片會被調整到640px寬,而且,由於chrome支持WebP,咱們能夠所以受益,將來能夠下降圖片的體積。在第一次請求以後,圖片就會被Mobify的CDN緩存,以備下次使用。由於這個圖片不須要任何art direction,咱們能夠繼續使用img元素。

你能夠看看這個自動調整圖片大小的例子。打開網站調試工具,肯定原始圖片沒有被下載!

使用這個方案,咱們簡化了工做流程。咱們僅僅上傳了一個高分辨率的圖片,而後讓API實現圖片的自動化調整大小。中間不須要代理過程,沒有改變任何屬性-只是一斷javascript。去吧,試着複製下面的代碼粘貼在head元素裏(請注意它必須寫在其餘資源標籤以前)。

<script>!function(a,b,c,d,e){function g(a,c,d,e){var f=b.getElementsByTagName("script")[0];a.src=e,a.id=c,a.setAttribute("class",d),f.parentNode.insertBefore(a,f)}a.Mobify={points:[+new Date]};var f=/((; )|#|&|^)mobify=(\d)/.exec(location.hash+"; "+b.cookie);if(f&&f[3]){if(!+f[3])return}else if(!c())return;b.write('<plaintext style="display:none">'),setTimeout(function(){var c=a.Mobify=a.Mobify||{};c.capturing=!0;var f=b.createElement("script"),h="mobify",i=function(){var c=new Date;c.setTime(c.getTime()+3e5),b.cookie="mobify=0; expires="+c.toGMTString()+"; path=/",a.location=a.location.href};f.onload=function(){if(e)if("string"==typeof e){var c=b.createElement("script");c.onerror=i,g(c,"main-executable",h,mainUrl)}else a.Mobify.mainExecutable=e.toString(),e()},f.onerror=i,g(f,"mobify-js",h,d)})}(window,document,function(){var a=/webkit|msie\s10|(firefox)[\/\s](\d+)|(opera)[\s\S]*version[\/\s](\d+)|3ds/i.exec(navigator.userAgent);return a?a[1]&&+a[2]<4?!1:a[3]&&+a[4]<11?!1:!0:!1},

// path to mobify.js
"//cdn.mobify.com/mobifyjs/build/mobify-2.0.1.min.js",

// calls to APIs go here
function() {
  var capturing = window.Mobify && window.Mobify.capturing || false;

  if (capturing) {
    Mobify.Capture.init(function(capture){
      var capturedDoc = capture.capturedDoc;

      var images = capturedDoc.querySelectorAll("img, picture");
      Mobify.ResizeImages.resize(images);

      // Render source DOM to document
      capture.renderCapturedDoc();
    });
  }
});
</script>

(請注意這段腳本不存在單點故障。若是Mobify.js載入失敗,那麼腳本會退出,你的網站依然會正常載入。若是圖片調整服務器掛了或者你處於一個開發環境中而且圖片在外網不可訪問,那麼就會載入原始的圖片。)

你也能夠利用完整的文檔。支持上面代碼的瀏覽器以下:全部的基於Webkit/Blink內核的瀏覽器,火狐4+,Opera 11+, and Internet Explorer 10+。

對於咱們大多數的用例來講,自動化調整img元素實在是太棒了。可是,就像演示的奧巴馬的例子,art direction對於某些特定類型的圖片是必要的。 那麼咱們如何才能繼續使用picture元素來支持art direction而不是添加同個圖片的6個版本呢?圖像API一樣會調整picture元素,也就是說你可使用picture元素實現art direction,而把調整大小的功能交給API。

調整 <picture> 元素

固然不一樣的瀏覽器自動化調整圖片大小是可行的,而自動化的art direction確實不可能。picture元素是最可能的方案來實如今不一樣的斷點指定不一樣的圖片,由於它內置的分支邏輯定義語法很健壯(儘管前文也提到了, srcN也提供了很是接近的功能)。不過,爲picture元素添加標記和爲每一個圖片建立6個版本讓人感受十分複雜:

<picture>
    <source srcset="responsive-obama-mobile.png 1x, responsive-obama-mobile-2x.png 2x">
    <source srcset="responsive-obama-512.png 1x, responsive-obama-1024.png 2x" media="(min-width: 512px)">
    <source srcset="responsive-obama-1024.png 1x, responsive-obama-1024.png 2x" media="(min-width: 1024px)">
    <source srcset="responsive-obama-2048.png 1x, responsive-obama-4096.png 2x" media="(min-width: 2048px)">
    <noscript><img src="responsive-obama-512.png"></noscript>
</picture>

當使用圖像API和picture元素相結合, 代碼標記明顯簡化了:

<picture>
    <source src="responsive-obama-mobile.png">
    <source src="responsive-obama.png" media="(min-width: 512px)">
    <img src="responsive-obama.png">
</picture>

這裏的source元素會自動重寫img元素(像前面的例子同樣)。同時,注意上面的標記不須要使用noscript來阻止第二次請求,由於捕捉(Capturing)容許你保留標記的語義。

Mobify.js一樣能用於已變動的picture元素,便於顯式的定義不一樣的斷點須要多寬的圖片,而再也不一覽與設備的寬度。 舉個例子, 你有一張平板電腦一半寬的圖片,那麼根據瀏覽器的最大寬度指定該圖片的寬度所生成的圖片,它會比實際須要的尺寸大一些:

請輸入圖片描述

爲了解決這個問題,圖像API能夠改變picture標記,使得咱們能夠從新設定每一個source元素的寬度,而不是爲每一個判定指定不一樣的src屬性。例如,咱們能夠寫成下面這樣:

<picture data-src="responsive-obama.png">
    <source src="responsive-obama-mobile.png">
    <source media="(min-width: 512px)">
    <source media="(min-width: 1024px)" data-width="512">
    <source media="(min-width: 2048px)" data-width="1024">
    <img src="responsive-obama.png">
</picture>

注意那個使用了data-src屬性的picture元素,這裏定義了一個高分辨率的原始圖片做爲一個起點,這個圖片會被用於其餘斷點的尺寸調整。

它在瀏覽器中是怎麼工做的

  • 若是瀏覽器寬度介於0到511像素(例如一部智能手機), 那麼使用responsive-obama-mobile.png (出於對art
    direction的考慮)。

  • 若是瀏覽器寬度介於512到1023像素,那麼使用responsive-obama.png,由於該媒體查詢下source沒有指定src。自動的決定寬度由於
    data-width沒有指定。

  • 若是瀏覽器寬度介於1024到2047像素,那麼使用responsive-obama.png,
    由於該媒體查詢下source沒有指定src。調整寬度爲512像素,它被定義在data-width屬性中。

  • 若是瀏覽器寬度爲2048像素或更大,那麼使用responsive-obama.png,
    由於該媒體查詢下source沒有指定src。調整寬度爲1024像素,它被定義在data-width屬性中。

  • 若是不支持javascript,恢復爲舊的img標籤。

圖像API會遍歷全部的picture元素,並將它轉變以下:

<picture data-src="http://cdn.mobify.com/mobifyjs/examples/assets/images/responsive-obama-mobile.jpg">
    <source src="//ir0.mobify.com/webp/400/http://cdn.mobify.com/mobifyjs/examples/assets/images/responsive-obama-mobile.jpg">
    <source src="//ir0.mobify.com/webp/400/http://cdn.mobify.com/mobifyjs/examples/assets/images/responsive-obama.jpg" media="(min-width: 512px)">
    <source src="//ir0.mobify.com/webp/512/http://cdn.mobify.com/mobifyjs/examples/assets/images/responsive-obama.jpg" media="(min-width: 1024px)" data-width="512">
    <source src="//ir0.mobify.com/webp/512/http://cdn.mobify.com/mobifyjs/examples/assets/images/responsive-obama.jpg" media="(min-width: 2048px)" data-width="1024">
    <img src="responsive-obama.jpg">
</picture>

Picture polyfill (包含在Mobify.js裏)會被執行而後根據媒體查詢選擇適當的圖片。當瀏覽器廠商支持了原生picture後,它也能運行良好。

這裏有一個使用已變動picture元素標記( the modified picture element ) 的例子,你能夠看看。

不經過捕獲的方式使用圖像API

使用捕獲須要將腳本寫在head標籤裏,這會阻塞javascript的調用而且會推遲資源的初始化下載。該延遲的時間長度在3G連接下大概爲0.5秒(例如dns解析和資源的捕獲和下載),在4G或wifi下耗時會相對少些,大約有60毫秒的延遲。這個延遲的小代價換到的是易於使用,和向下兼容以及語義化。

不經過捕獲的方式使用圖像API來避免阻塞javascript請求,你須要將全部img元素src屬性改成x-src屬性(你也能夠適當的添加noscript編輯來檢測瀏覽器javascript的支持狀況)而且在head標籤內粘貼下面的異步腳本:

<script src="//cdn.mobify.com/mobifyjs/build/mobify-2.0.1.min.js">
<script>
    var intervalId = setInterval(function(){
        if (window.Mobify) {
            var images = document.querySelectorAll('img[x-src], picture');
            if (images.length > 0) {
                Mobify.ResizeImages.resize(images);
            }
            // When the document has finished loading, stop checking for new images
            if (Mobify.Utils.domIsReady()) {
                clearInterval(intervalId)
            }
        }
    }, 100);
</script>

這段腳本會異步載入Mobify.js ,當載入完成後,開始正常載入圖片就像載入html文檔同樣。

在WebApp中使用圖像API

若是你正在使用一個客戶端javascript MVC框架,例如Backbone 或 AngularJS,你仍然可使用Mobify.js的圖像API。首先,將Mobify.js庫包含到你的app中:

<script src="//cdn.mobify.com/mobifyjs/build/mobify-2.0.1.min.js"></script>

而後,使用Mobify.js文檔中列出的方法重寫圖片的URL。

Mobify.ResizeImages.getImageUrl(url)

該方法接受一個絕對URL而後返回圖像調整後的URL。最簡單的方式將圖片傳入這個方法是經過建立 模板輔助函數(template helper) (例如, {{image_resize '/obama.png' }} 在 Handlebars.js)執行了getImageUrl 方法來自動生成圖像的URL。

在後端使用你的圖像尺寸調整(Image-Resizing)

圖像已經被Mobify's Performance Suite 尺寸調整服務器 調整了,它支持自動化調整WebP,CDN緩存等等。不過每月免費調整的數量有一個默認的限制,不過別擔憂,若是你對流量的需求比較大,那麼大聲喊Mobify 一聲,咱們會盡量的給予幫助。該API也容許你使用不一樣的圖像調整服務,例如Sencha.io Src,或是你本身的後端服務。

瀏覽器廠商怎麼樣能更好的支持相應式圖像?

Webkit團隊最近已經實現了src-set屬性,那麼Blink和Gecko也將在不久實現。這是正確道路上前進的一大步,這意味着瀏覽器廠商已經嚴重重視了相應式圖像的問題。然而,它不能解決 art direction問題,也不能避免生成多個分辨率版本文件的步驟。

開發社區最近也一塊兒討論了響應式圖片的問題。其中有一個有趣的提案是Ilya Grigorik提出的客戶端提醒,該提議包括在每一個請求頭部發送設備屬性例如DPR和高度。我喜歡這個方案,由於它能讓咱們繼續使用img標籤,而後在咱們須要建立分支邏輯實現art direction只須要使用picture(或srcN)便可。儘管添加額外的HTTP頭和使用內容協商來解決這個問題已經被證實有效,對與擁有成千上萬圖像的網站來講,使用該方案來定位圖像恐怕不太可行。經過服務層或代理重寫圖像,能夠解決這個問題,不過這兩張方式均可能被設置的有問題。在我看來,若是在客戶端能更好的控制資源加載,那這些個問題咱們是可以處理解決的。

若是開發者更好的控制了資源的加載,那麼響應式圖片應該會被看成一個簡單的問題來處理。之因此如此多的響應圖像解決方案是基於代理實習,是由於在文檔到達瀏覽器以前,圖像必須被重寫。這是對預加載器嘗試儘快下載圖片行爲的適應。不過代理的方案可能在安全性和擴展性方面會有不少問題,若是咱們有一個簡單的方式來和預加載器交互,那麼不少基於代理的方案就顯得冗餘了。

那麼如何在更好的控制資源加載的同時,仍然享受預加載器帶來的好處呢?這裏關鍵點是咱們不但願簡單的關閉預加載器-它並行下載的能力是一個巨大的勝利,而且已經被瀏覽器實現了。(請注意捕獲API不會阻塞並行下載。)其中一個方法是提供一個beforeload事件,該事件在資源載入以前觸發。該事件在safari瀏覽器中經過一個擴展就能夠實現了,在某些瀏覽器中也可使用,不過容量有限。若是如今可以使用該方案的話,那麼就再也不須要捕獲了。下面是一個使用beforeload事件的一個基礎例子:

function rewriteImgs(event) {
    if (event.target === "IMG") {
        var img = event.target;
        img.src = "//ir0.mobify.com/" + screen.width + "/" + img.src;
    }
}
document.addEventListener("beforeload", rewriteImgs, true);

關鍵的挑戰在於以某種方式在執行構圖循環時候(in the main rendering loop)讓預加載器和javascript交互良好。有一個當前正在瀏覽器中開發的一個新系統,叫作 Service Worker,
它的目的是容許開發者截獲網絡請求來協助構建離線web應用。然而,當前的實現不容許攔截初始請求。這是由於載入一個額外的腳本會阻塞其餘資源的載入-不過我相信這個狀況會改變,例如經過不犧牲性能的內聯腳本的方式。若是你有以下的需求,能夠考慮使用 Mobify.js 來自動處理響應式圖片:

總結

響應式圖片方面的問題有許多的解決方案,工做自動化的同時仍然能夠art direction的方案驅動將來的Web開發的解決方案。

  • 只須要爲每一個資源提供一份高版本的圖片,讓API實現提供基於設備條件(寬度,WebP支持狀況等等)的小圖片。
  • 每一個圖片之發生一次請求。
  • 須要100%的語義和向下兼容舊標記而且不須要改變後端(使用捕獲的狀況)。
  • 已經開始使用picture元素來自動調整大小,只專一與使用它來實現art direction。

原文 Automate Your Responsive Images With Mobify.js 做者 Shawn Jansepar

相關文章
相關標籤/搜索