(function($, window, document) { var mid = 0; $.Lazyload = $.Class.extend({ init: function(element, options) { var self = this; this.container = this.element = element; // placeholder //默认图片 this.options = $.extend({ selector: '', //查询哪些元素需要lazyload diff: false, //距离视窗底部多少像素出发lazyload force: false, //强制加载(不论元素是否在是视窗内) autoDestroy: true, //元素加载完后是否自动销毁当前插件对象 duration: 100 //滑动停止多久后开始加载 }, options); this._key = 0; this._containerIsNotDocument = this.container.nodeType !== 9; this._callbacks = {}; this._init(); }, _init: function() { this._initLoadFn(); this.addElements(); this._loadFn(); $.ready(function() { this._loadFn(); }.bind(this)); this.resume(); }, _initLoadFn: function() { var self = this; self._loadFn = this._buffer(function() { // 加载延迟项 if(self.options.autoDestroy && self._counter == 0 && $.isEmptyObject(self._callbacks)) { self.destroy(); } self._loadItems(); }, self.options.duration, self); }, /** *根据加载函数实现加载器 *@param {Function} load 加载函数 *@returns {Function} 加载器 */ _createLoader: function(load) { var value, loading, handles = [], h; return function(handle) { if(!loading) { loading = true; load(function(v) { value = v; while(h = handles.shift()) { try { h && h.apply(null, [value]); } catch(e) { setTimeout(function() { throw e; }, 0) } } }) } if(value) { handle && handle.apply(null, [value]); return value; } handle && handles.push(handle); return value; } }, _buffer: function(fn, ms, context) { var timer; var lastStart = 0; var lastEnd = 0; var ms = ms || 150; function run() { if(timer) { timer.cancel(); timer = 0; } lastStart = $.now(); fn.apply(context || this, arguments); lastEnd = $.now(); } return $.extend(function() { if( (!lastStart) || // 从未运行过 (lastEnd >= lastStart && $.now() - lastEnd > ms) || // 上次运行成功后已经超过ms毫秒 (lastEnd < lastStart && $.now() - lastStart > ms * 8) // 上次运行或未完成,后8*ms毫秒 ) { run(); } else { if(timer) { timer.cancel(); } timer = $.later(run, ms, null, arguments); } }, { stop: function() { if(timer) { timer.cancel(); timer = 0; } } }); }, _getBoundingRect: function(c) { var vh, vw, left, top; if(c !== undefined) { vh = c.offsetHeight; vw = c.offsetWidth; var offset = $.offset(c); left = offset.left; top = offset.top; } else { vh = window.innerHeight; vw = window.innerWidth; left = 0; top = window.pageYOffset; } var diff = this.options.diff; var diffX = diff === false ? vw : diff; var diffX0 = 0; var diffX1 = diffX; var diffY = diff === false ? vh : diff; var diffY0 = 0; var diffY1 = diffY; var right = left + vw; var bottom = top + vh; left -= diffX0; right += diffX1; top -= diffY0; bottom += diffY1; return { left: left, top: top, right: right, bottom: bottom }; }, _cacheWidth: function(el) { if(el._mui_lazy_width) { return el._mui_lazy_width; } return el._mui_lazy_width = el.offsetWidth; }, _cacheHeight: function(el) { if(el._mui_lazy_height) { return el._mui_lazy_height; } return el._mui_lazy_height = el.offsetHeight; }, _isCross: function(r1, r2) { var r = {}; r.top = Math.max(r1.top, r2.top); r.bottom = Math.min(r1.bottom, r2.bottom); r.left = Math.max(r1.left, r2.left); r.right = Math.min(r1.right, r2.right); return r.bottom >= r.top && r.right >= r.left; }, _elementInViewport: function(elem, windowRegion, containerRegion) { // display none or inside display none if(!elem.offsetWidth) { return false; } var elemOffset = $.offset(elem); var inContainer = true; var inWin; var left = elemOffset.left; var top = elemOffset.top; var elemRegion = { left: left, top: top, right: left + this._cacheWidth(elem), bottom: top + this._cacheHeight(elem) }; inWin = this._isCross(windowRegion, elemRegion); if(inWin && containerRegion) { inContainer = this._isCross(containerRegion, elemRegion); } // 确保在容器内出现 // 并且在视窗内也出现 return inContainer && inWin; }, _loadItems: function() { var self = this; // container is display none if(self._containerIsNotDocument && !self.container.offsetWidth) { return; } self._windowRegion = self._getBoundingRect(); if(self._containerIsNotDocument) { self._containerRegion = self._getBoundingRect(this.container); } $.each(self._callbacks, function(key, callback) { callback && self._loadItem(key, callback); }); }, _loadItem: function(key, callback) { var self = this; callback = callback || self._callbacks[key]; if(!callback) { return true; } var el = callback.el; var remove = false; var fn = callback.fn; if(self.options.force || self._elementInViewport(el, self._windowRegion, self._containerRegion)) { try { remove = fn.call(self, el, key); } catch(e) { setTimeout(function() { throw e; }, 0); } } if(remove !== false) { delete self._callbacks[key]; } return remove; }, addCallback: function(el, fn) { var self = this; var callbacks = self._callbacks; var callback = { el: el, fn: fn || $.noop }; var key = ++this._key; callbacks[key] = callback; // add 立即检测,防止首屏元素问题 if(self._windowRegion) { self._loadItem(key, callback); } else { self.refresh(); } }, addElements: function(elements) { var self = this; self._counter = self._counter || 0; var lazyloads = []; if(!elements && self.options.selector) { lazyloads = self.container.querySelectorAll(self.options.selector); } else { $.each(elements, function(index, el) { lazyloads = lazyloads.concat($.qsa(self.options.selector, el)); }); } //addElements时,自动初始化一次 if(self._containerIsNotDocument) { self._containerRegion = self._getBoundingRect(self.container); } $.each(lazyloads, function(index, el) { if(!el.getAttribute('data-lazyload-id')) { if(self.addElement(el)) { el.setAttribute('data-lazyload-id', mid++); self.addCallback(el, self.handle); } } }); }, addElement: function(el) { return true; }, handle: function() { //throw new Error('需子类实现'); }, refresh: function(check) { if(check) { //检查新的lazyload this.addElements(); } this._loadFn(); }, pause: function() { var load = this._loadFn; if(this._destroyed) { return; } window.removeEventListener('scroll', load); window.removeEventListener($.EVENT_MOVE, load); window.removeEventListener('resize', load); if(this._containerIsNotDocument) { this.container.removeEventListener('scrollend', load); this.container.removeEventListener('scroll', load); this.container.removeEventListener($.EVENT_MOVE, load); } }, resume: function() { var load = this._loadFn; if(this._destroyed) { return; } window.addEventListener('scroll', load, false); window.addEventListener($.EVENT_MOVE, load, false); window.addEventListener('resize', load, false); if(this._containerIsNotDocument) { this.container.addEventListener('scrollend', load, false); this.container.addEventListener('scroll', load, false); this.container.addEventListener($.EVENT_MOVE, load, false); } }, destroy: function() { var self = this; self.pause(); self._callbacks = {}; $.trigger(this.container, 'destroy', self); self._destroyed = 1; } }); })(mui, window, document);