使用高斯模糊的效果逐步加載圖片(仿 Medium)

用過 Medium 的用戶不會不記得它的圖片加載方式——純色-高斯模糊-加載完成並顯示javascript

這是一種很優雅的圖片預加載的方式(由於 Medium 的圖片質量都很高,若是所有一下加載的話,須要的時間不可思議,因此,這是一種很棒的作法)。從第一次打開 Medium 這個網站開始,我就被這種技術給吸引住了——好吧,直到今天才去研究它。css

在 Medium 網站,打開任何一篇文章,而後,咱們來 inspect 一下:html

<figure name="512a" 
        id="512a" 
        class="graf--figure graf--layoutCroppedHeightPreview graf-after--h3"
>
    <div class="aspectRatioPlaceholder is-locked">
        <div class="aspectRatioPlaceholder-fill" 
             style="padding-bottom: 30%;"
        ></div>
        <div class="progressiveMedia js-progressiveMedia graf-image is-canvasLoaded" 
             data-image-id="1*dZnfeZiXxf2BgN3VSQuOlA.jpeg" 
             data-width="3600" 
             data-height="3600" 
             data-scroll="native"
        >
            <img src="https://cdn-images-1.medium.com/freeze/fit/t/60/18/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg?q=20" 
                 crossorigin="anonymous" 
                 class="progressiveMedia-thumbnail js-progressiveMedia-thumbnail"
            >
            <canvas class="progressiveMedia-canvas js-progressiveMedia-canvas" 
                    width="75" 
                    height="22"
            ></canvas>
            <img class="progressiveMedia-image js-progressiveMedia-image" 
                 data-src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg" 
                 src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"
            >
            <noscript class="js-progressiveMedia-inner">
                &lt;img class="progressiveMedia-noscript js-progressiveMedia-inner" src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"&gt;
            </noscript>
        </div>
    </div>
</figure>

能夠看到,Medium 爲每一張圖片都設置了這麼長的一段 HTML。這樣作的目的就是爲了讓這個圖片的逐步加載過程可以平滑如一,同時還能在必定程度上提高用戶體驗。就算圖片沒有加載出來,顯示給用戶的是一個高斯模糊的圖片,其實也不失美感。java

那麼,這個圖片的逐步加載過程具體是什麼樣的呢?git

  1. 渲染一個 div 容器,這個容器就是用來顯示最終展現給用戶的圖片的。經過對容器設置一個百分比的 padding-bottom 來讓其比例和大小與最終圖片的比例和大小相同,這樣,就能避免圖片加載出來的時候致使的頁面的重排;github

  2. 使用 img 標籤來加載一張原圖質量的 10% ~ 20% 左右的圖片,這張圖片的質量很低,並且很小,因此能夠立刻加載出來;npm

  3. 一旦小圖加載完成,就開始使用 canvas 來進行繪製,添加模糊效果,同時,開始請求最終要加載的大圖;canvas

  4. 當最終的大圖也加載完成以後,顯示大圖,隱藏掉 canvas。app

以上就是 Medium 的作法。網站


咱們能夠本身來實現這個效果,實現過程以下:

  1. 渲染一個容器,保持與原圖的比例和尺寸相同,填充一個較淺的背景色;

  2. 先加載小圖,同時使用模糊效果;

  3. 小圖加載完成,開始請求大圖;

  4. 大圖加載完成,顯示大圖,隱藏小圖。

因此,綜合來看,其實並不複雜。

首先,咱們能夠把大圖和小圖的 URL 和尺寸都存起來,經過標籤的 data 屬性去動態獲取。因此,咱們的 HTML 能夠像下面這樣寫:

<figure name="blur"
        class="blur-img-container"
        data-real-width="1174"
        data-real-height="670"
        data-src="images/sm2.jpeg"
        src="https://cdn-images-1.medium.com/max/2000/1*0WwtDkE1q6HGZwD6Kn9SuQ.jpeg"
></figure>

其中各個參數表明的含義是:

  • data-real-width: 大圖的寬度

  • data-real-height: 大圖的高度

  • data-src: 小圖的 URL

  • src: 大圖的 URL

同時,咱們須要定義一些 CSS 的 class 來對大圖和小圖進行處理:

.blur-img-container {
    position: relative;
    background: #eeeeee;
    background-size: cover;
    overflow: hidden;
}

.blur-img-container img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: all 0.4s ease-in-out;
}

.blur-img-container .thumb-loaded {
    opacity: 1;
    filter: blur(10px);
    transform: scale(1);
}

.blur-img-container .large-loaded {
    opacity: 1;
}

.blur-img-container .thumb-hidden {
    opacity: 0;
}

而後,咱們的重點在於 JavaScript 的處理。

  • 須要動態的設置每一個圖片的容器的 padding-bottom 以防止頁面發生重排;

  • 經過 imageonload 事件來控制其樣式和進度

第一點,動態設置咱們的容器的 padding-bottom。能夠經過計算寬高比而後換算成百分比:

elem.style.paddingBottom = `${(realHeight / realWidth) * 100}%`;

第二點,使用圖像的 onload 事件來控制加載的進度:

let thumb = new Image();
thumb.src = thumbSrc;
thumb.onload = () => {
    // 小圖加載完成,顯示小圖,設置樣式
    setStyle(thumb, 'thumb-loaded');
};
elem.appendChild(thumb);

let realImg = new Image();
realImg.src = lgSrc;
realImg.onload = () => {
    // 大圖加載完成,顯示大圖,隱藏小圖
    setStyle(realImg, 'large-loaded');
    setStyle(thumb, 'thumb-hidden');
};

// 將大圖添加到頁面中
elem.appendChild(realImg);

其實,只要把上面兩點主要的功能作好了,咱們的這個效果基本上就實現了。


能夠經過個人 GitHub Repo 來查看完整的源代碼和例子 blur-image

同時,我將這個小功能封裝成了一個 package,須要的朋友能夠經過 npm install blur-image 或者 bower install blur-image 進行安裝和使用。具體的安裝和使用方法能夠查看文檔

參考連接
How Medium does progressive image loading
The 「Blur Up」 Technique for Loading Background Images

相關文章
相關標籤/搜索