English version:Progressive Image Loading like Medium
圖片延遲載入的 3 種方式
第一次知道有 Medium 這平台是上一份工作的時候,應該是 3 年前。當時看到他們文章內圖片加載是從霧到清晰的這個效果,有驚為天人的感覺。
當時只覺得這技術很厲害,因為那時對 JS 不熟,所以認為一定是什麼高科技技術。
最近在找資料時很常找到 Medium 上有人寫的文章,才又想起了這功能。
3 年後,我發現自己大概知道可以怎麼寫了,在一陣搜尋後看到了這篇:
Page Load Optimization by Progressive Image Loading (like Medium)
裡面說到圖片延遲載入有 3 種方式:
- 常見的 lazyload,就是滑到某個區塊後,那個區塊的圖片才開始載入。
- 像 Medium 的這種,其實跟 lazyload 很像,不一樣的是會先用一個佔位圖片放著,讓使用者以為圖片是慢慢讀取進來。
- Facebook 的那種,不過文章中沒說明 FB 是怎麼用的。
更驚人的是,文章中測試了一下 lazyload 跟 Medium 這兩個方式,Medium 的這種讀取速度更快。
看到這個比較,就想來寫一套自己可以用的圖片延遲載入。
Medium 漸進式載入的 3 個步驟
文章中作者也有提供他們的寫法,不過最近手上一個案子剛好有用背景圖去處理不同尺寸圖片的問題,就想用自己的方式去寫一個。
Medium 的這種漸近式讀圖,看上去很複雜,但看完文章後再思考一下,其很簡單,就三個步驟:
- 將原圖另外存一份寬度縮小到 10px,品質降成 10% 的小小圖,然後整個拉大撐滿原本要放圖片的地方,這種小小圖的大小不到 1kb。如果再狠一點,這個小小圖直接改放 base64,可以減少請求。
- 寫一段監聽滾動時的事件,當頁面捲動到圖片的區塊,就執行讀取原圖的功能,這邊直接使用套件「Waypoints」。
- 原圖讀完後,先把透明度調成 0,塞進有背景圖的區塊裡,因為區塊大小一開始就跟圖大小一樣,因此圖放上去後就會塞滿區塊。圖片放進去後,等個 50 毫秒就加入把透明度調回為 1 的 class,圖片就會有淡入效果。
以上就是思考過程,以下開始進入程式碼。
Medium 漸進式載入,實作程式碼部份
縮小圖的部份,用 Photoshop 來處理。
背景圖撐滿 div
,還可以等比例縮放,這邊用的是一個 CSS 的特性:
Vertical padding is relative to element’s width not height.
簡單來說,就是 padding-top
、padding-bottom
這兩個值如果用的單位是百分比(%),那它參考的是這個 div
的寬而不是高。
Bootstrap 的 Responsive embed 就是用這個特性去寫的。
以下是圖片區塊 div
的 HTML:
以下是我對背景圖撐滿 div
的 Sass 寫法:
因為 background-image
直接寫進 HTML: 裡了,所以 CSS 就處理 size 跟 position 這兩個即可。
要特別注意的是 height
要設成 0。
16:9、4:3 的比例,是 (16/9) * 100 來計算的,如果有圖片比例不是這兩種的情形,就改用 JS 去抓寬高後在丟進公式裡去計算就行。
接著是重要的 JS 了,直接引用了 jQuery,寫法上也用 ES6,如下:
第一步是先處理圖片不是 16:9 或 4:3 的狀況,會需要後端大大丟圖時一併丟圖的寬高,JS 才能抓值去計算 padding-bottom
的百分比。
第二步就是用了 Waypoints 這個套件,去監聽各個圖片區塊進入畫面了沒。
為了防止圖片區塊每一次滑動到都會執行功能,在最後要加上 destory()
把他取消掉。
第三步就是建立一個圖片物件,然後 append 進 div
中了。
原始檔放在 Github 上:
https://github.com/letswritetw/letswrite-medium-lazyload
實測頁面讀取時間
實際把頁面用 Pindom 去測試後,頁面讀取時間真的加快了。
下面這張是一般直接讀取圖片的數據,4.57 秒:
下面這張是用了延遲載入後的數據,降到 2.02 秒:
後來把功能 Demo 給了後端看,問說需不需要,得到的回應是,似乎大部份直接用 lazyload 就可以有效減少頁面讀取時間了,所以需求性沒很高。
覺得像 Medium 這種功能是對整體使用者經驗有特別要求的案子才會用到。
不過,也算是圓了 3 年前的夢,至少現在的我是有能力寫出來的。
頁面 Demo:像 Medium 的漸近式圖片加載
請問那個可以測量圖片速度的網站是哪個?
當時是用Pindom:https://www.pingdom.com/
不過這個站好像要收費了。