图片懒加载

140 阅读1分钟

源码

/**
 * @author sql
 * @description 图片懒加载 首先判断浏览器是否支持IntersectionObserver
 * @constructor {selector} '.className'
 */
export class LazyImage {
  constructor(selector) {
    // 懒记载图片列表,将伪数组转为数组,以便可以使用数组的api
    this.lazyImages = Array.prototype.slice.call(document.querySelectorAll(selector));
    this.init();
  }
  inViewShow() {
    let len = this.lazyImages.length;
    for (let i = 0; i < len; i++) {
      let imageElement = this.lazyImages[i];
      const rect = imageElement.getBoundingClientRect();
      // 出现在视野的时候加载图片
      if (rect.top < document.documentElement.clientHeight) {
      	// 兼容360、qq浏览器低版本
          $(imageElement).attr('src', $(imageElement).attr('data-src'));
        // 移除掉已经显示的
        this.lazyImages.splice(i, 1);
        len--;
        i--;
        if (this.lazyImages.length === 0) {
          // 如果全部都加载完 则去掉滚动事件监听
          document.removeEventListener('scroll', this._throttleFn);
        }
      }
    }
  }
  init() {
    // 通过IntersectionObserver api判断图片是否出现在可视区域内,不需要监听Scroll来判断
    if ('IntersectionObserver' in window) {
      let lazyImageObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach((entry, index) => {
          // 如果元素可见
          if (entry.intersectionRatio && entry.intersectionRatio > 0) {
            let lazyImage = entry.target;
            lazyImage.src = lazyImage.dataset.src;
            lazyImage.classList.remove('lazy-image');
            lazyImageObserver.unobserve(lazyImage);
          }
        });
      });
      this.lazyImages.forEach(function (lazyImage) {
        lazyImageObserver.observe(lazyImage);
      });
    } else {
      this.inViewShow();
      // 搭配节流函数
      this._throttleFn = throttle(this.inViewShow.bind(this));
      document.addEventListener('scroll', this._throttleFn);
    }
  }
}

/**
 * @description 节流函数
 * @param {function} fn
 * @param {number} delay
 * @param {number} mustRun
 */
export function throttle(fn, delay = 15, mustRun = 30) {
  let t_start = null;
  let timer = null;
  let context = this;
  return function () {
    let t_current = +new Date();
    let args = Array.prototype.slice.call(arguments);
    clearTimeout(timer);
    if (!t_start) {
      t_start = t_current;
    }
    if (t_current - t_start > mustRun) {
      fn.apply(context, args);
      t_start = t_current;
    } else {
      timer = setTimeout(() => {
        fn.apply(context, args);
      }, delay);
    }
  };
}

使用方法

// index.HTML
<img
   data-src="../static/images/index/index.jpg"
   src="../static/images/common/loading.jpg"
   class="lazy-image"
   alt="首页图片"
/>
// index.js
new LazyImage('.lazy-image')