《前端記錄》圖片懶載入(lazyload)實現原理
懶載入定義
懶載入是一種設計模式,被運用在軟體設計和網頁設計當中,對於網頁介面,其特徵為使用者透過滑鼠,滾動瀏覽頁面,直到頁面下方時,就會自動載入更多內容;有多數網站採用這項網頁設計,例如Google圖片搜尋、Google+、Facebook、Twitter、Pinterest[6]和維基百科的Flow討論系統。也有結合無限滾動和多頁,兩著特性的網頁設計[7]。--來自維基百科
圖片懶載入的好處
1.首先它能提升使用者的體驗,試想一下,如果開啟頁面的時候就將頁面上所有的圖片全部獲取載入,如果圖片數量較大,對於使用者來說簡直就是災難,會出現卡頓現象,影響使用者體驗。
2.有選擇性地請求圖片,這樣能明顯減少了伺服器的壓力和流量,也能夠減小瀏覽器的負擔。
圖片懶載入的實現原理
頁面在瀏覽器中開啟,img標籤的src屬性會自動請求其圖片地址,實現懶載入我們先要使用自定義屬性(例:data-src)來避免img的自動載入,也相當於把圖片地址儲存在當前標籤裡。然後js監聽父元素scroll事件,當此圖片出現在可視區域後,js把自定義src設定為圖片src。
接下來讓我們看一下基於jQuery的實現
$.fn.lazyload = function (options) { var defaults = { // 在html標籤中存放的屬性名稱; attr: "data-url", // 父元素預設為window container: window, callback: $.noop }; // 不管有沒有傳入引數,先合併再說; var params = $.extend({}, defaults, options || {}); // 把父元素轉為jquery物件; var container = $(params.container); // 新建一個數組,然後呼叫each方法,用於儲存每個dom物件相關的資料; params.cache = []; $(this).each(function () { // 取出jquery物件中每個dom物件的節點型別,取出每個dom物件上設定的圖片路徑 var node = this.nodeName.toLowerCase(), url = $(this).attr(params["attr"]); //重組,把每個dom物件上的屬性存為一個物件; var data = { obj: $(this), tag: node, url: url }; // 把這個物件加到一個數組中; params.cache.push(data); }); console.log(params) var callback = function (call) { if ($.isFunction(params.callback)) { // params.callback.call(call); } }; let canRun = true; //每次觸發滾動事件時,對每個dom元素與container元素進行位置判斷,如果滿足條件,就把路徑賦予這個dom元素! var loading = function () { if(!canRun){ return } canRun = false; // 獲取父元素的高度 var contHeight = container.outerHeight(); var contWidth = container.outerWidth(); // 獲取父元素相對於文件頁頂部的距離,這邊要注意了,分為以下兩種情況; if (container.get(0) === window) { // 第一種情況父元素為window,獲取瀏覽器滾動條已滾動的距離;$(window)沒有offset()方法; var contop = $(window).scrollTop(); var conleft = $(window).scrollLeft(); } else { // 第二種情況父元素為非window元素,獲取它的滾動條滾動的距離; var contop = container.offset().top; var conleft = container.offset().left; } console.log('contHeight:'+contHeight,'contWidth:'+contWidth,'contop:'+contop,'conleft:'+conleft); $.each(params.cache, function (i, data) { var o = data.obj, tag = data.tag, url = data.url, post, posb, posl, posr; if (o) { //物件頂部與文件頂部之間的距離,如果它小於父元素底部與文件頂部的距離,則說明垂直方向上已經進入可視區域了; post = o.offset().top - (contop + contHeight); //物件底部與文件頂部之間的距離,如果它大於父元素頂部與文件頂部的距離,則說明垂直方向上已經進入可視區域了; posb = o.offset().top + o.height() - contop; // 水平方向上同理; posl = o.offset().left - (conleft + contWidth); posr = o.offset().left + o.width() - conleft; console.log(post,posb,posl,posr) // 只有當這個物件是可視的,並且這四個條件都滿足時,才能給這個物件賦予圖片路徑; if (o.is(':visible') && (post < 0 && posb > 0) && (posl < 0 && posr > 0)) { if (url) { //在瀏覽器視窗內 if (tag === "img") { //設定圖片src callback(o.attr("src", url)); } else { // 設定除img之外元素的背景url callback(o.css("background-image", "url(" + url + ")")); } } else { // 無地址,直接觸發回撥 callback(o); } // 給物件設定完圖片路徑之後,把params.cache中的物件給清除掉;物件再進入可視區,就不再進行重複設定了; data.obj = null; } } }); setTimeout(()=>{ canRun = true; },300) }; //載入完畢即執行 loading(); //滾動執行 container.bind("scroll", loading); }; $("img").lazyload({attr:'data-src'})
我們可以看到呼叫lazyload方法後傳入自定義src引數,我們會先遍歷當前jQuery選中的元素並取出自定義src的value、nodeName、this指標然後儲存到cache數組裡,接下來建立loading函式,重點來了我們怎麼判斷當前圖片元素是否在可視區域:
- 1.先取出父元素的高度和當前距頂部的距離;
- 2.迴圈cache陣列,獲取每個元素的this;
-
3.計算
post = 元素距離頂部的距離 - (父距頂部的距離+父可視高度)
posb = 元素距離頂部的距離 + 元素的高度 - 父距頂部的距離; - 4.判斷如果post < 0 && posb > 0 我們就是在垂直方向看到了此元素;
- 5.可以在此判斷下根據指標將自定義src替換為src;
然後我們把父元素繫結scroll事件監聽函式為loading,這樣每次滾動loading函式都會進行計算把位於次區域的圖片展示出來。
上面我們分析的是垂直方向判斷 水平方向同理,還有一點我們可以看到我們在上面的實現中使用了簡單的函式節流,本文不再過多贅述。
接下來我還將要寫一些vue-lazyload的原始碼分析等,有興趣可以關注我。