lazy load images.
/**
* Include this script in the page where you want to use lazy load.
* to make a div or image lazy load of image, this is an example:
*
* HTML:
* <span class='lazy' data-image="http://placehold.it/300x300"></span>
*
* OR:
* <img class='lazy' data-image="http://placehold.it/300x300" />
*
* To use lazy load in loading, trigger the `lazyload:restart` event
* on document.
*
* To uninstall the lazy load, trigger the `lazyload:teardown` event
* on document.
*/
var LazyLoad = require('./lib/lazy-load');
(function ($) {
var instance = new LazyLoad();
// setup after dom is ready.
$(document).ready(function () {
window.setTimeout(function () {
instance.setup();
}, 100);
});
$(document).on('lazyload:teardown', function () {
instance.teardown();
});
$(document).on('lazyload:restart', function () {
instance.restart();
});
window.lazy = instance;
}.call(this, window.$ || window.jQuery));
/*!
* Copyright 2016 MoSeeker, Inc.
* @author Towry Wang
*/
var verge = require('verge');
var classes = require('dom-classes');
var objectAssign = require('object-assign');
function LazyLoad(options) {
var _options = options || {};
if (Object.prototype.toString.call(_options) !== '[object Object]') {
throw new TypeError("LazyLoad requires an object as options.");
}
this.options = objectAssign({
lazyClass: 'lazy',
ignoreFail: true,
taste: true,
within: 50
}, _options);
this._setup = false;
this._timer = null;
this._images = null;
this._inProcess = false;
}
module.exports = LazyLoad;
LazyLoad.prototype.setup = function () {
if (this._setup) return;
this._setup = true;
this._onResizeHandler = this._onResizeHandler.bind(this);
this._onScrollHandler = this._onScrollHandler.bind(this);
this._onTouchEndHandler = this._onTouchEndHandler.bind(this);
this._queryLazies();
this.bindEvent();
if (this.options.taste) {
this.taste();
}
}
LazyLoad.prototype.taste = function () {
if (this._timer) return;
var self = this;
this._inProcess = true;
this._timer = window.setTimeout(function () {
self.consumeImages();
}, 0);
}
LazyLoad.prototype.restart = function () {
this.teardown();
this.setup();
}
LazyLoad.prototype.teardown = function () {
if (!this._setup) return;
this.unbindEvent();
if (this._timer) {
window.clearTimeout(this._timer);
this._timer = null;
}
this._images = null;
this._setup = false;
this._inProcess = false;
}
LazyLoad.prototype.bindEvent = function () {
if (!this._setup) return;
addEvent(window, this._onScrollHandler);
addEvent(window, 'resize', this._onResizeHandler);
addEvent(window, 'touchend', this._onTouchEndHandler);
}
LazyLoad.prototype.unbindEvent = function () {
if (!this._setup) return;
removeEvent(window, this._onScrollHandler);
removeEvent(window, 'resize', this._onResizeHandler);
removeEvent(window, 'touchend', this._onTouchEndHandler);
}
LazyLoad.prototype._queryLazies = function () {
this._images = [];
if (document.querySelectorAll) {
var images = document.querySelectorAll('.' + this.options.lazyClass);
} else {
images = document.getElementsByClassName(this.options.lazyClass);
}
if (!images) {
this._images = null;
return;
}
this._images = Array.prototype.slice.call(images);
}
LazyLoad.prototype._onTouchEndHandler = function () {
window.setTimeout(function () {
this.consumeImages();
}.bind(this), 400);
}
LazyLoad.prototype._onResizeHandler = function () {
this.restart();
}
LazyLoad.prototype._onScrollHandler = function () {
if (this._timer) {
window.clearTimeout(this._timer);
this._timer = null;
this._inProcess = false;
}
var self = this;
this._inProcess = true;
this._timer = window.setTimeout(function () {
self.consumeImages();
}, 10);
}
LazyLoad.prototype.consumeImages = function () {
if (!this._images || !this._images.length) {
this.teardown();
return;
}
if (!this._inProcess) {
return;
}
var left = [];
var i = 0;
var image;
for (i; i < this._images.length; i++) {
if (!this._inProcess) {
this._images = i === 0 ? left.concat(this._images) : left.concat(this._images.slice(i-1));
return;
}
image = this._images[i];
if (!image) continue;
if (!verge.inViewport(image, this.options.within) || isNaN(verge.aspect(image))) {
left.push(image);
continue;
} else {
console.log(image);
}
loadImage(image, function (image) {
return function () {
classes.remove(image, this.options.lazyClass);
}.bind(this);
}.call(this, image), this.options.ignoreFail ? null : function () {
this._images.push(image);
}.bind(this));
}
if (i === this._images.length) {
this._images = left;
return;
}
this._images = i === 0 ? left.concat(this._images) : left.concat(this._images.slice(i-1));
}
function loadImage(ele, sucessFn, failFn) {
var img = new Image();
var src = ele.getAttribute('data-image');
if (!src) return;
img.onload = function () {
if (!ele.tagName) return;
if (ele.tagName.toLowerCase() === 'img') {
ele.src = src;
} else {
ele.style.backgroundImage = 'url(' + src + ')';
}
classes.remove(ele, 'lazy-loading');
classes.add(ele, 'lazy-loaded');
sucessFn ? sucessFn() : null;
}
img.onerror = function () {
classes.remove(ele, 'lazy-loading');
classes.add(ele, 'lazy-error');
failFn ? failFn() : null;
}
classes.remove(ele, 'lazy-loaded');
classes.add(ele, 'lazy-loading');
img.src = src;
}
function addEvent(el, name, listener) {
if (!listener) {
listener = name;
name = 'scroll';
}
// if (name === 'scroll' && 'ontouchmove' in window) {
// name = 'touchmove';
// }
if (window.addEventListener) {
window.addEventListener(name, listener);
} else {
window.attachEvent('on' + name, listener);
}
}
function removeEvent(el, listener) {
if (!listener) {
listener = name;
name = 'scroll';
}
// if (name === 'scroll' && 'ontouchmove' in window) {
// name = 'touchmove';
// }
if (window.removeEventListener) {
window.removeEventListener(name, listener);
} else {
window.detachEvent(name, listener);
}
}