前言
本文首發微信公眾號平臺(itclancoder),點選該連結使用SQIP工具和交叉點觀測器進行漸進式影像載入閱讀體驗會更好
在前面一文使用交叉點觀察器延遲載入影像以提高效能中,已經知曉了使用該方式可以提高頁面的訪問速度,那在此基礎上,我們還可以做得更好?,答案顯而易見,如果你爬梯子訪問過一些國外的圖片類的網站,國內若常混跡於知乎,簡書,掘金等社群,你也總會看到比較炫酷的體驗效果,就是很多圖片從非常模糊狀態過渡到清晰的影像,這是怎麼實現的?本文將為你揭曉,在自己的實際開發中,可以嘗試將此skill運用到專案中,如果文中有誤導的地方,歡迎路過的老師多提意見和指正
如果你定期訪問諸如Facebook,Pinterest或Medium等網站,你可能已經注意到,第一次載入頁面時,你將會看到低質量或模糊影像的頁面。然後,隨著頁面繼續載入,模糊/低質量影像將被替換為全質量版本。要看到這個例項的例子,讓我們來看看下面的圖片
上面的圖片是中間載入頁面的螢幕截圖。左側的螢幕截圖顯示了首次載入低質量影像時的頁面,然後右側的螢幕截圖顯示了頁面完成載入後的頁面,並顯示了完整的質量影像
這種影像載入技術被稱為LQIP(低質量影像佔位符),幾年前由Guy Podjarny首次提出。這種技術背後的想法是,在連線速度較慢的情況下,你可以儘快向使用者展示完全可用的網頁,為他們提供更好的體驗。即使在更好的網路連線上,這仍然為使用者提供了更快的可用頁面,並且體驗得到了改善。從網路效能的角度來看,這意味著你的網頁的可用版本將載入得更快,並且(取決於其他因素),你應該有更快的時間來開始有意義的繪製
事實上,在今年的Performance Calendar中,Tobias Baldauf撰寫了一篇關於LQIP載入技術的深度文章,他建立了一個名為SQIP的工具
SQIP是一種建立低質量影像版本的工具,作為SVG可用作佔位符,然後在連線允許時載入完整質量版本。我最近開始嘗試使用SQIP,開始建立低質量版本的影像可能非常有趣
前段時間,我使用Intersection Observer寫了一個影像延遲載入技術。延遲載入影像背後的想法是,你需要等到使用者進一步向下滾動頁面,並在發出網路請求之前將影像放入檢視中。如果你的網頁包含多個影像,但你只能在滾動檢視影像時載入每個影像,則最終會節省頻寬,並確保網頁載入速度更快
這讓我思考;我想知道是否可以將交叉觀察者和使用Tobias的SQIP工具建立的低質量佔位符影像結合起來。一旦我開始進一步嘗試,它比我想象的更容易。使用延遲載入技術將意味著使用者只載入他們在視口中看到的內容,而與低質量影像相結合則意味著雙重網頁效能會帶來麻煩
在這篇文章中,我將通過我所經歷的步驟和您如何開始使用這種技術來談談
目錄:
1. 開始入門(下載安裝go,命令列終端下安裝SQIP工具)
2. 使用交叉點觀察者進行延遲載入(核心js實現)
3. 總結(使用低質量影像佔位符(SQIP)與使用Intersection Observer的延遲載入技術結合使用時,節省頻寬,提升效能)
複製程式碼
開始入門
在我們繼續之前,我們需要安裝SQIP。在我們開箱即用這個工具之前,需要先安裝一些先決條件。首先,您需要安裝Go(百度GO官網下載或者去中文網址下載相應go並安裝,檢測go是否安裝,命令列下輸入go)。一旦安裝Go,你需要安裝一個名為Primitive的工具。使用以下命令可以從終端輕鬆完成此操作
go get -u github.com/fogleman/primitive(可以使用npm安裝npm install -g primitive)
複製程式碼
最後,我們可以使用以下命令安裝SQIP
npm install -g sqip
複製程式碼
我們現在準備開始使用SQIP建立低質量的佔位符影像。我選擇了一張隨機的狗作為我的測試影像(誰不喜歡狗!)。為了處理我們的影像,我們需要在終端中執行以下命令
sqip -o dog.svg dog.jpg
複製程式碼
上述命令將啟動SQIP工具,處理dog.jpg影像並吐出一個名為dog.svg的低質量佔位符檔案。現在新處理的影像看上去有點像以下內容
用SQIP處理完後,該圖片會指定在img標籤的src中
未通過SQIP前,該實際圖片會指定在img標籤的data-src中
左邊的圖片顯示了低質量的SVG版本,右邊的圖片是完整的質量版本。關於這個工具的好處是這個影像的低質量版本只有800位元組 – 令人驚歎,在本地伺服器中可進行測試,我示例中的圖片svg佔900位元組,具體以你自己測試的為準
使用交叉點觀察者進行延遲載入
現在我們有了兩個版本的影像,我們可以開始懶載入它們。如果你以前從未聽說過交叉觀測器,它將內建到大多數現代瀏覽器中,並讓你知道觀察到的元素何時進入或退出瀏覽器的視口。這使得它非常理想,因為它能夠非同步傳遞資料,不會影響主執行緒,使其成為向您提供反饋的有效手段
如果你曾經使用過傳統的圖片延遲載入器,那麼你將會意識到,幾乎所有這些庫都會使用滾動事件或使用定期計時器來檢查元素的邊界,然後再確定它是否在檢視中。這種方法的問題在於,它迫使瀏覽器重新佈局整個頁面,並且在某些情況下會引起相當大的麻煩到你的網站。我們可以使用相交觀測器做得更好
在本文中,我將著重介紹這種延遲載入技術的基礎知識
好吧,讓我們開始吧。設想一個基本的HTML頁面,其中包含三個與上圖類似的影像。在網頁上,你將擁有與以下程式碼類似的圖片元素
<img class="js-lazy-image" src="dog.svg" src="dog.jpg">
複製程式碼
在上面的程式碼中,你可能會注意到影像標籤中有兩個影像源。首先,我們在頁面載入時載入dog.svg影像,這是我們的低質量影像。接下來,我們使用一個名為data-src的資料屬性指向全質量影像源。我們將使用它來儘快替換低質量影像和全面質量的影像。你可能還會注意到,image元素也有一個名為js-lazy-image的類 – 它用於JavaScript程式碼中以確定我們想要延遲載入哪些元素
我建立了一個名為lazyload.js的JavaScript檔案 – 它包含以下程式碼
// Get all of the images that are marked up to lazy load
const images = document.querySelectorAll(`.js-lazy-image`);
const config = {
// 如果影像在Y軸上達到50畫素以內,請開始下載 If the image gets within 50px in the Y axis, start the download.
rootMargin: `50px 0px`,
threshold: 0.01
};
// 頁面上影像的觀察者 The observer for the images on the page
let observer = new IntersectionObserver(onIntersection, config);
images.forEach(image => {
observer.observe(image);
});
}
複製程式碼
上面的例子看起來像很多程式碼,但讓我們一步一步分解它。首先,我選擇頁面上具有js-lazy-image類的所有影像。接下來,我建立一個新的IntersectionObserver,並使用它觀察我們選擇的具有類js-lazy-image的所有影像。
使用IntersectionObserver的預設選項,當元素部分進入檢視並完全離開視口時,你的回撥將被呼叫。在這種情況下,我正在通過一些額外的配置選項到IntersectionObserver。使用rootMargin允許你為根指定頁邊距,從而有效地允許你增大或縮小用於交點的區域。我們希望確保如果影像在Y軸上達到50畫素以內,我們將開始下載
現在我們已經建立了一個交叉點觀察器,並且正在觀察頁面上的影像,我們可以利用交叉點事件,當元素進入檢視時將會觸發
function onIntersection(entries) {
// 迴圈輸入條目 Loop through the entries
entries.forEach(entry => {
// 我們在視口中 Are we in viewport?
if (entry.intersectionRatio > 0) {
// 停止觀看並載入影像 Stop watching and load the image
observer.unobserve(entry.target);
preloadImage(entry.target);
}
});
}
複製程式碼
在上面的程式碼中,只要我們正在觀察的元素進入使用者的視口,就會觸發onIntersection函式。此時,我們可以遍歷我們正在觀察的影像,並確定哪個影像處於視口中。如果當前元素處於相交比中,我們知道該影像位於使用者視口中,我們可以載入它。載入影像後,我們不需要再觀察它,並且使用unobserve()將從交叉觀察者的條目列表中將其刪除。而已!只要使用者滾動並且影像進入檢視,相應的影像就會被載入
如果您想要測試這些程式碼,我已經建立了一個演示頁面,可以在deanhume.github.io/lazy-observer-load找到它。為了讓您更全面地瞭解整個網頁的外觀,讓我們來想象下面的頁面
你會注意到,因為中間影像位於使用者的視口中,所以它被延遲載入,並且低質量影像被替換為全質量影像。視口下方的所有東西(紅線)仍然模糊不清。如果使用者滾動到這些影像,這些影像只會被替換,節省使用者頻寬並確保頁面載入速度更快
如果你正在以快速連線測試此演示,您甚至可能不會注意到影像被換出。我發現最好的測試方法是在Chrome開發人員工具中啟用網路限制並禁用快取
這是示例中簡易的HTML程式碼:
<img class="js-lazy-image centered js-lazy-image--handled fade-in" src="./img/dog-running.jpg" width="400" height="400" src="./img/dog-running.jpg">
<br /><br /><br /><br /><br />
<img class="js-lazy-image centered" src="./img/dog-face.svg" width="400" height="400" src="./img/dog-face.jpg">
<br /><br /><br />
<img class="js-lazy-image centered" src="./img/dog-battersea.svg" width="400" height="400" src="./img/dog-battersea.jpg">
複製程式碼
css程式碼:主要是找到元素新增樣式,居中,動畫淡入效果
.centered {
display: block;
margin: 0 auto;
}
.fade-in {
animation-name: fadeIn;
animation-duration: 1.3s;
animation-timing-function: cubic-bezier(0, 0, 0.4, 1);// 規定動畫的速度曲線,0到1之間的值,4個點描述整個曲線的運動形狀
animation-fill-mode: forwards; // 該屬性規定動畫在播放之前或之後,其動畫效果是否可見,此處規定當動畫完成後,保持最後一個屬性值,設定為動畫的最後一幀的樣式
}
img[Attributes Style] {
width: 400px;
height: 400px;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
複製程式碼
完整的js示例程式碼:
`use strict`;
// 獲取圖片元素
var images = document.querySelectorAll(`.js-lazy-image`),
config = {
rootMargin: `50px 0px`,
threshold: 0.01
},
imageCount = images.length,
observer;
if (!(`IntersectionObserver`in window))
loadImagesImmediately(images);
else {
observer = new IntersectionObserver(onIntersection,config);
for (var image, i = 0; i < images.length; i++)
(image = images[i],
!image.classList.contains(`js-lazy-image--handled`)) && observer.observe(image)
}
function fetchImage(a) {
return new Promise(function(b, c) {
var d = new Image;
d.src = a,
d.onload = b,
d.onerror = c
}
)
}
function preloadImage(a) {
var b = a.dataset.src;
return b ? fetchImage(b).then(function() {
applyImage(a, b)
}) : void 0
}
function loadImagesImmediately(a) {
for (var d, b = Array.from(a), c = 0; c < a.length; c++)
d = a[c],
preloadImage(d)
}
function disconnect() {
observer && observer.disconnect()
}
function onIntersection(a) {
0 === imageCount && observer.disconnect();
for (var c, b = 0; b < a.length; b++)
c = a[b],
0 < c.intersectionRatio && (imageCount--,
observer.unobserve(c.target),
preloadImage(c.target))
}
function applyImage(a, b) {
a.classList.add(`js-lazy-image--handled`),
a.src = b,
a.classList.add(`fade-in`)
}
複製程式碼
這是我在本地伺服器測試的結果
總結
使用低質量影像佔位符(LQIP)確保您的使用者可以更快地獲得可用頁面。當與使用Intersection Observer的延遲載入技術結合使用時,使用者也可以節省頻寬。嘗試SQIP很有趣,其實這種做法就是在首屏載入影像時,以低質量模糊影像載入過渡到清晰影像,在體積上,經過SQIP處理後,與實際圖片比較起來,可以看出容量還更小,更多的做法,從各個網站上看出,他們的處理方式都很類似,頁面上同一張圖片用兩種儲存格式
當觸發某個條件,載入到該圖片時,先載入低質量體積小的圖片,然後快速的被該實際圖片尺寸給替換。至於優化圖片,可以將圖片壓縮,cdn加速,雪碧圖等的.而svg是一種向量圖形,基於畫素儲存資料,而是通過記錄座標的形式儲存圖形資訊。SVG使用基於XML的語義化標籤結構,這有點像HTML。由於是DOM結構,你可以通過ID獲取SVG元素,並操縱它們。這帶來了很多可能性,例如使用JavaScript和CSS 修改並對元素進行動畫操作或者建立響應式圖形,比如阿里的svg圖示等的
至於面試的時候,當回答圖片優化時,圖片選擇(jpg/jpeg,gif,png,TIFF(mac)等),懶載入,壓縮,以及webgl,canvas,以及文中SQIP建立低質量影像版本的工具生成svg格式,個人覺得,回答後者令人要誇目得多
點選該連結使用SQIP工具和交叉點觀測器進行漸進式影像載入閱讀體驗會更好