這篇文章也發在我的部落格,歡迎圍觀?
Translated by FrankZhang.
如有錯誤,敬請不吝告知,謝謝!
在以前的一篇文章中,我仔細分析了Medium.com用來展示圖片的技術,這種技術可以在展示圖片的時候讓圖片從一張模糊的圖片過渡到一張清晰的圖片。從那以後,我找到了其他一些同樣採用相似方式展示圖片的網站。下面讓我們來看一下Quora, Quartz 以及 Clicktorelease 是如何實現的。
快速導讀
圖片是網頁中最大的靜態資源,他們通常會佔據三分之二的空間。圖片優化和選擇正確的圖片格式是至關重要的。另外,有些網站會在圖片沒有載入的時候,使用小的縮圖當作佔位符。這種技術通常也被稱作“blur-up“。這種技術主要是為了提升使用者感知效能,如果搭配懶載入使用,還可以達到節省流量的效果。
當然這項技術還是有其他可替代的技術的。其中一種是使用一張單獨的JPEG圖片,調整它的掃描指令碼使得可以快速渲染出一個原始的畫素化的版本,並且在後續掃描中逐步渲染出最終清晰的圖片。我推薦你通讀Progressive JPEGs and green Martians 去更瞭解這項技術。
還有一點,如果你喜歡圖片載入優化這方面的內容,可以關注Cloudinary’s Blog,裡面有很多關於這方面話題的博文。
Quartz
看一下下面的這個視訊,它記錄著當使用慢速的網路訪問QZ.com的網頁時,Quartz's 的方式按照下面的步驟實現:
- 請求一個小的用於被模糊的
<img />
。在這種情況下,他們使用寬度為50px,質量為80%的圖片。 - 通過CSS filter應用模糊效果。
- 請求一個大的圖片。
關於Quartz的一個有趣的地方在於,他們使用響應式圖片的語法去標記指定大圖片。最大的不同點在於他們使用data-srcset
屬性,這樣他們就可以控制何時發起請求並且防止瀏覽器在標記被解析之後去請求大尺寸的圖片。
我發現這種方式非常優雅,因為他們嘗試遵循“標準”的方式去處理圖片,就是通過增加額外的欄位以實現懶載入和實現過度動畫。
<figure class="progressive-image featured-image size-extra-large">
<picture style="padding-bottom: 56.1875%; background-image: url(https://qzprod.files.wordpress.com/2017/01/fish.jpg?quality=80&strip=all&w=50);">
<img src="https://qzprod.files.wordpress.com/2017/01/fish.jpg?quality=80&strip=all&w=50">
<img alt="INDIA-fishermen" />
<noscript>
<img src="https://qzprod.files.wordpress.com/2017/01/fish.jpg?quality=80&strip=all&w=320" alt="INDIA-fishermen">
</noscript>
</picture>
<figcaption>...</figcaption>
</figure>
複製程式碼
Quora
Quora也是在他們的po文裡面實現了圖片blur-up技術。例子詳見head to this page。
這裡我們可以看到當顯示模糊佔點陣圖片的時候頁面的樣子。
讓我們深入程式碼來看看到底時如何實現的。首先,讓我們來看看HTML標記:
<div>
<canvas class="qtext_image_placeholder portrait qtext_image zoomable_in zoomable_in_feed"
width="499"
height="874"
data-src="data:image/PNG;base64,UklGRmgAAABXRUJQVlA4IFwAAADwAQCdASoGAAoAAUAmJYgCdEf/g…iD0z/yA/5ipcuk5xHSdrS38j8CkH7s+vKeZu9EwRy0f/KPIlo/+UifdfcpiRcJiRnXXAAAAA==">
</canvas>
<img src />
</div>
複製程式碼
為什麼同時使用data-src
和master-src
?master_src
圖片是當點選圖片獲取更大尺寸時載入的那張圖片。
Quora沒有使用模糊效果。他們使用canvas.drawImage()
在canvas裡渲染小的縮圖。你可以看到通過檢視他們的程式碼發現這個方法,在捆綁在主要JS檔案的shared/lazy_load_images
模組中可以看到:
// draw the data-uri image on the canvas
function a(e) {
if (e.getAttribute("data-src")) {
var t = new Image;
t.src = e.getAttribute("data-src");
var i = e.getAttribute("width")
, n = e.getAttribute("height")
, o = e.getContext("2d");
t.addEventListener("load", function() {
o.drawImage(t, 0, 0, i, n)
}, false)
}
}
// go through all canvas on the page with a data-src
function r() {
var e = document.querySelectorAll("canvas[data-src]");
d.insertionQ("canvas[data-src]").every(function(e) {
a(e)
}),
[].forEach.call(e, a)
}
複製程式碼
簡單來說,Quora所使用的技術主要包括:
- 在canvas中渲染一個內聯的非常小的png。
- 使用Webp(如果支援的話)或者其他格式請求大尺寸圖片。
- 將大尺寸圖片設定為
<img />
元素的資源,然後逐漸變化它的透明度並且隱藏canvas。
Clicktorelease
Clicktorelease.com, @thespite,剛剛重新設計過,也為圖片設定了懶載入。
這裡最重要的部分是縮圖並不是要載入的圖片本身,而是縮圖DCT矩陣的值,這使得負載非常非常小。
載入圖片的步驟具體如下:
- 請求
thumb-src
圖片。這是一個16x4的PNG 縮圖,通常大小在300B左右。 - 建立一個
<canvas />
並且在上面繪製通過反向計算縮圖的DCT得到的圖片。 - 使用
original-src
地址請求大尺寸的圖片。如果瀏覽器支援webp格式,那麼“.webp”就會被新增到src上,這樣就會請求webp格式的圖片。
原始的標記大概是這樣的:
<div>
<div>
<div>
<noscript><img src="/images/graphical-web-2016.jpg"></noscript>
</div>
</div>
複製程式碼
我認為這是一個非常好關於漸進增強的例子,提供了<noscript>
作為替代,並且逐步疊加效果,從一個固定的背景圖片,到一個模糊的圖片,再到最後的支援webp的大尺寸的圖片。
總結
看到實現漸進式圖片載入的這些微小的變化非常有趣,其中有使用Javascript實現的,有基於響應式圖片實現的,有使用CSS filters實現的,還有使用canvas實現的。
如果你想要一個基礎的例子,可以移步my “Reproducing Medium loading image effect” pen。如果你也看到其他的網站實現了同樣的技術,請記得一定要告訴我。
相關內容
我在我的RenderConf 2017的演講Progressive Image Rendering中涉及到這個話題,當然這中間還一併提到其他一些可以使得圖片更高效載入的技術。
我也寫過關於使用SVG去建立佔位符的方法,詳見Using SVG as placeholders — More Image Loading Techniques。