Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

实现图片懒加载的两种方案 #33

Open
cookiepool opened this issue Jan 9, 2020 · 0 comments
Open

实现图片懒加载的两种方案 #33

cookiepool opened this issue Jan 9, 2020 · 0 comments
Labels
前端相关 前端相关

Comments

@cookiepool
Copy link
Owner

cookiepool commented Jan 9, 2020

常见的懒加载方案

这个方案是借助滚动距离来判断的,获取document.documentElement的scrollTop与需要懒加载的图片标签的offsetTop进行比较,看图片是否进入可视区域,从而实现图片加载。

代码如下:

// 封装一个获取图片到顶部的距离
function getTop(elm){
  let tempTop = elm.offsetTop;
  //如果包裹img的父元素离顶部也有距离,那么也要把它加上
  while(elm = elm.offsetParent) { //注意这儿offsetParent返回的是元素不是数值,同时这儿是赋值语句
    tempTop += elm.offsetTop;
  }
  return tempTop;
}

function lazyLoad(imgs){
  //获取可视区域高度
  let viewHeight = document.documentElement.clientHeight || window.innerHeight;
  //获取滚动高度
  let scrollTopVal = document.documentElement.scrollTop || document.body.scrollTop;
  for(var i = 0; i < imgs.length; i++){
      if((viewHeight + scrollTopVal) > getTop(imgs[i])){
        imgs[i].src = imgs[i].getAttribute('data-src')
      }
  }
}

// 使用
// 给你需要懒加载的图片设置一个类名'lazy-load'
let imgs = document.querySelectorAll('.lazy-load');
lazyLoad(imgs);
// 进行滚动监听
window.onscroll = function() {
  lazyLoad(imgList);
}

使用交叉观察者实现懒加载

浏览器自带的API IntersectionObserver进行检测,十分方便

先贴上代码实现:

// 给你需要懒加载的图片设置一个类名'lazy-load'
let imgs = document.querySelectorAll('.lazy-load');

// 实例化一个交叉观察者对象
let obsever = new IntersectionObserver((entries) => {
  entries.forEach((item, index, arrSelf) => {
    // 判断检测的对象是否进入可视窗口
    if(item.isIntersecting) {
      // 进入后进行图片地址赋值
      item.target.src = item.target.dataset.src;
      // 实现图片加载后不在进行检测
      obsever.unobserve(item.target);
    }
  });
});

// 注册观察需要懒加载的img
imgs.forEach((item) => {
  obsever.observe(item);
});

IntersectionObserver

用法

new IntersectionObserver(callback, options);
  • callback 是一个回调函数,接受一个参数,返回当前已监听并且发生了交叉的目标集合。
  • options 配置参数,可选,是一个对象。
new IntersectionObserver((entries) => {

}, {
  // options参数
});

entries当前已监听并且发生了交叉的目标集合,我们可以把它用来循环,entres的打印结果如下:

参数表,对于entries这个集合里面的每个对象都有如下常用的参数:

  • entries下的对象

| 属性 | 说明 |
| - | - | - |
| boundingClientReact | 元素的空间信息,就是位置信息这些个 |
| intersctionRatio | 元素可见区域的占比 |
| isInterscting | 判断元素是否交叉,也就是元素在可视区是否可见了 |
| target | 目标节点,跟我们使用事件的event.target效果一样 |

  • options 一个配置对象

| 属性 | 说明 |
| - | - | - |
| root | 指定父元素,默认为视窗 |
| rootMargin | 触发交叉的偏移值,默认为"0px 0px 0px 0px"。(上左下右,正数为向外扩散,负数则向内收缩) |

  • IntersectionObserver的实例对象的常用方法

| 名称 | 说明 | 参数 |
| - | - | - | - |
| observe | 开始监听一个目标元素 | DOM节点 |
| unobserve | 停止监听一个目标元素 | DOM节点 |
| takeRecords | 返回所有监听的目标元素集合 | |
| disconnect | 停止监听所有元素 | |

封装一个支持多浏览器的懒加载方案

IntersectionObserve这个API用起来确实方便,但是还是有一部分浏览器不支持,所以需要封装判断下。

function getTop(elm){
  let tempTop = elm.offsetTop;
  //如果包裹img的父元素离顶部也有距离,那么也要把它加上
  while(elm = elm.offsetParent) { //注意这儿offsetParent返回的是元素不是数值,同时这儿是赋值语句
    tempTop += elm.offsetTop;
  }
  return tempTop;
}

function lazyLoad(imgs){
  //获取可视区域高度
  let viewHeight = document.documentElement.clientHeight;
  //获取滚动高度
  let scrollTopVal = document.documentElement.scrollTop || document.body.scrollTop;
  console.log(viewHeight, scrollTopVal)
  for(var i = 0; i < imgs.length; i++){
      if((viewHeight + scrollTopVal) > getTop(imgs[i])){
        imgs[i].src = imgs[i].getAttribute('data-src')
      }
  }
}

function useIntersectionObserve(imgList) {
  let obsever = new IntersectionObserver((entries) => {
    entries.forEach((item, index, arrSelf) => {
      console.log(entries)
      if(item.isIntersecting) {
        item.target.src = item.target.dataset.src;
        obsever.unobserve(item.target);
      }
    });
  });

  imgList.forEach((item) => {
    obsever.observe(item);
  });
}


export default {
  // 这两个个方案要求img标签有默认的高度,不然当一个页面出来就展示图片的情况下会一下展示很多图片,
  // 所以在使用懒加载方案时给img加默认高度
  // 单独使用普通懒加载方案
  lazyloadDefault(selector) {
    let imgList = document.querySelectorAll(selector);

    lazyLoad(imgList);
    window.onscroll = function() {
      lazyLoad(imgList);
    }
  },
  // 单独使用IntersectionObserve
  lazyLoadIntersectionObserver(selector) {
    let imgList = document.querySelectorAll(selector);

    let obsever = new IntersectionObserver((entries) => {
      entries.forEach((item, index, arrSelf) => {
        console.log(entries)
        if(item.isIntersecting) {
          item.target.src = item.target.dataset.src;
          obsever.unobserve(item.target);
        }
      });
    });

    imgList.forEach((item) => {
      obsever.observe(item);
    });
  },
  // 多浏览器适配方案
  lazyLoadPolyfill(selector) {
    let imgList = document.querySelectorAll(selector);

    if(!window.IntersectionObserver) {
      lazyLoad(imgList);
      window.onscroll = function() {
        lazyLoad(imgList);
      }
    }else {
      useIntersectionObserve(imgList)
    }
  }
}
@cookiepool cookiepool changed the title 图片懒加载实现的两种方案 实现的图片懒加载两种方案 Jan 13, 2020
@cookiepool cookiepool changed the title 实现的图片懒加载两种方案 实现图片懒加载的两种方案 Jan 13, 2020
@cookiepool cookiepool added the 前端相关 前端相关 label Jan 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
前端相关 前端相关
Projects
None yet
Development

No branches or pull requests

1 participant