圖片流量優化

2018.3.1更:

有贊·微商城(base杭州)部門招前端啦,最近的前端hc有十多個,跪求大佬扔簡歷,我直接進行內推實時反饋進度,有興趣的郵件 lvdada#youzan.com,或直接微信勾搭我 wsldd225 瞭解跟多html

有贊開源組件庫·zanUI前端


刷新一個頁面消耗的流量除了腳本樣式文件之外,大頭其實在下載的圖片。一張圖片動輒幾十kb,想盡辦法優化樣式、腳本文件所優化的圖片流量其實還不如一張圖片大。html5

本文從兩個角度介紹如何對圖片流量進行優化。本文進行圖片流量優化的前提都是對於移動端而言。java

webp

首先從圖片格式方面着手,webp(google官方網址)是谷歌推出的一種圖片格式,優勢在於同等畫面質量下,體積比jpg、png少了25%以上。以兩張jpg、png圖片爲例:ios

  1. JPG http://cdn1.showjoy.com/images/c9/c9c2221942774550ad53342da23774de.jpg
  2. PNG http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png
size JPG PNG
無壓縮 165kb 55kb
tinypng壓縮 75kb 20kb
webp轉換 54kb 6.1kb

由表格的羅列可知,將圖片轉換爲webp格式,圖片的體積比tinypng壓縮完後的體積還要小,且圖片質量甚至還要高於tinypng壓縮。nginx

雖然webp格式的圖片相對於png和jpg體積小質量高,可是目前的兼容性在全球範圍只達到了70%左右。(caniuse截止20160911)git

根據caniuse,目前在移動端安卓機型4.4以上所有支持,可是ios全軍覆滅。我司用戶ios、安卓55分紅,支持了webp至少能爲一半用戶提供更小體積的圖片體驗。並且聽說ios10系統將支持webp,這樣一來我司產品的webp支持度將會更高。ios10有望支持webpgithub

圖片服務器支持webp轉換

我司本來就有基於nginx+lua+graphicsmagick的圖片縮略圖功能,具體使用方法相似web

http://cdn1.shwojoy.com/images/34/xxxxx.png
http://cdn1.shwojoy.com/images/34/xxxxx.png.300x300.png瀏覽器

爲了支持webp轉換須要修改原先的lua腳本,添加對.webp後綴的識別。使之能對相似xxxxx.png.300x300.png.webp或者xxxxx.png.webp這樣的域名進行識別並轉換。

nginx+lua+graphicsmagick這套方案其實作的事情就是nginx對域名進行攔截,lua腳本進行域名後綴規則的匹配,好比說300x300.png/.webp相似的後綴,匹配完成後再在lua裏調用graphicsmagick的命令,進行一些圖片轉換、裁剪等工做。

lua腳本片斷

if table.isLegal(size_list) and extend == "webp" then
        command = [[/usr/local/GraphicsMagick-1.3.25/bin/gm convert -quality 75 -density 72 +profile "*"  ]] .. ngx.var.image_root ..  originalUri  .. " -geometry " .. area .. " " .. ngx.var.file;
        os.execute(command);
end

值得注意的是graphicsmagick版本在1.3.20及以上才支持webp download

graphicsmagick能作到轉換webp還須要下載編譯libwebp。graphicsmagick支持webp教程

圖片服務器支持webp轉換後,就能實時轉化webp格式的圖片了,爲接下來的webp兼容方案提供了技術支持。

  • http://cdn1.showjoy.com/images/c9/c9c2221942774550ad53342da23774de.jpg
  • http://cdn1.showjoy.com/images/c9/c9c2221942774550ad53342da23774de.jpg.webp

webp兼容方案

目前在瀏覽器端判斷是否支持webp最好的方法就是特性檢測法。根據檢測結果將是否支持webp的值存入cookie,供以後須要判斷webp兼容性的地方使用。

特性檢測腳本:(參考)

;(function(doc) {
    // 給html根節點加上webps類名
    function addRootTag() {
        doc.documentElement.className += " webpa";
    }

    // 判斷是否有webp_showjoy=available這個cookie
    if (!/webp_showjoy=available/.test(document.cookie)) {
        var image = new Image();

        // 圖片加載完成時候的操做
        image.onload = function() {

            // 圖片加載成功且寬度爲1,那麼就表明支持webp了,由於這張base64圖是webp格式。若是不支持會觸發image.error方法
            if (image.width == 1) {

                // html根節點添加class,而且埋入cookie
                addRootTag();
                document.cookie = "webp_showjoy=available; max-age=31536000; domain=";
            }
        };

        // 一張支持alpha透明度的webp的圖片,使用base64編碼
        image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==';
    } else {
        addRootTag();
    }
}(document));

若是瀏覽器支持webp格式的圖片則在cookie中設置標誌,而且在html標籤上設置webpaclassName,這個classname的做用是在less文件中兼容webp圖片。

less文件中的webp兼容

.webpbg(@url) {
  background-image: url(@url);
  .webpa & {
    background-image: url('@{url}.webp');
  }
}

這段less取代了原先在less文件中描述背景圖片的代碼。

好比原先定義背景圖片

div {
    background-image: url(xx);
}

如今使用兼容方案則爲:

div {
    webpbg(xxx);
}

html文件中的webp圖片兼容

我司目前html部分經過java的velocity編寫,對於同步傳遞到頁面上的圖片webp將經過以下方式兼容。

<img class="slider-img" src="$!{banner.recordMap.get('圖片地址').value}.750x448.jpg$!{isWebp}">

$!{isWebp} 變量是後臺經過判斷瀏覽器請求的cookie中是否有以前定義的webp_showjoy=available,有則返回.webp後綴,無則返回空。

這樣有個問題是用戶第一次瀏覽產品頁面時後臺判斷cookie永遠是false,因此用戶第一次瀏覽是不可能返回.webp後綴的圖片的。

還有一種狀況是圖片大量使用的時候咱們會使用懶加載進行圖片的延遲加載。這時就能夠修改懶加載插件,在插件裏動態兼容webp圖片了。

/* 根據cookie返回圖片是否webp的地址 */
function getwebpsrc (imgsrc) {
    var needwebp = false,
        src = '';
    if (/webp_showjoy=available/.test(document.cookie)) {
        needwebp = true;
    }
    src = needwebp ? imgsrc + '.webp' : imgsrc;
    return src;
}

到此就完成了移動端對webp格式圖片的支持。這也是圖片流量優化其中之一方案。

retina兼容圖片流量優化

前端應該都瞭解在retina屏下應該使用@2x或者@3x等倍率的圖片,才能保證圖片的清晰度。可是爲了切圖方便,部分公司都會統一在切圖階段切出@2x的圖片,無論瀏覽設備是retina屏仍是普通屏,一概都使用@2x的圖片。這樣作有兩個壞處,一是@2x的圖片在非retina屏下會出現downsampled現象,雖然不會影響清晰度,可是會缺乏一些銳利度。二是@2x的圖片相比較@1x的圖片,前者體積大於後者,這也就形成了流量的浪費以及影響頁面打開性能。

因此正確處理方法是針對retina屏的是否採用不一樣尺寸的圖片。圖片裁剪已經在圖片服務器上實現。在考慮retina時也須要加上webp的兼容,二者一塊兒做用會大大減小圖片的尺寸。

@2x http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png.300x300.png.webp
@1x http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png.150x150.png.webp

less文件兼容retina

.retinabg(@file-2x; @reg-2x; @reg-1x; @type) when (isstring(@reg-2x)) {

  background-image: url("@{file-2x}.@{reg-1x}.@{type}");
  .webpa & {
    background-image: url('@{file-2x}.@{reg-1x}.@{type}.webp');
  }
  @media
  only screen and (-webkit-min-device-pixel-ratio: 2),
  only screen and (   min--moz-device-pixel-ratio: 2),
  only screen and (     -o-min-device-pixel-ratio: 2/1),
  only screen and (        min-device-pixel-ratio: 2),
  only screen and (                min-resolution: 192dpi),
  only screen and (                min-resolution: 2dppx) {
    background-image: url("@{file-2x}.@{reg-2x}.@{type}");
    .webpa & {
      background-image: url('@{file-2x}.@{reg-2x}.@{type}.webp');
    }
  }
}

當要代替原先書寫時的background-image: url(),能夠寫成以下方式:

.retinabg(http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png; 300x300; 150x150; png)

html文件兼容retina

在html中本次方案准備使用html5特性srcset屬性。

secset屬性的目的在於容許開發者爲某個圖片的屬性指定一系列的來源,其中這些圖片的來源是要根據客戶端顯示屏的像素分辨率來設定的。

好比在volecity模板中定義圖片資源:

<img class="pic" src="$!{newProduct.image}.300x300.png$!{isWebp}" srcset="$!{newProduct.image}.150x150.png$!{isWebp} 1x, $!{newProduct.image}.300x300.png$!{isWebp} 2x">

這段圖片定義說明了在retina(2x)屏幕下使用300x300的圖片來源,而在飛retina屏下(1x)下使用150x150的圖。

html中懶加載的圖片

因爲懶加載的圖片在插件中就會進行src賦值的操做,因此直接在懶加載插件中根據window.devicePixelRatio進行判斷修改圖片url。

/* 根據cookie返回圖片是否webp的地址 */
/* 根據dpr返回不一樣尺寸的圖片 */
function getwebpsrc (imgsrc) {
    var areaInfo = '';
    if (window.devicePixelRatio && window.devicePixelRatio <= 1) {
        var area = imgsrc.match(/[0-9]+x[0-9]+/);
        if (area) {
            var areaSplit = area[0].split('x');
            areaInfo = areaSplit[0] /2 + 'x' + areaSplit[1] /2;
            imgsrc = imgsrc.replace(/[0-9]+x[0-9]+/, areaInfo)
        }
    }
    var needwebp = false,
        src = '';
    if (/webp_showjoy=available/.test(document.cookie)) {
        needwebp = true;
    }
    src = needwebp ? imgsrc + '.webp' : imgsrc;
    return src;
}

在這個方案下全部圖片的編寫都必需要帶上尺寸後綴(_num_x_num_),在我司圖片服務器支持下還有一個特別的好處:圖片服務器會對帶有尺寸後綴的圖片進行尺寸裁剪的同時進行圖片壓縮,遇到不支持webp格式的瀏覽器上就會順帶對圖片進行壓縮,盡力地減少圖片體積。

srcset屬性目前在移動端的兼容性十分不錯,安卓4.x版本不支持。

總結

一共兩點圖片流量優化方案,一是針對webp圖片格式,二是針對retina。最後總結下來的編寫圖片代碼的情景就爲3種:

  1. html同步圖片編寫

    <img class="pic" src="$!{newProduct.image}.300x300.png$!{isWebp}" srcset="$!{newProduct.image}.150x150.png$!{isWebp} 1x, $!{newProduct.image}.300x300.png$!{isWebp} 2x">
  2. 懶加載圖片編寫

    <img class="goods-pic j_Lazyload" data-original="{{$value.image}}.300x300.png" src="http://cdn1.showjoy.com/images/a5/a560e106324d4670acd11b69aee0f11f.png">
  3. less

    .retinabg(http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png; 300x300; 150x150; png)
    
    webpbg(http://cdn1.showjoy.com/images/bb/bb1c8b0d275e4ba2903dc822a03add50.png);

對我以上的理解有疑問和意見的歡迎找我私聊~微博-寫前端的暹羅

相關文章
相關標籤/搜索