日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

什么是延時加載?

圖片延遲加載也稱 “懶加載”,通常應(yīng)用于圖片比較多的網(wǎng)頁

為什么要使用延時加載?

假如一個網(wǎng)頁中,含有大量的圖片,當(dāng)用戶訪問網(wǎng)頁時,那么瀏覽器會發(fā)送n個圖片的請求,加載速度會變得緩慢,性能也會下降。如果使用了延時加載,當(dāng)用戶訪問頁面的時候,只加載首屏中的圖片;后續(xù)的圖片只有在用戶滾動時,即將要呈現(xiàn)給用戶瀏覽時再按需加載,這樣可以提高頁面的加載速度,也提升了用戶體驗。而且,統(tǒng)一時間內(nèi)更少的請求也減輕了服務(wù)器中的負擔(dān)。

延時加載的原理

基本原理就是最開始時,所有圖片都先放一張占位圖片(如灰色背景圖),真實的圖片地址則放在 data-src 中,這么一來,網(wǎng)頁在打開時只會加載一張圖片。

然后,再給 window 或 body 或者是圖片主體內(nèi)容綁定一個滾動監(jiān)聽事件,當(dāng)圖片出現(xiàn)在可視區(qū)域內(nèi),即滾動距離 + 窗體可視距離 > 圖片到容器頂部的距離時,將講真實圖片地址賦值給圖片的 src,否則不加載。

使用原生js實現(xiàn)圖片的延時加載

延時加載需要傳入的參數(shù):

var selector = options.selector || 'img',
 imgSrc = options.src || 'data-src',
 defaultSrc = options.defaultSrc || '',
 wrApper = options.wrap || body;

其中:

  • wrapper :延時加載的容器。在該容器下,所有符合圖片選擇器條件的圖片均會延時加載。
  • selector :圖片選擇器。表示需要延遲加載的圖片的選擇器,如 img.lazyload-image ,默認為所有的 img 標簽。
  • imgSrc :圖片真實地址存放屬性。表示圖片的真實路徑存放在標簽的哪個屬性中,默認為 data-src。
  • defaultSrc :初始加載的圖片地址,默認為空,當(dāng)為空時,不處理延時加載的圖片的路徑,若圖片本身沒有路徑,則顯示為空。
  • 獲取容器中所有的圖片。
function getAllImages(selector){
 return Array.prototype.concat.apply([], wrapper.querySelectorAll(selector));
}

該函數(shù)在容器中查找出所有需要延時加載的圖片,并將 NodeList 類型的對象轉(zhuǎn)換為允許使用 map 函數(shù)的數(shù)組。

如果設(shè)置了初始圖片地址,則加載。

function setDefault(){
 images.map(function(img){
 img.src = defaultSrc;
 })
}

給 window 綁定滾動事件

function loadImage(){
 var nowHeight = body.scrollTop || doc.documentElement.scrollTop;
 console.log(nowHeight);
 if (images.length > 0){
 images.map(function(img, index) {
 if (nowHeight + winHeight > img.offsetTop) {
 img.src = img.getAttribute(imgSrc);
 images.splice(index, 1);
 }
 })
 }else{
 window.onscroll = null;
 }
}
window.onscroll = loadImage();

每次滾動網(wǎng)頁時,都會遍歷所有的圖片,將圖片的位置與當(dāng)前滾動位置作對比,當(dāng)符合加載條件時,將圖片的真實地址賦值給圖片,并將圖片從集合中移除;當(dāng)所有需要延時加載的圖片都加載完畢后,將滾動事件取消綁定。

測試是否可行

測試結(jié)果:

從chrome的網(wǎng)絡(luò)請求圖中可見,5張圖片并不是在網(wǎng)頁打開的時候就請求了,而是當(dāng)滑動到某個區(qū)域時才觸發(fā)加載,基本實現(xiàn)了圖片的延時加載。

測試結(jié)果

性能調(diào)整

上述只是簡單的實現(xiàn)了一個延時加載的 demo,還有很多地方需要調(diào)整和完善。

調(diào)整 1:onscroll 函數(shù)可能會被覆蓋

問題:

因為有時候頁面需要滾動無限加載時,插件會重寫 window 的 onscroll 函數(shù),從而導(dǎo)致圖片的延時加載滾動監(jiān)聽失效。

解決辦法:

需要更改為將監(jiān)聽事件注冊到 window 上,移除時只需要移除相應(yīng)的事件即可。

調(diào)整后的代碼

function bindListener(element, type, callback){
 if (element.addEventListener) {
 element.addEventListener(type, callback);
 }else if (element.attachEvent) {
 //兼容至 IE8
 element.attachEvent('on'+type, callback)
 }else{
 element['on'+type] = callback;
 }
}
function removeListener(element, type, callback){
 if (element.removeEventListener) {
 element.removeEventListener(type, callback);
 }else if (element.detachEvent) {
 element.detachEvent('on'+type, callback)
 }else{
 element['on'+type] = callback;
 }
}
function loadImage(){
 var nowHeight = body.scrollTop || doc.documentElement.scrollTop;
 console.log(nowHeight);
 if (images.length > 0){
 images.map(function(img, index) {
 if (nowHeight + winHeight > img.offsetTop) {
 img.src = img.getAttribute(imgSrc);
 images.splice(index, 1);
 }
 })
 }else{
 //解綁滾動事件
 removeListener(window, 'scroll', loadImage)
 }
}
//綁定滾動事件
bindListener(window, 'scroll', loadImage)

調(diào)整2:滾動時的回調(diào)函數(shù)執(zhí)行次數(shù)太多

問題

在本次測試中,從動圖最后可以看到,當(dāng)滾動網(wǎng)頁時,loadImage 函數(shù)執(zhí)行了非常多次,滾輪每向下滾動 100px 基本上就要執(zhí)行 10 次左右的 loadImage,若處理函數(shù)稍微復(fù)雜,響應(yīng)速度跟不上觸發(fā)頻率,則會造成瀏覽器的卡頓甚至假死,影響用戶體驗。

解決辦法

使用 throttle 控制觸發(fā)頻率,讓瀏覽器有更多的時間間隔去執(zhí)行相應(yīng)操作,減少頁面抖動。

調(diào)整后的代碼:

//參考 `underscore` 的源碼
var throttle = function(func, wait, options) {
 var context, args, result;
 var timeout = null;
 // 上次執(zhí)行時間點
 var previous = 0;
 if (!options) options = {};
 // 延遲執(zhí)行函數(shù)
 var later = function() {
 // 若設(shè)定了開始邊界不執(zhí)行選項,上次執(zhí)行時間始終為0
 previous = options.leading === false ? 0 : _now();
 timeout = null;
 result = func.apply(context, args);
 if (!timeout) context = args = null;
 };
 return function() {
 var now = _now();
 // 首次執(zhí)行時,如果設(shè)定了開始邊界不執(zhí)行選項,將上次執(zhí)行時間設(shè)定為當(dāng)前時間。
 if (!previous && options.leading === false) previous = now;
 // 延遲執(zhí)行時間間隔
 var remaining = wait - (now - previous);
 context = this;
 args = arguments;
 // 延遲時間間隔remaining小于等于0,表示上次執(zhí)行至此所間隔時間已經(jīng)超過一個時間窗口
 // remaining大于時間窗口wait,表示客戶端系統(tǒng)時間被調(diào)整過
 if (remaining <= 0 || remaining > wait) {
 clearTimeout(timeout);
 timeout = null;
 previous = now;
 result = func.apply(context, args);
 if (!timeout) context = args = null;
 //如果延遲執(zhí)行不存在,且沒有設(shè)定結(jié)尾邊界不執(zhí)行選項
 } else if (!timeout && options.trailing !== false) {
 timeout = setTimeout(later, remaining);
 }
 return result;
 };
};
//在調(diào)用高頻率觸發(fā)函數(shù)處使用 throttle 控制頻率在 次/wait
var load = throttle(loadImage, 250);
//綁定滾動事件
bindListener(window, 'scroll', load);
//解綁滾動事件
removeListener(window, 'scroll', load)

調(diào)整后的測試

調(diào)整后的測試結(jié)果

封裝為插件形式

;(function(window, undefined){
 function _now(){
 return new Date().getTime();
 }
 //輔助函數(shù)
 var throttle = function(func, wait, options) {
 var context, args, result;
 var timeout = null;
 // 上次執(zhí)行時間點
 var previous = 0;
 if (!options) options = {};
 // 延遲執(zhí)行函數(shù)
 var later = function() {
 // 若設(shè)定了開始邊界不執(zhí)行選項,上次執(zhí)行時間始終為0
 previous = options.leading === false ? 0 : _now();
 timeout = null;
 result = func.apply(context, args);
 if (!timeout) context = args = null;
 };
 return function() {
 var now = _now();
 // 首次執(zhí)行時,如果設(shè)定了開始邊界不執(zhí)行選項,將上次執(zhí)行時間設(shè)定為當(dāng)前時間。
 if (!previous && options.leading === false) previous = now;
 // 延遲執(zhí)行時間間隔
 var remaining = wait - (now - previous);
 context = this;
 args = arguments;
 // 延遲時間間隔remaining小于等于0,表示上次執(zhí)行至此所間隔時間已經(jīng)超過一個時間窗口
 // remaining大于時間窗口wait,表示客戶端系統(tǒng)時間被調(diào)整過
 if (remaining <= 0 || remaining > wait) {
 clearTimeout(timeout);
 timeout = null;
 previous = now;
 result = func.apply(context, args);
 if (!timeout) context = args = null;
 //如果延遲執(zhí)行不存在,且沒有設(shè)定結(jié)尾邊界不執(zhí)行選項
 } else if (!timeout && options.trailing !== false) {
 timeout = setTimeout(later, remaining);
 }
 return result;
 };
 };
 //分析參數(shù)
 function extend(custom, src){
 var result = {};
 for(var attr in src){
 result[attr] = custom[attr] || src[attr]
 }
 return result;
 }
 //綁定事件,兼容處理
 function bindListener(element, type, callback){
 if (element.addEventListener) {
 element.addEventListener(type, callback);
 }else if (element.attachEvent) {
 element.attachEvent('on'+type, callback)
 }else{
 element['on'+type] = callback;
 }
 }
 //解綁事件,兼容處理
 function removeListener(element, type, callback){
 if (element.removeEventListener) {
 element.removeEventListener(type, callback);
 }else if (element.detachEvent) {
 element.detachEvent('on'+type, callback)
 }else{
 element['on'+type] = null;
 }
 }
 //判斷一個元素是否為DOM對象,兼容處理
 function isElement(o) {
 if(o && (typeof htmlElement==="function" || typeof HTMLElement==="object") && o instanceof HTMLElement){
 return true;
 }else{
 return (o && o.nodeType && o.nodeType===1) ? true : false;
 };
 };
 var lazyload = function(options){
 //輔助變量
 var images = [],
 doc = document,
 body = document.body,
 winHeight = screen.availHeight;
 //參數(shù)配置
 var opt = extend(options, {
 wrapper: body,
 selector: 'img',
 imgSrc: 'data-src',
 defaultSrc: ''
 });
 if (!isElement(opt.wrapper)) {
 console.log('not an HTMLElement');
 if(typeof opt.wrapper != 'string'){
 //若 wrapper 不是DOM對象 或者不是字符串,報錯
 throw new Error('wrapper should be an HTMLElement or a selector string');
 }else{
 //選擇器
 opt.wrapper = doc.querySelector(opt.wrapper) || body;
 }
 }
 //查找所有需要延時加載的圖片
 function getAllImages(selector){
 return Array.prototype.concat.apply([], opt.wrapper.querySelectorAll(selector));
 }
 //設(shè)置默認顯示圖片
 function setDefault(){
 images.map(function(img){
 img.src = opt.defaultSrc;
 })
 }
 //加載圖片
 function loadImage(){
 var nowHeight = body.scrollTop || doc.documentElement.scrollTop;
 console.log(nowHeight);
 if (images.length > 0){
 images.map(function(img, index) {
 if (nowHeight + winHeight > img.offsetTop) {
 img.src = img.getAttribute(opt.imgSrc);
 console.log('loaded');
 images.splice(index, 1);
 }
 })
 }else{
 removeListener(window, 'scroll', load)
 }
 }
 var load = throttle(loadImage, 250);
 return (function(){
 images = getAllImages(opt.selector);
 bindListener(window, 'scroll', load);
 opt.defaultSrc && setDefault()
 loadImage();
 })()
 };
 window.lazyload = lazyload;
})(window);

上述代碼拷貝到項目中即可使用,使用方式:

//使用默認參數(shù)
new lazyload();
//使用自定義參數(shù)
new lazyload({
 wrapper: '.article-content',
 selector: '.image',
 src: 'data-image',
 defaultSrc: 'example.com/static/images/default.png'
});

若在 IE8 中使用,沒有 map 函數(shù)時,請在引用插件前加入下列處理 map 函數(shù)兼容性的代碼:

// 實現(xiàn) ECMA-262, Edition 5, 15.4.4.19
// 參考: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
 Array.prototype.map = function(callback, thisArg) {
 var T, A, k;
 if (this == null) {
 throw new TypeError(" this is null or not defined");
 }
 // 1. 將O賦值為調(diào)用map方法的數(shù)組.
 var O = Object(this);
 // 2.將len賦值為數(shù)組O的長度.
 var len = O.length >>> 0;
 // 3.如果callback不是函數(shù),則拋出TypeError異常.
 if (Object.prototype.toString.call(callback) != "[object Function]") {
 throw new TypeError(callback + " is not a function");
 }
 // 4. 如果參數(shù)thisArg有值,則將T賦值為thisArg;否則T為undefined.
 if (thisArg) {
 T = thisArg;
 }
 // 5. 創(chuàng)建新數(shù)組A,長度為原數(shù)組O長度len
 A = new Array(len);
 // 6. 將k賦值為0
 k = 0;
 // 7. 當(dāng) k < len 時,執(zhí)行循環(huán).
 while (k < len) {
 var kValue, mappedValue;
 //遍歷O,k為原數(shù)組索引
 if (k in O) {
 //kValue為索引k對應(yīng)的值.
 kValue = O[k];
 // 執(zhí)行callback,this指向T,參數(shù)有三個.分別是kValue:值,k:索引,O:原數(shù)組.
 mappedValue = callback.call(T, kValue, k, O);
 // 返回值添加到新數(shù)組A中.
 A[k] = mappedValue;
 }
 // k自增1
 k++;
 }
 // 8. 返回新數(shù)組A
 return A;
 };
}

分享到:
標簽:延時 加載 圖片 js
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定