vue-lazyload.esm.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. /*!
  2. * Vue-Lazyload.js v1.3.4
  3. * (c) 2021 Awe <hilongjw@gmail.com>
  4. * Released under the MIT License.
  5. */
  6. /*!
  7. * is-primitive <https://github.com/jonschlinkert/is-primitive>
  8. *
  9. * Copyright (c) 2014-2015, Jon Schlinkert.
  10. * Licensed under the MIT License.
  11. */
  12. // see http://jsperf.com/testing-value-is-primitive/7
  13. var isPrimitive = function isPrimitive(value) {
  14. return value == null || typeof value !== 'function' && typeof value !== 'object';
  15. };
  16. var isPrimitive$1 = /*#__PURE__*/Object.freeze({
  17. __proto__: null,
  18. 'default': isPrimitive,
  19. __moduleExports: isPrimitive
  20. });
  21. /*!
  22. * assign-symbols <https://github.com/jonschlinkert/assign-symbols>
  23. *
  24. * Copyright (c) 2015, Jon Schlinkert.
  25. * Licensed under the MIT License.
  26. */
  27. var assignSymbols = function (receiver, objects) {
  28. if (receiver === null || typeof receiver === 'undefined') {
  29. throw new TypeError('expected first argument to be an object.');
  30. }
  31. if (typeof objects === 'undefined' || typeof Symbol === 'undefined') {
  32. return receiver;
  33. }
  34. if (typeof Object.getOwnPropertySymbols !== 'function') {
  35. return receiver;
  36. }
  37. var isEnumerable = Object.prototype.propertyIsEnumerable;
  38. var target = Object(receiver);
  39. var len = arguments.length,
  40. i = 0;
  41. while (++i < len) {
  42. var provider = Object(arguments[i]);
  43. var names = Object.getOwnPropertySymbols(provider);
  44. for (var j = 0; j < names.length; j++) {
  45. var key = names[j];
  46. if (isEnumerable.call(provider, key)) {
  47. target[key] = provider[key];
  48. }
  49. }
  50. }
  51. return target;
  52. };
  53. var assignSymbols$1 = /*#__PURE__*/Object.freeze({
  54. __proto__: null,
  55. 'default': assignSymbols,
  56. __moduleExports: assignSymbols
  57. });
  58. var toString = Object.prototype.toString;
  59. /**
  60. * Get the native `typeof` a value.
  61. *
  62. * @param {*} `val`
  63. * @return {*} Native javascript type
  64. */
  65. var kindOf = function kindOf(val) {
  66. var type = typeof val;
  67. // primitivies
  68. if (type === 'undefined') {
  69. return 'undefined';
  70. }
  71. if (val === null) {
  72. return 'null';
  73. }
  74. if (val === true || val === false || val instanceof Boolean) {
  75. return 'boolean';
  76. }
  77. if (type === 'string' || val instanceof String) {
  78. return 'string';
  79. }
  80. if (type === 'number' || val instanceof Number) {
  81. return 'number';
  82. }
  83. // functions
  84. if (type === 'function' || val instanceof Function) {
  85. if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') {
  86. return 'generatorfunction';
  87. }
  88. return 'function';
  89. }
  90. // array
  91. if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) {
  92. return 'array';
  93. }
  94. // check for instances of RegExp and Date before calling `toString`
  95. if (val instanceof RegExp) {
  96. return 'regexp';
  97. }
  98. if (val instanceof Date) {
  99. return 'date';
  100. }
  101. // other objects
  102. type = toString.call(val);
  103. if (type === '[object RegExp]') {
  104. return 'regexp';
  105. }
  106. if (type === '[object Date]') {
  107. return 'date';
  108. }
  109. if (type === '[object Arguments]') {
  110. return 'arguments';
  111. }
  112. if (type === '[object Error]') {
  113. return 'error';
  114. }
  115. if (type === '[object Promise]') {
  116. return 'promise';
  117. }
  118. // buffer
  119. if (isBuffer(val)) {
  120. return 'buffer';
  121. }
  122. // es6: Map, WeakMap, Set, WeakSet
  123. if (type === '[object Set]') {
  124. return 'set';
  125. }
  126. if (type === '[object WeakSet]') {
  127. return 'weakset';
  128. }
  129. if (type === '[object Map]') {
  130. return 'map';
  131. }
  132. if (type === '[object WeakMap]') {
  133. return 'weakmap';
  134. }
  135. if (type === '[object Symbol]') {
  136. return 'symbol';
  137. }
  138. if (type === '[object Map Iterator]') {
  139. return 'mapiterator';
  140. }
  141. if (type === '[object Set Iterator]') {
  142. return 'setiterator';
  143. }
  144. if (type === '[object String Iterator]') {
  145. return 'stringiterator';
  146. }
  147. if (type === '[object Array Iterator]') {
  148. return 'arrayiterator';
  149. }
  150. // typed arrays
  151. if (type === '[object Int8Array]') {
  152. return 'int8array';
  153. }
  154. if (type === '[object Uint8Array]') {
  155. return 'uint8array';
  156. }
  157. if (type === '[object Uint8ClampedArray]') {
  158. return 'uint8clampedarray';
  159. }
  160. if (type === '[object Int16Array]') {
  161. return 'int16array';
  162. }
  163. if (type === '[object Uint16Array]') {
  164. return 'uint16array';
  165. }
  166. if (type === '[object Int32Array]') {
  167. return 'int32array';
  168. }
  169. if (type === '[object Uint32Array]') {
  170. return 'uint32array';
  171. }
  172. if (type === '[object Float32Array]') {
  173. return 'float32array';
  174. }
  175. if (type === '[object Float64Array]') {
  176. return 'float64array';
  177. }
  178. // must be a plain object
  179. return 'object';
  180. };
  181. /**
  182. * If you need to support Safari 5-7 (8-10 yr-old browser),
  183. * take a look at https://github.com/feross/is-buffer
  184. */
  185. function isBuffer(val) {
  186. return val.constructor && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
  187. }
  188. var kindOf$1 = /*#__PURE__*/Object.freeze({
  189. __proto__: null,
  190. 'default': kindOf,
  191. __moduleExports: kindOf
  192. });
  193. var isPrimitive$2 = ( isPrimitive$1 && isPrimitive ) || isPrimitive$1;
  194. var assignSymbols$2 = ( assignSymbols$1 && assignSymbols ) || assignSymbols$1;
  195. var typeOf = ( kindOf$1 && kindOf ) || kindOf$1;
  196. function assign(target /*, objects*/) {
  197. target = target || {};
  198. var len = arguments.length,
  199. i = 0;
  200. if (len === 1) {
  201. return target;
  202. }
  203. while (++i < len) {
  204. var val = arguments[i];
  205. if (isPrimitive$2(target)) {
  206. target = val;
  207. }
  208. if (isObject(val)) {
  209. extend(target, val);
  210. }
  211. }
  212. return target;
  213. }
  214. /**
  215. * Shallow extend
  216. */
  217. function extend(target, obj) {
  218. assignSymbols$2(target, obj);
  219. for (var key in obj) {
  220. if (isValidKey(key) && hasOwn(obj, key)) {
  221. var val = obj[key];
  222. if (isObject(val)) {
  223. if (typeOf(target[key]) === 'undefined' && typeOf(val) === 'function') {
  224. target[key] = val;
  225. }
  226. target[key] = assign(target[key] || {}, val);
  227. } else {
  228. target[key] = val;
  229. }
  230. }
  231. }
  232. return target;
  233. }
  234. /**
  235. * Returns true if the object is a plain object or a function.
  236. */
  237. function isObject(obj) {
  238. return typeOf(obj) === 'object' || typeOf(obj) === 'function';
  239. }
  240. /**
  241. * Returns true if the given `key` is an own property of `obj`.
  242. */
  243. function hasOwn(obj, key) {
  244. return Object.prototype.hasOwnProperty.call(obj, key);
  245. }
  246. /**
  247. * Returns true if the given `key` is a valid key that can be used for assigning properties.
  248. */
  249. function isValidKey(key) {
  250. return key !== '__proto__' && key !== 'constructor' && key !== 'prototype';
  251. }
  252. /**
  253. * Expose `assign`
  254. */
  255. var assignDeep = assign;
  256. const inBrowser = typeof window !== 'undefined' && window !== null;
  257. const hasIntersectionObserver = checkIntersectionObserver();
  258. function checkIntersectionObserver() {
  259. if (inBrowser && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
  260. // Minimal polyfill for Edge 15's lack of `isIntersecting`
  261. // See: https://github.com/w3c/IntersectionObserver/issues/211
  262. if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
  263. Object.defineProperty(window.IntersectionObserverEntry.prototype, 'isIntersecting', {
  264. get: function () {
  265. return this.intersectionRatio > 0;
  266. }
  267. });
  268. }
  269. return true;
  270. }
  271. return false;
  272. }
  273. const modeType = {
  274. event: 'event',
  275. observer: 'observer'
  276. // CustomEvent polyfill for IE
  277. };const CustomEvent = function () {
  278. if (!inBrowser) return;
  279. // not IE
  280. if (typeof window.CustomEvent === 'function') return window.CustomEvent;
  281. function CustomEvent(event, params) {
  282. params = params || { bubbles: false, cancelable: false, detail: undefined };
  283. var evt = document.createEvent('CustomEvent');
  284. evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
  285. return evt;
  286. }
  287. CustomEvent.prototype = window.Event.prototype;
  288. return CustomEvent;
  289. }();
  290. function remove(arr, item) {
  291. if (!arr.length) return;
  292. const index = arr.indexOf(item);
  293. if (index > -1) return arr.splice(index, 1);
  294. }
  295. function some(arr, fn) {
  296. let has = false;
  297. for (let i = 0, len = arr.length; i < len; i++) {
  298. if (fn(arr[i])) {
  299. has = true;
  300. break;
  301. }
  302. }
  303. return has;
  304. }
  305. function getBestSelectionFromSrcset(el, scale) {
  306. if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return;
  307. let options = el.getAttribute('data-srcset');
  308. const result = [];
  309. const container = el.parentNode;
  310. const containerWidth = container.offsetWidth * scale;
  311. let spaceIndex;
  312. let tmpSrc;
  313. let tmpWidth;
  314. options = options.trim().split(',');
  315. options.map(item => {
  316. item = item.trim();
  317. spaceIndex = item.lastIndexOf(' ');
  318. if (spaceIndex === -1) {
  319. tmpSrc = item;
  320. tmpWidth = 999998;
  321. } else {
  322. tmpSrc = item.substr(0, spaceIndex);
  323. tmpWidth = parseInt(item.substr(spaceIndex + 1, item.length - spaceIndex - 2), 10);
  324. }
  325. result.push([tmpWidth, tmpSrc]);
  326. });
  327. result.sort(function (a, b) {
  328. if (a[0] < b[0]) {
  329. return 1;
  330. }
  331. if (a[0] > b[0]) {
  332. return -1;
  333. }
  334. if (a[0] === b[0]) {
  335. if (b[1].indexOf('.webp', b[1].length - 5) !== -1) {
  336. return 1;
  337. }
  338. if (a[1].indexOf('.webp', a[1].length - 5) !== -1) {
  339. return -1;
  340. }
  341. }
  342. return 0;
  343. });
  344. let bestSelectedSrc = '';
  345. let tmpOption;
  346. for (let i = 0; i < result.length; i++) {
  347. tmpOption = result[i];
  348. bestSelectedSrc = tmpOption[1];
  349. const next = result[i + 1];
  350. if (next && next[0] < containerWidth) {
  351. bestSelectedSrc = tmpOption[1];
  352. break;
  353. } else if (!next) {
  354. bestSelectedSrc = tmpOption[1];
  355. break;
  356. }
  357. }
  358. return bestSelectedSrc;
  359. }
  360. function find(arr, fn) {
  361. let item;
  362. for (let i = 0, len = arr.length; i < len; i++) {
  363. if (fn(arr[i])) {
  364. item = arr[i];
  365. break;
  366. }
  367. }
  368. return item;
  369. }
  370. const getDPR = (scale = 1) => inBrowser ? window.devicePixelRatio || scale : scale;
  371. function supportWebp() {
  372. if (!inBrowser) return false;
  373. let support = true;
  374. try {
  375. const elem = document.createElement('canvas');
  376. if (elem.getContext && elem.getContext('2d')) {
  377. support = elem.toDataURL('image/webp').indexOf('data:image/webp') === 0;
  378. }
  379. } catch (err) {
  380. support = false;
  381. }
  382. return support;
  383. }
  384. function throttle(action, delay) {
  385. let timeout = null;
  386. let movement = null;
  387. let lastRun = 0;
  388. let needRun = false;
  389. return function () {
  390. needRun = true;
  391. if (timeout) {
  392. return;
  393. }
  394. let elapsed = Date.now() - lastRun;
  395. let context = this;
  396. let args = arguments;
  397. let runCallback = function () {
  398. lastRun = Date.now();
  399. timeout = false;
  400. action.apply(context, args);
  401. };
  402. if (elapsed >= delay) {
  403. runCallback();
  404. } else {
  405. timeout = setTimeout(runCallback, delay);
  406. }
  407. if (needRun) {
  408. clearTimeout(movement);
  409. movement = setTimeout(runCallback, 2 * delay);
  410. }
  411. };
  412. }
  413. function testSupportsPassive() {
  414. if (!inBrowser) return;
  415. let support = false;
  416. try {
  417. let opts = Object.defineProperty({}, 'passive', {
  418. get: function () {
  419. support = true;
  420. }
  421. });
  422. window.addEventListener('test', null, opts);
  423. } catch (e) {}
  424. return support;
  425. }
  426. const supportsPassive = testSupportsPassive();
  427. const _ = {
  428. on(el, type, func, capture = false) {
  429. if (supportsPassive) {
  430. el.addEventListener(type, func, {
  431. capture: capture,
  432. passive: true
  433. });
  434. } else {
  435. el.addEventListener(type, func, capture);
  436. }
  437. },
  438. off(el, type, func, capture = false) {
  439. el.removeEventListener(type, func, capture);
  440. }
  441. };
  442. const loadImageAsync = (item, resolve, reject) => {
  443. let image = new Image();
  444. if (!item || !item.src) {
  445. const err = new Error('image src is required');
  446. return reject(err);
  447. }
  448. image.src = item.src;
  449. if (item.cors) {
  450. image.crossOrigin = item.cors;
  451. }
  452. image.onload = function () {
  453. resolve({
  454. naturalHeight: image.naturalHeight,
  455. naturalWidth: image.naturalWidth,
  456. src: image.src
  457. });
  458. };
  459. image.onerror = function (e) {
  460. reject(e);
  461. };
  462. };
  463. const style = (el, prop) => {
  464. return typeof getComputedStyle !== 'undefined' ? getComputedStyle(el, null).getPropertyValue(prop) : el.style[prop];
  465. };
  466. const overflow = el => {
  467. return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x');
  468. };
  469. const scrollParent = el => {
  470. if (!inBrowser) return;
  471. if (!(el instanceof HTMLElement)) {
  472. return window;
  473. }
  474. let parent = el;
  475. while (parent) {
  476. if (parent === document.body || parent === document.documentElement) {
  477. break;
  478. }
  479. if (!parent.parentNode) {
  480. break;
  481. }
  482. if (/(scroll|auto)/.test(overflow(parent))) {
  483. return parent;
  484. }
  485. parent = parent.parentNode;
  486. }
  487. return window;
  488. };
  489. function isObject$1(obj) {
  490. return obj !== null && typeof obj === 'object';
  491. }
  492. function ObjectKeys(obj) {
  493. if (!(obj instanceof Object)) return [];
  494. if (Object.keys) {
  495. return Object.keys(obj);
  496. } else {
  497. let keys = [];
  498. for (let key in obj) {
  499. if (obj.hasOwnProperty(key)) {
  500. keys.push(key);
  501. }
  502. }
  503. return keys;
  504. }
  505. }
  506. function ArrayFrom(arrLike) {
  507. let len = arrLike.length;
  508. const list = [];
  509. for (let i = 0; i < len; i++) {
  510. list.push(arrLike[i]);
  511. }
  512. return list;
  513. }
  514. function noop() {}
  515. class ImageCache {
  516. constructor({ max }) {
  517. this.options = {
  518. max: max || 100
  519. };
  520. this._caches = [];
  521. }
  522. has(key) {
  523. return this._caches.indexOf(key) > -1;
  524. }
  525. add(key) {
  526. if (this.has(key)) return;
  527. this._caches.push(key);
  528. if (this._caches.length > this.options.max) {
  529. this.free();
  530. }
  531. }
  532. free() {
  533. this._caches.shift();
  534. }
  535. }
  536. // el: {
  537. // state,
  538. // src,
  539. // error,
  540. // loading
  541. // }
  542. class ReactiveListener {
  543. constructor({ el, src, error, loading, bindType, $parent, options, cors, elRenderer, imageCache }) {
  544. this.el = el;
  545. this.src = src;
  546. this.error = error;
  547. this.loading = loading;
  548. this.bindType = bindType;
  549. this.attempt = 0;
  550. this.cors = cors;
  551. this.naturalHeight = 0;
  552. this.naturalWidth = 0;
  553. this.options = options;
  554. this.rect = null;
  555. this.$parent = $parent;
  556. this.elRenderer = elRenderer;
  557. this._imageCache = imageCache;
  558. this.performanceData = {
  559. init: Date.now(),
  560. loadStart: 0,
  561. loadEnd: 0
  562. };
  563. this.filter();
  564. this.initState();
  565. this.render('loading', false);
  566. }
  567. /*
  568. * init listener state
  569. * @return
  570. */
  571. initState() {
  572. if ('dataset' in this.el) {
  573. this.el.dataset.src = this.src;
  574. } else {
  575. this.el.setAttribute('data-src', this.src);
  576. }
  577. this.state = {
  578. loading: false,
  579. error: false,
  580. loaded: false,
  581. rendered: false
  582. };
  583. }
  584. /*
  585. * record performance
  586. * @return
  587. */
  588. record(event) {
  589. this.performanceData[event] = Date.now();
  590. }
  591. /*
  592. * update image listener data
  593. * @param {String} image uri
  594. * @param {String} loading image uri
  595. * @param {String} error image uri
  596. * @return
  597. */
  598. update({ src, loading, error }) {
  599. const oldSrc = this.src;
  600. this.src = src;
  601. this.loading = loading;
  602. this.error = error;
  603. this.filter();
  604. if (oldSrc !== this.src) {
  605. this.attempt = 0;
  606. this.initState();
  607. }
  608. }
  609. /*
  610. * get el node rect
  611. * @return
  612. */
  613. getRect() {
  614. this.rect = this.el.getBoundingClientRect();
  615. }
  616. /*
  617. * check el is in view
  618. * @return {Boolean} el is in view
  619. */
  620. checkInView() {
  621. this.getRect();
  622. return this.rect.top < window.innerHeight * this.options.preLoad && this.rect.bottom > this.options.preLoadTop && this.rect.left < window.innerWidth * this.options.preLoad && this.rect.right > 0;
  623. }
  624. /*
  625. * listener filter
  626. */
  627. filter() {
  628. ObjectKeys(this.options.filter).map(key => {
  629. this.options.filter[key](this, this.options);
  630. });
  631. }
  632. /*
  633. * render loading first
  634. * @params cb:Function
  635. * @return
  636. */
  637. renderLoading(cb) {
  638. this.state.loading = true;
  639. loadImageAsync({
  640. src: this.loading,
  641. cors: this.cors
  642. }, data => {
  643. this.render('loading', false);
  644. this.state.loading = false;
  645. cb();
  646. }, () => {
  647. // handler `loading image` load failed
  648. cb();
  649. this.state.loading = false;
  650. if (!this.options.silent) console.warn(`VueLazyload log: load failed with loading image(${this.loading})`);
  651. });
  652. }
  653. /*
  654. * try load image and render it
  655. * @return
  656. */
  657. load(onFinish = noop) {
  658. if (this.attempt > this.options.attempt - 1 && this.state.error) {
  659. if (!this.options.silent) console.log(`VueLazyload log: ${this.src} tried too more than ${this.options.attempt} times`);
  660. onFinish();
  661. return;
  662. }
  663. if (this.state.rendered && this.state.loaded) return;
  664. if (this._imageCache.has(this.src)) {
  665. this.state.loaded = true;
  666. this.render('loaded', true);
  667. this.state.rendered = true;
  668. return onFinish();
  669. }
  670. this.renderLoading(() => {
  671. this.attempt++;
  672. this.options.adapter['beforeLoad'] && this.options.adapter['beforeLoad'](this, this.options);
  673. this.record('loadStart');
  674. loadImageAsync({
  675. src: this.src,
  676. cors: this.cors
  677. }, data => {
  678. this.naturalHeight = data.naturalHeight;
  679. this.naturalWidth = data.naturalWidth;
  680. this.state.loaded = true;
  681. this.state.error = false;
  682. this.record('loadEnd');
  683. this.render('loaded', false);
  684. this.state.rendered = true;
  685. this._imageCache.add(this.src);
  686. onFinish();
  687. }, err => {
  688. !this.options.silent && console.error(err);
  689. this.state.error = true;
  690. this.state.loaded = false;
  691. this.render('error', false);
  692. });
  693. });
  694. }
  695. /*
  696. * render image
  697. * @param {String} state to render // ['loading', 'src', 'error']
  698. * @param {String} is form cache
  699. * @return
  700. */
  701. render(state, cache) {
  702. this.elRenderer(this, state, cache);
  703. }
  704. /*
  705. * output performance data
  706. * @return {Object} performance data
  707. */
  708. performance() {
  709. let state = 'loading';
  710. let time = 0;
  711. if (this.state.loaded) {
  712. state = 'loaded';
  713. time = (this.performanceData.loadEnd - this.performanceData.loadStart) / 1000;
  714. }
  715. if (this.state.error) state = 'error';
  716. return {
  717. src: this.src,
  718. state,
  719. time
  720. };
  721. }
  722. /*
  723. * $destroy
  724. * @return
  725. */
  726. $destroy() {
  727. this.el = null;
  728. this.src = null;
  729. this.error = null;
  730. this.loading = null;
  731. this.bindType = null;
  732. this.attempt = 0;
  733. }
  734. }
  735. const DEFAULT_URL = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
  736. const DEFAULT_EVENTS = ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend', 'touchmove'];
  737. const DEFAULT_OBSERVER_OPTIONS = {
  738. rootMargin: '0px',
  739. threshold: 0
  740. };
  741. function Lazy(Vue) {
  742. return class Lazy {
  743. constructor({ preLoad, error, throttleWait, preLoadTop, dispatchEvent, loading, attempt, silent = true, scale, listenEvents, hasbind, filter, adapter, observer, observerOptions }) {
  744. this.version = '"1.3.4"';
  745. this.mode = modeType.event;
  746. this.ListenerQueue = [];
  747. this.TargetIndex = 0;
  748. this.TargetQueue = [];
  749. this.options = {
  750. silent: silent,
  751. dispatchEvent: !!dispatchEvent,
  752. throttleWait: throttleWait || 200,
  753. preLoad: preLoad || 1.3,
  754. preLoadTop: preLoadTop || 0,
  755. error: error || DEFAULT_URL,
  756. loading: loading || DEFAULT_URL,
  757. attempt: attempt || 3,
  758. scale: scale || getDPR(scale),
  759. ListenEvents: listenEvents || DEFAULT_EVENTS,
  760. hasbind: false,
  761. supportWebp: supportWebp(),
  762. filter: filter || {},
  763. adapter: adapter || {},
  764. observer: !!observer,
  765. observerOptions: observerOptions || DEFAULT_OBSERVER_OPTIONS
  766. };
  767. this._initEvent();
  768. this._imageCache = new ImageCache({ max: 200 });
  769. this.lazyLoadHandler = throttle(this._lazyLoadHandler.bind(this), this.options.throttleWait);
  770. this.setMode(this.options.observer ? modeType.observer : modeType.event);
  771. }
  772. /**
  773. * update config
  774. * @param {Object} config params
  775. * @return
  776. */
  777. config(options = {}) {
  778. assignDeep(this.options, options);
  779. }
  780. /**
  781. * output listener's load performance
  782. * @return {Array}
  783. */
  784. performance() {
  785. let list = [];
  786. this.ListenerQueue.map(item => {
  787. list.push(item.performance());
  788. });
  789. return list;
  790. }
  791. /*
  792. * add lazy component to queue
  793. * @param {Vue} vm lazy component instance
  794. * @return
  795. */
  796. addLazyBox(vm) {
  797. this.ListenerQueue.push(vm);
  798. if (inBrowser) {
  799. this._addListenerTarget(window);
  800. this._observer && this._observer.observe(vm.el);
  801. if (vm.$el && vm.$el.parentNode) {
  802. this._addListenerTarget(vm.$el.parentNode);
  803. }
  804. }
  805. }
  806. /*
  807. * add image listener to queue
  808. * @param {DOM} el
  809. * @param {object} binding vue directive binding
  810. * @param {vnode} vnode vue directive vnode
  811. * @return
  812. */
  813. add(el, binding, vnode) {
  814. if (some(this.ListenerQueue, item => item.el === el)) {
  815. this.update(el, binding);
  816. return Vue.nextTick(this.lazyLoadHandler);
  817. }
  818. let { src, loading, error, cors } = this._valueFormatter(binding.value);
  819. Vue.nextTick(() => {
  820. src = getBestSelectionFromSrcset(el, this.options.scale) || src;
  821. this._observer && this._observer.observe(el);
  822. const container = Object.keys(binding.modifiers)[0];
  823. let $parent;
  824. if (container) {
  825. $parent = vnode.context.$refs[container];
  826. // if there is container passed in, try ref first, then fallback to getElementById to support the original usage
  827. $parent = $parent ? $parent.$el || $parent : document.getElementById(container);
  828. }
  829. if (!$parent) {
  830. $parent = scrollParent(el);
  831. }
  832. const newListener = new ReactiveListener({
  833. bindType: binding.arg,
  834. $parent,
  835. el,
  836. loading,
  837. error,
  838. src,
  839. cors,
  840. elRenderer: this._elRenderer.bind(this),
  841. options: this.options,
  842. imageCache: this._imageCache
  843. });
  844. this.ListenerQueue.push(newListener);
  845. if (inBrowser) {
  846. this._addListenerTarget(window);
  847. this._addListenerTarget($parent);
  848. }
  849. this.lazyLoadHandler();
  850. Vue.nextTick(() => this.lazyLoadHandler());
  851. });
  852. }
  853. /**
  854. * update image src
  855. * @param {DOM} el
  856. * @param {object} vue directive binding
  857. * @return
  858. */
  859. update(el, binding, vnode) {
  860. let { src, loading, error } = this._valueFormatter(binding.value);
  861. src = getBestSelectionFromSrcset(el, this.options.scale) || src;
  862. const exist = find(this.ListenerQueue, item => item.el === el);
  863. if (!exist) {
  864. this.add(el, binding, vnode);
  865. } else {
  866. exist.update({
  867. src,
  868. loading,
  869. error
  870. });
  871. }
  872. if (this._observer) {
  873. this._observer.unobserve(el);
  874. this._observer.observe(el);
  875. }
  876. this.lazyLoadHandler();
  877. Vue.nextTick(() => this.lazyLoadHandler());
  878. }
  879. /**
  880. * remove listener form list
  881. * @param {DOM} el
  882. * @return
  883. */
  884. remove(el) {
  885. if (!el) return;
  886. this._observer && this._observer.unobserve(el);
  887. const existItem = find(this.ListenerQueue, item => item.el === el);
  888. if (existItem) {
  889. this._removeListenerTarget(existItem.$parent);
  890. this._removeListenerTarget(window);
  891. remove(this.ListenerQueue, existItem);
  892. existItem.$destroy();
  893. }
  894. }
  895. /*
  896. * remove lazy components form list
  897. * @param {Vue} vm Vue instance
  898. * @return
  899. */
  900. removeComponent(vm) {
  901. if (!vm) return;
  902. remove(this.ListenerQueue, vm);
  903. this._observer && this._observer.unobserve(vm.el);
  904. if (vm.$parent && vm.$el.parentNode) {
  905. this._removeListenerTarget(vm.$el.parentNode);
  906. }
  907. this._removeListenerTarget(window);
  908. }
  909. setMode(mode) {
  910. if (!hasIntersectionObserver && mode === modeType.observer) {
  911. mode = modeType.event;
  912. }
  913. this.mode = mode; // event or observer
  914. if (mode === modeType.event) {
  915. if (this._observer) {
  916. this.ListenerQueue.forEach(listener => {
  917. this._observer.unobserve(listener.el);
  918. });
  919. this._observer = null;
  920. }
  921. this.TargetQueue.forEach(target => {
  922. this._initListen(target.el, true);
  923. });
  924. } else {
  925. this.TargetQueue.forEach(target => {
  926. this._initListen(target.el, false);
  927. });
  928. this._initIntersectionObserver();
  929. }
  930. }
  931. /*
  932. *** Private functions ***
  933. */
  934. /*
  935. * add listener target
  936. * @param {DOM} el listener target
  937. * @return
  938. */
  939. _addListenerTarget(el) {
  940. if (!el) return;
  941. let target = find(this.TargetQueue, target => target.el === el);
  942. if (!target) {
  943. target = {
  944. el: el,
  945. id: ++this.TargetIndex,
  946. childrenCount: 1,
  947. listened: true
  948. };
  949. this.mode === modeType.event && this._initListen(target.el, true);
  950. this.TargetQueue.push(target);
  951. } else {
  952. target.childrenCount++;
  953. }
  954. return this.TargetIndex;
  955. }
  956. /*
  957. * remove listener target or reduce target childrenCount
  958. * @param {DOM} el or window
  959. * @return
  960. */
  961. _removeListenerTarget(el) {
  962. this.TargetQueue.forEach((target, index) => {
  963. if (target.el === el) {
  964. target.childrenCount--;
  965. if (!target.childrenCount) {
  966. this._initListen(target.el, false);
  967. this.TargetQueue.splice(index, 1);
  968. target = null;
  969. }
  970. }
  971. });
  972. }
  973. /*
  974. * add or remove eventlistener
  975. * @param {DOM} el DOM or Window
  976. * @param {boolean} start flag
  977. * @return
  978. */
  979. _initListen(el, start) {
  980. this.options.ListenEvents.forEach(evt => _[start ? 'on' : 'off'](el, evt, this.lazyLoadHandler));
  981. }
  982. _initEvent() {
  983. this.Event = {
  984. listeners: {
  985. loading: [],
  986. loaded: [],
  987. error: []
  988. }
  989. };
  990. this.$on = (event, func) => {
  991. if (!this.Event.listeners[event]) this.Event.listeners[event] = [];
  992. this.Event.listeners[event].push(func);
  993. };
  994. this.$once = (event, func) => {
  995. const vm = this;
  996. function on() {
  997. vm.$off(event, on);
  998. func.apply(vm, arguments);
  999. }
  1000. this.$on(event, on);
  1001. };
  1002. this.$off = (event, func) => {
  1003. if (!func) {
  1004. if (!this.Event.listeners[event]) return;
  1005. this.Event.listeners[event].length = 0;
  1006. return;
  1007. }
  1008. remove(this.Event.listeners[event], func);
  1009. };
  1010. this.$emit = (event, context, inCache) => {
  1011. if (!this.Event.listeners[event]) return;
  1012. this.Event.listeners[event].forEach(func => func(context, inCache));
  1013. };
  1014. }
  1015. /**
  1016. * find nodes which in viewport and trigger load
  1017. * @return
  1018. */
  1019. _lazyLoadHandler() {
  1020. const freeList = [];
  1021. this.ListenerQueue.forEach((listener, index) => {
  1022. if (!listener.el || !listener.el.parentNode) {
  1023. freeList.push(listener);
  1024. }
  1025. const catIn = listener.checkInView();
  1026. if (!catIn) return;
  1027. listener.load();
  1028. });
  1029. freeList.forEach(item => {
  1030. remove(this.ListenerQueue, item);
  1031. item.$destroy();
  1032. });
  1033. }
  1034. /**
  1035. * init IntersectionObserver
  1036. * set mode to observer
  1037. * @return
  1038. */
  1039. _initIntersectionObserver() {
  1040. if (!hasIntersectionObserver) return;
  1041. this._observer = new IntersectionObserver(this._observerHandler.bind(this), this.options.observerOptions);
  1042. if (this.ListenerQueue.length) {
  1043. this.ListenerQueue.forEach(listener => {
  1044. this._observer.observe(listener.el);
  1045. });
  1046. }
  1047. }
  1048. /**
  1049. * init IntersectionObserver
  1050. * @return
  1051. */
  1052. _observerHandler(entries, observer) {
  1053. entries.forEach(entry => {
  1054. if (entry.isIntersecting) {
  1055. this.ListenerQueue.forEach(listener => {
  1056. if (listener.el === entry.target) {
  1057. if (listener.state.loaded) return this._observer.unobserve(listener.el);
  1058. listener.load();
  1059. }
  1060. });
  1061. }
  1062. });
  1063. }
  1064. /**
  1065. * set element attribute with image'url and state
  1066. * @param {object} lazyload listener object
  1067. * @param {string} state will be rendered
  1068. * @param {bool} inCache is rendered from cache
  1069. * @return
  1070. */
  1071. _elRenderer(listener, state, cache) {
  1072. if (!listener.el) return;
  1073. const { el, bindType } = listener;
  1074. let src;
  1075. switch (state) {
  1076. case 'loading':
  1077. src = listener.loading;
  1078. break;
  1079. case 'error':
  1080. src = listener.error;
  1081. break;
  1082. default:
  1083. src = listener.src;
  1084. break;
  1085. }
  1086. if (bindType) {
  1087. el.style[bindType] = 'url("' + src + '")';
  1088. } else if (el.getAttribute('src') !== src) {
  1089. el.setAttribute('src', src);
  1090. }
  1091. el.setAttribute('lazy', state);
  1092. this.$emit(state, listener, cache);
  1093. this.options.adapter[state] && this.options.adapter[state](listener, this.options);
  1094. if (this.options.dispatchEvent) {
  1095. const event = new CustomEvent(state, {
  1096. detail: listener
  1097. });
  1098. el.dispatchEvent(event);
  1099. }
  1100. }
  1101. /**
  1102. * generate loading loaded error image url
  1103. * @param {string} image's src
  1104. * @return {object} image's loading, loaded, error url
  1105. */
  1106. _valueFormatter(value) {
  1107. let src = value;
  1108. let loading = this.options.loading;
  1109. let error = this.options.error;
  1110. // value is object
  1111. if (isObject$1(value)) {
  1112. if (!value.src && !this.options.silent) console.error('Vue Lazyload warning: miss src with ' + value);
  1113. src = value.src;
  1114. loading = value.loading || this.options.loading;
  1115. error = value.error || this.options.error;
  1116. }
  1117. return {
  1118. src,
  1119. loading,
  1120. error
  1121. };
  1122. }
  1123. };
  1124. }
  1125. Lazy.install = (Vue, options = {}) => {
  1126. const LazyClass = Lazy(Vue);
  1127. const lazy = new LazyClass(options);
  1128. const isVue2 = Vue.version.split('.')[0] === '2';
  1129. if (isVue2) {
  1130. Vue.directive('lazy', {
  1131. bind: lazy.add.bind(lazy),
  1132. update: lazy.update.bind(lazy),
  1133. componentUpdated: lazy.lazyLoadHandler.bind(lazy),
  1134. unbind: lazy.remove.bind(lazy)
  1135. });
  1136. } else {
  1137. Vue.directive('lazy', {
  1138. bind: lazy.lazyLoadHandler.bind(lazy),
  1139. update(newValue, oldValue) {
  1140. assignDeep(this.vm.$refs, this.vm.$els);
  1141. lazy.add(this.el, {
  1142. modifiers: this.modifiers || {},
  1143. arg: this.arg,
  1144. value: newValue,
  1145. oldValue: oldValue
  1146. }, {
  1147. context: this.vm
  1148. });
  1149. },
  1150. unbind() {
  1151. lazy.remove(this.el);
  1152. }
  1153. });
  1154. }
  1155. };
  1156. const LazyComponent = lazy => {
  1157. return {
  1158. props: {
  1159. tag: {
  1160. type: String,
  1161. default: 'div'
  1162. }
  1163. },
  1164. render(h) {
  1165. return h(this.tag, null, this.show ? this.$slots.default : null);
  1166. },
  1167. data() {
  1168. return {
  1169. el: null,
  1170. state: {
  1171. loaded: false
  1172. },
  1173. rect: {},
  1174. show: false
  1175. };
  1176. },
  1177. mounted() {
  1178. this.el = this.$el;
  1179. lazy.addLazyBox(this);
  1180. lazy.lazyLoadHandler();
  1181. },
  1182. beforeDestroy() {
  1183. lazy.removeComponent(this);
  1184. },
  1185. methods: {
  1186. getRect() {
  1187. this.rect = this.$el.getBoundingClientRect();
  1188. },
  1189. checkInView() {
  1190. this.getRect();
  1191. return inBrowser && this.rect.top < window.innerHeight * lazy.options.preLoad && this.rect.bottom > 0 && this.rect.left < window.innerWidth * lazy.options.preLoad && this.rect.right > 0;
  1192. },
  1193. load() {
  1194. this.show = true;
  1195. this.state.loaded = true;
  1196. this.$emit('show', this);
  1197. },
  1198. destroy() {
  1199. return this.$destroy;
  1200. }
  1201. }
  1202. };
  1203. };
  1204. LazyComponent.install = function (Vue, options = {}) {
  1205. const LazyClass = Lazy(Vue);
  1206. const lazy = new LazyClass(options);
  1207. Vue.component('lazy-component', LazyComponent(lazy));
  1208. };
  1209. class LazyContainerMananger {
  1210. constructor({ lazy }) {
  1211. this.lazy = lazy;
  1212. lazy.lazyContainerMananger = this;
  1213. this._queue = [];
  1214. }
  1215. bind(el, binding, vnode) {
  1216. const container = new LazyContainer({ el, binding, vnode, lazy: this.lazy });
  1217. this._queue.push(container);
  1218. }
  1219. update(el, binding, vnode) {
  1220. const container = find(this._queue, item => item.el === el);
  1221. if (!container) return;
  1222. container.update({ el, binding, vnode });
  1223. }
  1224. unbind(el, binding, vnode) {
  1225. const container = find(this._queue, item => item.el === el);
  1226. if (!container) return;
  1227. container.clear();
  1228. remove(this._queue, container);
  1229. }
  1230. }
  1231. const defaultOptions = {
  1232. selector: 'img'
  1233. };
  1234. class LazyContainer {
  1235. constructor({ el, binding, vnode, lazy }) {
  1236. this.el = null;
  1237. this.vnode = vnode;
  1238. this.binding = binding;
  1239. this.options = {};
  1240. this.lazy = lazy;
  1241. this._queue = [];
  1242. this.update({ el, binding });
  1243. }
  1244. update({ el, binding }) {
  1245. this.el = el;
  1246. this.options = assignDeep({}, defaultOptions, binding.value);
  1247. const imgs = this.getImgs();
  1248. imgs.forEach(el => {
  1249. this.lazy.add(el, assignDeep({}, this.binding, {
  1250. value: {
  1251. src: 'dataset' in el ? el.dataset.src : el.getAttribute('data-src'),
  1252. error: ('dataset' in el ? el.dataset.error : el.getAttribute('data-error')) || this.options.error,
  1253. loading: ('dataset' in el ? el.dataset.loading : el.getAttribute('data-loading')) || this.options.loading
  1254. }
  1255. }), this.vnode);
  1256. });
  1257. }
  1258. getImgs() {
  1259. return ArrayFrom(this.el.querySelectorAll(this.options.selector));
  1260. }
  1261. clear() {
  1262. const imgs = this.getImgs();
  1263. imgs.forEach(el => this.lazy.remove(el));
  1264. this.vnode = null;
  1265. this.binding = null;
  1266. this.lazy = null;
  1267. }
  1268. }
  1269. LazyContainer.install = (Vue, options = {}) => {
  1270. const LazyClass = Lazy(Vue);
  1271. const lazy = new LazyClass(options);
  1272. const lazyContainer = new LazyContainer({ lazy });
  1273. const isVue2 = Vue.version.split('.')[0] === '2';
  1274. if (isVue2) {
  1275. Vue.directive('lazy-container', {
  1276. bind: lazyContainer.bind.bind(lazyContainer),
  1277. componentUpdated: lazyContainer.update.bind(lazyContainer),
  1278. unbind: lazyContainer.unbind.bind(lazyContainer)
  1279. });
  1280. } else {
  1281. Vue.directive('lazy-container', {
  1282. update(newValue, oldValue) {
  1283. lazyContainer.update(this.el, {
  1284. modifiers: this.modifiers || {},
  1285. arg: this.arg,
  1286. value: newValue,
  1287. oldValue: oldValue
  1288. }, {
  1289. context: this.vm
  1290. });
  1291. },
  1292. unbind() {
  1293. lazyContainer.unbind(this.el);
  1294. }
  1295. });
  1296. }
  1297. };
  1298. const LazyImage = lazyManager => {
  1299. return {
  1300. props: {
  1301. src: [String, Object],
  1302. tag: {
  1303. type: String,
  1304. default: 'img'
  1305. }
  1306. },
  1307. render(h) {
  1308. return h(this.tag, {
  1309. attrs: {
  1310. src: this.renderSrc
  1311. }
  1312. }, this.$slots.default);
  1313. },
  1314. data() {
  1315. return {
  1316. el: null,
  1317. options: {
  1318. src: '',
  1319. error: '',
  1320. loading: '',
  1321. attempt: lazyManager.options.attempt
  1322. },
  1323. state: {
  1324. loaded: false,
  1325. error: false,
  1326. attempt: 0
  1327. },
  1328. rect: {},
  1329. renderSrc: ''
  1330. };
  1331. },
  1332. watch: {
  1333. src() {
  1334. this.init();
  1335. lazyManager.addLazyBox(this);
  1336. lazyManager.lazyLoadHandler();
  1337. }
  1338. },
  1339. created() {
  1340. this.init();
  1341. this.renderSrc = this.options.loading;
  1342. },
  1343. mounted() {
  1344. this.el = this.$el;
  1345. lazyManager.addLazyBox(this);
  1346. lazyManager.lazyLoadHandler();
  1347. },
  1348. beforeDestroy() {
  1349. lazyManager.removeComponent(this);
  1350. },
  1351. methods: {
  1352. init() {
  1353. const { src, loading, error } = lazyManager._valueFormatter(this.src);
  1354. this.state.loaded = false;
  1355. this.options.src = src;
  1356. this.options.error = error;
  1357. this.options.loading = loading;
  1358. this.renderSrc = this.options.loading;
  1359. },
  1360. getRect() {
  1361. this.rect = this.$el.getBoundingClientRect();
  1362. },
  1363. checkInView() {
  1364. this.getRect();
  1365. return inBrowser && this.rect.top < window.innerHeight * lazyManager.options.preLoad && this.rect.bottom > 0 && this.rect.left < window.innerWidth * lazyManager.options.preLoad && this.rect.right > 0;
  1366. },
  1367. load(onFinish = noop) {
  1368. if (this.state.attempt > this.options.attempt - 1 && this.state.error) {
  1369. if (!lazyManager.options.silent) console.log(`VueLazyload log: ${this.options.src} tried too more than ${this.options.attempt} times`);
  1370. onFinish();
  1371. return;
  1372. }
  1373. const src = this.options.src;
  1374. loadImageAsync({ src }, ({ src }) => {
  1375. this.renderSrc = src;
  1376. this.state.loaded = true;
  1377. }, e => {
  1378. this.state.attempt++;
  1379. this.renderSrc = this.options.error;
  1380. this.state.error = true;
  1381. });
  1382. }
  1383. }
  1384. };
  1385. };
  1386. LazyImage.install = (Vue, options = {}) => {
  1387. const LazyClass = Lazy(Vue);
  1388. const lazy = new LazyClass(options);
  1389. Vue.component('lazy-image', LazyImage(lazy));
  1390. };
  1391. var index = {
  1392. /*
  1393. * install function
  1394. * @param {Vue} Vue
  1395. * @param {object} options lazyload options
  1396. */
  1397. install(Vue, options = {}) {
  1398. const LazyClass = Lazy(Vue);
  1399. const lazy = new LazyClass(options);
  1400. const lazyContainer = new LazyContainerMananger({ lazy });
  1401. const isVue2 = Vue.version.split('.')[0] === '2';
  1402. Vue.prototype.$Lazyload = lazy;
  1403. if (options.lazyComponent) {
  1404. Vue.component('lazy-component', LazyComponent(lazy));
  1405. }
  1406. if (options.lazyImage) {
  1407. Vue.component('lazy-image', LazyImage(lazy));
  1408. }
  1409. if (isVue2) {
  1410. Vue.directive('lazy', {
  1411. bind: lazy.add.bind(lazy),
  1412. update: lazy.update.bind(lazy),
  1413. componentUpdated: lazy.lazyLoadHandler.bind(lazy),
  1414. unbind: lazy.remove.bind(lazy)
  1415. });
  1416. Vue.directive('lazy-container', {
  1417. bind: lazyContainer.bind.bind(lazyContainer),
  1418. componentUpdated: lazyContainer.update.bind(lazyContainer),
  1419. unbind: lazyContainer.unbind.bind(lazyContainer)
  1420. });
  1421. } else {
  1422. Vue.directive('lazy', {
  1423. bind: lazy.lazyLoadHandler.bind(lazy),
  1424. update(newValue, oldValue) {
  1425. assignDeep(this.vm.$refs, this.vm.$els);
  1426. lazy.add(this.el, {
  1427. modifiers: this.modifiers || {},
  1428. arg: this.arg,
  1429. value: newValue,
  1430. oldValue: oldValue
  1431. }, {
  1432. context: this.vm
  1433. });
  1434. },
  1435. unbind() {
  1436. lazy.remove(this.el);
  1437. }
  1438. });
  1439. Vue.directive('lazy-container', {
  1440. update(newValue, oldValue) {
  1441. lazyContainer.update(this.el, {
  1442. modifiers: this.modifiers || {},
  1443. arg: this.arg,
  1444. value: newValue,
  1445. oldValue: oldValue
  1446. }, {
  1447. context: this.vm
  1448. });
  1449. },
  1450. unbind() {
  1451. lazyContainer.unbind(this.el);
  1452. }
  1453. });
  1454. }
  1455. }
  1456. };
  1457. export default index;
  1458. export { Lazy, LazyComponent, LazyContainerMananger as LazyContainer, LazyImage };