图片懒加载

图片懒加载是当页面有很多图片,出于性能考虑,先加载浏览器窗口可视区域的图片,当页面向下滚动时,再加载出现在是视口区域的图片。

先定义页面元素:

  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse1-mm.cn.bing.net/th?id=OIP.SRP4_J4iHcyBJD65ylBj0gHaE8&w=286&h=186&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse4-mm.cn.bing.net/th?id=OIP.xkothzpEYYhoKXf9j2anqQHaHa&w=190&h=185&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse4-mm.cn.bing.net/th?id=OIP.Bp8GJ5MJTdb_tVb3LACyvAHaE8&w=282&h=188&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse3-mm.cn.bing.net/th?id=OIP.nhUwwNvy9CudXaZ1kY65KQHaE8&w=231&h=160&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse3-mm.cn.bing.net/th?id=OIP.4MeID57Cfg62gWPgkmC0RQAAAA&w=299&h=154&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse1-mm.cn.bing.net/th?id=OIP.SRP4_J4iHcyBJD65ylBj0gHaE8&w=227&h=160&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse1-mm.cn.bing.net/th?id=OIP.SbiQ_lKGs-nTkCyxwAAv9AHaE8&w=229&h=160&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse3-mm.cn.bing.net/th?id=OIP.p6t4cbxYgGqjBZvGzKOkIAHaEo&w=228&h=160&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse2-mm.cn.bing.net/th?id=OIP.cmcdKxRHPy_Al-khrvyg5gHaHa&w=170&h=170&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse4-mm.cn.bing.net/th?id=OIP.ysLAUrh3UMLWfj_E5dZ4igHaFa&w=228&h=166&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse1-mm.cn.bing.net/th?id=OIP.pE-awZkcAEbU_hkVCTIUAAHaE6&w=260&h=173&c=7&o=5&pid=1.7" alt="">
  <img src="http://cdn.uedna.com/201402/1392662516435_1140x0.gif" data-src="https://tse2-mm.cn.bing.net/th?id=OIP.EZKqAWO0uSiSDNwIqjgv3AHaEo&w=228&h=160&c=7&o=5&pid=1.7" alt="">

一、节流、防抖

图参考 https://segmentfault.com/a/1190000005926579

节流(throttle)

节流

上图中绿色表示触发一次事件,持续触发时,throttle会合并一定时间内的事件,并在该时间结束时触发一次事件。

防抖(debounce)

防抖

上图中,持续触发事件时,debounce会合并事件且不会触发事件,当一定时间内没有事件发生时,才去触发。

懒加载实现

/*
* 判断图片元素是否可见
* el 图片
* return {boolean}
*/
function isVisible(el) {
  const elScrollTop  = el.getBoundingClientRect().top,
        clientHeight = window.innerHeight;
  return elScrollTop <= clientHeight;
}

/*
* 加载图片,当图片出片出现在可视区域时,将img的data-src属性值赋给src属性
*/
function loadImages() {
  const images  = document.querySelectorAll('img'),
        imgsLen = images.length;
  let index = 0;         // 定位到当前图片

  for(let i = index; i < imgsLen; i++) {
    if(isVisible(images[i])) {
      if(el.src) {
        el.src = el.dataset.src;
      }
    }
  }
}

/*
* 节流
* fn 回调函数
* delay 间隔时间
*/
function throttle (fn, delay = 100) {
  let startTime = 0;
  return function () {
    let now  = new Date(),
        self = this;

    // 间隔的时间大于延迟的时间时,执行回调函数,并重置间隔的开始时间
    if(now - startTime > delay) {
      fn.apply(self, arguments);
      startTime = now;
    }
  }
}

/*
* 防抖
* fn 回调函数
* delay 间隔时间
*/
function debounce (fn, delay = 500) {
  let timer = null;

  return function () {
    let self = this;

    clearTimeout(timer);

    // 固定时间调用一次函数
    timer = setTimeout(function() {
      fn.apply(self, arguments)
    }, delay)
  }
}

window.onload = checkImgs;              // 初始加载可视区域的图片
window.onscroll = throttle(checkImgs);  // 滚动页面时,节流调用
window.onscroll = debounce(checkImgs);  // 滚动页面时,防抖调用

二、IntersectionObserver

观察元素是否在浏览器视口内。

const io = new IntersectionObserver(fn, option);

// 开始观察
io.observe(element);

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

fn的参数是一个数组,每个数组都是一个IntersectionObserverEntry对象,包括以下属性:

  • time : 可见性发生变化的时间,单位为毫秒
  • rootBounds : 与getBoundingClientRect()方法的返回值一样
  • boundingClientRect : 目标元素的矩形区域的信息
  • intersectionRect : 目标元素与视口的交叉区域的信息
  • intersectionRadio : 目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时为0
  • target : 被观察的目标元素,是一个DOM节点对象

intersectionRadio > 0 && intersectionRadio <= 1即在可视区域内

懒加载实现

/*
* 将图片对象转化为数组
* selector 选择器
*/
function getImages(selector) {
  return Array.from(document.querySelectorAll(selector));
}

// 创建observer实例
const io = new IntersectionObserver(imgs => {
  imgs.forEach(item => {
    const el = item.target,
          intersectionRadio = item.intersectionRatio;

    if(intersectionRadio > 0 && intersectionRadio <= 1) {
      if(el.src) {
        el.src = el.dataset.src
      }
    }
  })
})

// 调用并观察图片
getImages('img').forEach(item => io.observe(item));

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器