<!DOCTYPE html> <html> <head> <title>懒加载</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> body{ background: #eee; } ul,li{padding: 0; margin: 0; list-style: none;} #main .list-wrap{ overflow: hidden; } #main .list-item{ padding: 5px 0; background: #fff; border: 1px solid #ccc; overflow: hidden; } #main .pic-wrap{ float: left; margin: 0 10px; height: 100px; min-width: 50px; } #main .cont-wrap{ padding: 5px; color: #777; font-size: 14px; line-height: 1.4; } </style> </head> <body> <div id="main"> <ul class="list-wrap"> <li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div>
</li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
<li class="list-item amfe-appear"> <img class="pic-wrap" data-src="你的图片地址" /> <div class="cont-wrap"> 这个效果有点好 </div> </li>
</ul> </div>
<script src="bundle.js"></script>
<script> (function () {
var AmfeAppear = require('amfe-appear'); //宝贝列表实例化,查找类名包含amfe-appear的元素
var itemAppear = AmfeAppear.appear.init({ cls: 'amfe-appear', onAppear: function(){
//元素处在可视区域内,执行懒加载
var img = this.querySelector('img'); // 延迟执行,方便查看效果
setTimeout(function() {
img.src = img.getAttribute('data-src'); }, 500); }, onDisappear: function() {
//元素处在不可视区域内,做输出
console.log(this); } });
// 触发校验 itemAppear.fire(); }) () ;
</script>
</body>
</html>
bundle.js
1 <script> 2 /*bundle.js*/ 3 require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 4 'use strict'; 5 6 Object.defineProperty(exports, "__esModule", { 7 value: true 8 }); 9 10 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 11 12 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 14 var doc = document; 15 var appearEvt = doc.createEvent("HTMLEvents"); //创建自定义显示事件 ; 16 var disappearEvt = doc.createEvent("HTMLEvents"); //创建自定义显示事件 ; 17 18 function createEvent(eventType) { 19 appearEvt.initEvent(eventType, false, true); 20 disappearEvt.initEvent(eventType, false, true); 21 } 22 23 /** 24 * [throttle 节流函数] 25 * @param {[function]} func [执行函数] 26 * @param {[int]} wait [等待时长] 27 * @return {[type]} [description] 28 */ 29 function throttle(func, wait) { 30 var previous = 0, 31 //上次执行的时间 32 timeout = null, 33 //setTimout任务 34 args = void 0, 35 //参数 36 result = void 0; //结果 37 var later = function later() { 38 previous = Date.now(); 39 timeout = null; //清空计时器 40 func(args); 41 }; 42 return function () { 43 var now = Date.now(), 44 args = arguments, 45 remaining = wait - (now - previous); 46 if (remaining <= 0 || remaining >= wait) { 47 //如果没有剩余时间,或者存在修改过系统时间导致剩余时间增大的情况,则执行 48 clearTimeout(timeout); 49 timeout = null; 50 result = func(args); 51 } else if (timeout === null) { 52 timeout = setTimeout(later, remaining); 53 } 54 return result; 55 }; 56 } 57 58 /** 59 * [getOffset 获取边距尺寸] 60 * @param {[type]} el [description] 61 * @param {[type]} param [description] 62 * @return {[type]} [description] 63 */ 64 function getOffset(el, param) { 65 var l, r, t, b; 66 if (!el) { 67 return; 68 } 69 if (!param) { 70 param = { 71 x: 0, 72 y: 0, 73 h: null, 74 w: null 75 }; 76 } 77 78 if (el !== window) { 79 el = el.getBoundingClientRect(); 80 l = el.left; 81 t = el.top; 82 r = el.right; 83 b = el.bottom; 84 } else { 85 l = 0; 86 t = 0; 87 r = l + el.innerWidth; 88 b = t + el.innerHeight; 89 } 90 param.h = param.h || (el.height || el.innerHeight) - param.y; 91 param.w = param.w || (el.width || el.innerWidth) - param.x; 92 var offset = { 93 'left': l + (el.width || el.innerWidth) - param.w - param.x, 94 'top': t + (el.height || el.innerHeight) - param.h - param.y, 95 'right': r - param.x, 96 'bottom': b - param.y 97 }; 98 return offset; 99 } 100 101 //元素位置比较 102 function compareOffset(d1, d2) { 103 var left = d2.right > d1.left && d2.left < d1.right; 104 var top = d2.bottom > d1.top && d2.top < d1.bottom; 105 return left && top; 106 } 107 //获取移动方向 108 function getDirection(beforeOffset, nowOffset) { 109 var direction = 'none'; 110 var horizental = beforeOffset.left - nowOffset.left; 111 var vertical = beforeOffset.top - nowOffset.top; 112 if (vertical === 0) { 113 if (horizental !== 0) { 114 direction = horizental > 0 ? 'left' : 'right'; 115 } else { 116 direction = 'none'; 117 } 118 } 119 if (horizental === 0) { 120 if (vertical !== 0) { 121 direction = vertical > 0 ? 'up' : 'down'; 122 } else { 123 direction = 'none'; 124 } 125 } 126 return direction; 127 } 128 129 function extend(target, el) { 130 for (var k in el) { 131 if (el.hasOwnProperty(k)) { 132 target[k] = el[k]; 133 } 134 } 135 return target; 136 } 137 138 /** 139 * [__bindEvent 绑定事件,包括滚动、touchmove、transform、resize等] 140 * @return {[type]} [description] 141 */ 142 function __bindEvent() { 143 var _this = this, 144 _arguments = arguments; 145 146 var handle = throttle(function () { 147 __fire.apply(_this, _arguments); 148 }, this.options.wait); 149 if (this.__handle) { 150 //避免重复绑定 151 this.viewWrapper.removeEventListener('scroll', this.__handle); 152 this.__handle = null; 153 } 154 this.__handle = handle; 155 this.viewWrapper.addEventListener('scroll', handle, false); 156 this.viewWrapper.addEventListener('resize', function () { 157 __fire.apply(_this, _arguments); 158 }, false); 159 this.viewWrapper.addEventListener('animationEnd', function () { 160 __fire.apply(_this, _arguments); 161 }, false); 162 // android4.0以下 163 this.viewWrapper.addEventListener('webkitAnimationEnd', function () { 164 __fire.apply(_this, _arguments); 165 }, false); 166 this.viewWrapper.addEventListener('transitionend', function () { 167 __fire.apply(_this, _arguments); 168 }, false); 169 } 170 171 //获取容器内所有的加载元素 172 function __getElements(selector) { 173 var _this2 = this; 174 175 //获取视窗容器 176 var viewWrapper = this.options.viewWrapper; 177 if (typeof viewWrapper === 'string') { 178 //如果是字符串,则选择器 179 this.viewWrapper = doc.querySelector(viewWrapper); 180 } else { 181 //对象传值 182 this.viewWrapper = viewWrapper; 183 } 184 var appearWatchElements = void 0; 185 //获取容器内的所有目标元素 186 if (this.viewWrapper === window) { 187 appearWatchElements = doc.querySelectorAll(selector); 188 } else { 189 appearWatchElements = this.viewWrapper.querySelectorAll(selector); 190 } 191 appearWatchElements = [].slice.call(appearWatchElements, null); 192 193 appearWatchElements = appearWatchElements.filter(function (ele) { 194 // 如果已经绑定过,清除appear状态,不再加入到数组里 195 if (ele.dataset.bind === '1') { 196 delete ele._hasAppear; 197 delete ele._hasDisAppear; 198 delete ele._appear; 199 ele.classList.remove(_this2.options.cls); 200 return false; 201 } else { 202 return true; 203 } 204 }); 205 206 return appearWatchElements; 207 } 208 209 function __initBoundingRect(elements) { 210 var _this3 = this; 211 212 if (elements && elements.length > 0) { 213 [].forEach.call(elements, function (ele) { 214 ele._eleOffset = getOffset(ele); 215 //移除类名 216 ele.classList.remove(_this3.options.cls); 217 // 标志已经绑定 218 ele.dataset.bind = 1; 219 }); 220 } 221 } 222 223 // 触发加载 224 function __fire() { 225 var viewWrapper = this.viewWrapper, 226 elements = this.appearWatchElements, 227 appearCallback = this.options.onAppear, 228 //appear的执行函数 229 isDispatch = this.options.isDispatch, 230 // 是否分发事件 231 disappearCallback = this.options.onDisappear, 232 //disappear的执行函数 233 viewWrapperOffset = getOffset(viewWrapper, { 234 x: this.options.x, 235 y: this.options.y, 236 h: this.options.h, 237 w: this.options.w 238 }), 239 isOnce = this.options.once; //是否只执行一次 240 if (elements && elements.length > 0) { 241 [].forEach.call(elements, function (ele) { 242 //获取左右距离 243 var eleOffset = getOffset(ele), 244 direction = getDirection(ele._eleOffset, eleOffset); 245 //保存上个时段的位置信息 246 ele._eleOffset = eleOffset; 247 //查看是否在可视区域范围内 248 var isInView = compareOffset(viewWrapperOffset, eleOffset), 249 appear = ele._appear, 250 _hasAppear = ele._hasAppear, 251 _hasDisAppear = ele._hasDisAppear; 252 appearEvt.data = { 253 direction: direction, 254 eleOffset: eleOffset 255 }; 256 disappearEvt.data = { 257 direction: direction, 258 eleOffset: eleOffset 259 }; 260 if (isInView && !appear) { 261 if (isOnce && !_hasAppear || !isOnce) { 262 //如果只触发一次并且没有触发过或者允许触发多次 263 //如果在可视区域内,并且是从disppear进入appear,则执行回调 264 var appearFn = function appearFn(ev) { 265 appearCallback && appearCallback.call(ele, ev); 266 ele.removeEventListener('appear', appearFn); 267 }; 268 ele.addEventListener('appear', appearFn); 269 if (isDispatch) { 270 //触发自定义事件 271 ele.dispatchEvent(appearEvt); 272 } else { 273 appearFn(appearEvt); 274 } 275 ele._hasAppear = true; 276 ele._appear = true; 277 } 278 } else if (!isInView && appear) { 279 if (isOnce && !_hasDisAppear || !isOnce) { 280 //如果不在可视区域内,并且是从appear进入disappear,执行disappear回调 281 var disappearFn = function disappearFn(ev) { 282 disappearCallback && disappearCallback.call(ele, ev); 283 ele.removeEventListener('disappear', disappearFn); 284 }; 285 ele.addEventListener('disappear', disappearFn); 286 287 if (isDispatch) { 288 //触发自定义事件 289 ele.dispatchEvent(disappearEvt); 290 } else { 291 disappearFn(disappearEvt); 292 } 293 ele._hasDisAppear = true; 294 ele._appear = false; 295 } 296 } 297 }); 298 } 299 } 300 301 function __init(opts) { 302 //扩展参数 303 extend(this.options, opts || (opts = {})); 304 //注册事件 305 createEvent(this.options.eventType); 306 //获取目标元素 307 this.appearWatchElements = this.appearWatchElements || __getElements.call(this, "." + this.options.cls); 308 //初始化位置信息 309 __initBoundingRect.call(this, this.appearWatchElements); 310 //绑定事件 311 __bindEvent.call(this); 312 } 313 314 var Appear = function () { 315 function Appear() { 316 _classCallCheck(this, Appear); 317 318 //默认参数 319 this.options = { 320 viewWrapper: window, 321 wait: 100, 322 x: 0, 323 y: 0, 324 w: null, 325 h: null, 326 cls: 'amfe-appear', 327 once: false, 328 isDispatch: true, 329 eventType: 'appear', // 事件类型,默认出现事件为appear、消失事件为disappear,自定义事件名,消失事件自动加上前缀dis 330 onAppear: function onAppear() {}, 331 onDisappear: function onDisappear() {} 332 }; 333 this.viewWrapper = null; 334 this.appearWatchElements = null; 335 __init.apply(this, arguments); 336 } 337 338 _createClass(Appear, [{ 339 key: "bind", 340 value: function bind(node) { 341 var cls = this.options.cls; 342 // 添加需要绑定的appear元素 343 if (typeof node === 'string') { 344 var elements = __getElements.call(this, node); 345 [].forEach.call(elements, function (ele) { 346 if (!ele.classList.contains(cls)) { 347 ele.classList.add(cls); 348 } 349 }); 350 } else if (node.nodeType === 1 && (this.viewWrapper === window || this.viewWrapper.contains(node))) { 351 //如果传入的是元素并且在包含在容器中,直接添加类名 352 if (!node.classList.contains(cls)) { 353 //添加类名 354 node.classList.add(cls); 355 } 356 } else { 357 return this; 358 } 359 //新增的子元素 360 var newElements = __getElements.call(this, "." + this.options.cls); 361 //对缓存的子元素做增量 362 this.appearWatchElements = this.appearWatchElements.concat(newElements); 363 //初始化新子元素的位置信息 364 __initBoundingRect.call(this, newElements); 365 return this; 366 } 367 // 重置函数 368 369 }, { 370 key: "reset", 371 value: function reset(opts) { 372 __init.call(this, opts); 373 this.appearWatchElements.forEach(function (ele) { 374 delete ele._hasAppear; 375 delete ele._hasDisAppear; 376 delete ele._appear; 377 }); 378 return this; 379 } 380 }, { 381 key: "fire", 382 value: function fire() { 383 if (!this.appearWatchElements) { 384 this.appearWatchElements = []; 385 } 386 var newElements = __getElements.call(this, "." + this.options.cls); 387 this.appearWatchElements = this.appearWatchElements.concat(newElements); 388 //初始化位置信息 389 __initBoundingRect.call(this, newElements); 390 __fire.call(this); 391 return this; 392 } 393 }]); 394 395 return Appear; 396 }(); 397 398 var appear = { 399 instances: [], 400 init: function init(opts) { 401 var instance = new Appear(opts); 402 this.instances.push(instance); 403 return instance; 404 }, 405 fireAll: function fireAll() { 406 var instances = this.instances; 407 instances.forEach(function (instance) { 408 instance.fire(); 409 }); 410 } 411 }; 412 413 exports.default = appear; 414 415 },{}],"amfe-appear":[function(require,module,exports){ 416 'use strict'; 417 418 /** 419 * @module amfeAppear 420 */ 421 422 /** 423 * @requires class:Appear 424 */ 425 426 Object.defineProperty(exports, "__esModule", { 427 value: true 428 }); 429 exports.appear = exports.version = undefined; 430 431 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; 432 433 var _appear = require('./appear'); 434 435 var _appear2 = _interopRequireDefault(_appear); 436 437 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 438 439 var version = '1.0.0'; 440 /*eslint-disable no-alert, no-console */ 441 442 /* istanbul ignore if */ 443 if (typeof alert === 'function' && (typeof console === 'undefined' ? 'undefined' : _typeof(console)) === 'object') { 444 console.log('bar'); 445 } 446 447 /*eslint-enable no-alert, no-console */ 448 449 exports. 450 /** 451 * version 452 * @type {string} 453 */ 454 version = version; 455 exports. 456 /** 457 * @type {Appear} 458 */ 459 appear = _appear2.default; 460 461 },{"./appear":1}]},{},[]) 462 </script>
效果图就不上了,就是懒加载的效果