图片懒加载

Posted by WWJ on October 8, 2018

图片懒加载

window.echo = (function (window, document) {

  'use strict';

  /*
   * 构造函数
   */
  var Echo = function (elem) {
    this.elem = elem;
    this.render();
    this.listen();
  };

  /*
   * Images for echoing
   */
  var echoStore = [];
  
  /*
   * 元素是否出现在视口
   */
  var scrolledIntoView = function (element) {
    var coords = element.getBoundingClientRect();
    return (coords.top >= 0 && coords.left >= 0 && coords.top <= (window.innerHeight || document.documentElement.clientHeight));
  };

  /*
   * 改变图片的src
   */
  var echoSrc = function (img, callback) {
    img.src = img.getAttribute('data-echo');
    if (callback) {
      callback();
    }
  };

  /*
   * 移除已经加载的图片
   */
  var removeEcho = function (element, index) {
    if (echoStore.indexOf(element) !== -1) {
      echoStore.splice(index, 1);
    }
  };

  /*
   * Echo the images and callbacks
   */
  var echoImages = function () {
    for (var i = 0; i < echoStore.length; i++) {
      var self = echoStore[i];
      if (scrolledIntoView(self)) {
        echoSrc(self, removeEcho(self, i));
      }
    }
  };

  /*
   * Prototypal setup
   */
  Echo.prototype = {
    init : function () {
      echoStore.push(this.elem);
    },
    render : function () {
      if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', echoImages, false);
      } else {
        window.onload = echoImages;
      }
    },
    listen : function () {
      window.onscroll = echoImages;
    }
  };

  /*
   * 初始化插件
   */
  var lazyImgs = document.querySelectorAll('img[data-echo]');
  for (var i = 0; i < lazyImgs.length; i++) {
    new Echo(lazyImgs[i]).init();
  }

})(window, document);

该脚本采用面向对象的方法,在for循环中的NodeList的每个元素实例上实例化Echo对象(它是Function构造函数)。您可以使用new运算符在脚本末尾看到此实例化。

第一个代码块是一个构造函数,依照规定,构造函数函数名应该大写

var Echo = function (elem) {
  this.elem = elem;
  this.render();
  this.listen();
};

我传入了elem参数,它将是调用插件的for循环中的当前元素,并调用render()、listen()。在内部,这将运行Object继承的原型函数

接下来是一个空数组

var echoStore = [];

这个空数组将充当我们的数据存储,用于推送需要延迟加载的图像。对于这种类型的问题使用数组是一个很好的做法,因为我们可以删除已经从同一个数组加载的图像,这将阻止我们的循环遍历同一个数组,可以更快地执行并循环更少的项目。

接下来,一个简洁的小函数来检测元素是否在视图中:

var scrolledIntoView = function (element) {
  var coords = element.getBoundingClientRect();
  return (coords.top >= 0 && coords.left >= 0 && coords.top <= (window.innerHeight || document.documentElement.clientHeight));
};

使用getBoundingClientRect()法返回一个文本矩形对象,返回的数据描述了顶部,右侧,底部和左侧像素。然后我们可以对window.innerHeight或document.documentElement.clientHeight进行智能比较,它可以跨浏览器为您提供浏览器中的可见区域。

接下来是一个非常简单的函数,可以在需要时将当前图像的src属性切换到关联的data-echo属性:

var echoSrc = function (img, callback) {
  img.src = img.getAttribute('data-echo');
  if (callback) {
    callback();
  }
};

如果存在回调,它将运行(我在这里传递回调,但为了防止错误,只需声明这个东西就好了)。

我设置的下一个函数来检查当前元素是否存在于数组中,如果存在,则使用当前索引上的.splice()方法将其删除以删除“本身”:

var removeEcho = function (element, index) {
  if (echoStore.indexOf(element) !== -1) {
    echoStore.splice(index, 1);
  }
};

该插件最主要的一点是基于我们的数据存储阵列在视图持续更新。此函数循环遍历我们的数据存储,并在启动scrolledIntoView函数后检查数组中的当前元素是否在视图中。如果证明是真的,那么我们调用echoSrc函数,传入当前元素以及当前元素的索引值,即i。此索引值将传递到removeEcho函数,该函数又从数组中删除自身的副本。这意味着我们的数组变得越来越短,我们的JavaScript在循环遍历我们的剩余元素时不必那么努力或长时间工作。

var echoImages = function () {
  for (var i = 0; i < echoStore.length; i++) {
    var self = echoStore[i];
    if (scrolledIntoView(self)) {
      echoSrc(self, removeEcho(self, i));
    }
  }
};

脚本的面向对象部分在原型扩展中查找,其中包含一些函数。第一个是init()函数,它只是将当前元素推送到我们的数据存储数组中。 render()函数检查是否存在addEventListener事件,然后在触发DOMContentLoaded事件后调用echoImages函数。如果它不存在,可能在IE7 / 8中,它只会运行onload。 listen()函数将在每次滚动窗口时再次运行该函数,以轮询并查看是否有任何元素进入视图以更多地发挥其魔力。

Echo.prototype = {
  init : function () {
    echoStore.push(this.elem);
  },
  render : function () {
    if (document.addEventListener) {
      document.addEventListener('DOMContentLoaded', echoImages, false);
    } else {
      window.onload = echoImages;
    }
  },
  listen : function () {
    window.onscroll = echoImages;
  }
};

脚本的最后一部分是优雅的API,您可以在NodeList中的每个项目上调用新的Object:

var lazyImgs = document.querySelectorAll('img[data-echo]');
for (var i = 0; i < lazyImgs.length; i++) {
  new Echo(lazyImgs[i]).init();
}

或者

[].forEach.call(document.querySelectorAll('img[data-echo]'), function (img) {
  new Echo(img).init();
}