用過 medium 的使用者不會不記得它的圖檔加載方式——純色-高斯模糊-加載完成并顯示。
這是一種很優雅的圖檔預加載的方式(因為 medium 的圖檔品質都很高,如果全部一下加載的話,需要的時間難以想象,是以,這是一種很棒的做法)。從第一次打開 medium 這個網站開始,我就被這種技術給吸引住了——好吧,直到今天才去研究它。
在 medium 網站,打開任何一篇文章,然後,我們來 inspect 一下:
<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。這樣做的目的就是為了讓這個圖檔的逐漸加載過程能夠平滑如一,同時還能在一定程度上提升使用者體驗。就算圖檔沒有加載出來,顯示給使用者的是一個高斯模糊的圖檔,其實也不失美感。
那麼,這個圖檔的逐漸加載過程具體是什麼樣的呢?
渲染一個 div 容器,這個容器就是用來顯示最終展示給使用者的圖檔的。通過對容器設定一個百分比的 padding-bottom 來讓其比例和大小與最終圖檔的比例和大小相同,這樣,就能避免圖檔加載出來的時候導緻的頁面的重排;
使用 img 标簽來加載一張原圖品質的 10% ~ 20% 左右的圖檔,這張圖檔的品質很低,而且很小,是以可以馬上加載出來;
一旦小圖加載完成,就開始使用 canvas 來進行繪制,添加模糊效果,同時,開始請求最終要加載的大圖;
當最終的大圖也加載完成之後,顯示大圖,隐藏掉 canvas。
以上就是 medium 的做法。
我們可以自己來實作這個效果,實作過程如下:
渲染一個容器,保持與原圖的比例和尺寸相同,填充一個較淺的背景色;
先加載小圖,同時使用模糊效果;
小圖加載完成,開始請求大圖;
大圖加載完成,顯示大圖,隐藏小圖。
是以,綜合來看,其實并不複雜。
首先,我們可以把大圖和小圖的 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 {
.blur-img-container .thumb-hidden {
}
然後,我們的重點在于 javascript 的處理。
需要動态的設定每個圖檔的容器的 padding-bottom 以防止頁面發生重排;
通過 image 的 onload 事件來控制其樣式和進度
第一點,動态設定我們的容器的 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);
其實,隻要把上面兩點主要的功能做好了,我們的這個效果基本上就實作了。
同時,我将這個小功能封裝成了一個 package,需要的朋友可以通過 npm install blur-image 或者 bower install blur-image 進行安裝和使用。具體的安裝和使用方法可以檢視文檔。
作者:erichain_zain
來源:51cto