lazy.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { getWindow } from 'ssr-window';
  2. import $ from '../../shared/dom.js';
  3. export default function Lazy(_ref) {
  4. let {
  5. swiper,
  6. extendParams,
  7. on,
  8. emit
  9. } = _ref;
  10. extendParams({
  11. lazy: {
  12. checkInView: false,
  13. enabled: false,
  14. loadPrevNext: false,
  15. loadPrevNextAmount: 1,
  16. loadOnTransitionStart: false,
  17. scrollingElement: '',
  18. elementClass: 'swiper-lazy',
  19. loadingClass: 'swiper-lazy-loading',
  20. loadedClass: 'swiper-lazy-loaded',
  21. preloaderClass: 'swiper-lazy-preloader'
  22. }
  23. });
  24. swiper.lazy = {};
  25. let scrollHandlerAttached = false;
  26. let initialImageLoaded = false;
  27. function loadInSlide(index, loadInDuplicate) {
  28. if (loadInDuplicate === void 0) {
  29. loadInDuplicate = true;
  30. }
  31. const params = swiper.params.lazy;
  32. if (typeof index === 'undefined') return;
  33. if (swiper.slides.length === 0) return;
  34. const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
  35. const $slideEl = isVirtual ? swiper.$wrapperEl.children(`.${swiper.params.slideClass}[data-swiper-slide-index="${index}"]`) : swiper.slides.eq(index);
  36. const $images = $slideEl.find(`.${params.elementClass}:not(.${params.loadedClass}):not(.${params.loadingClass})`);
  37. if ($slideEl.hasClass(params.elementClass) && !$slideEl.hasClass(params.loadedClass) && !$slideEl.hasClass(params.loadingClass)) {
  38. $images.push($slideEl[0]);
  39. }
  40. if ($images.length === 0) return;
  41. $images.each(imageEl => {
  42. const $imageEl = $(imageEl);
  43. $imageEl.addClass(params.loadingClass);
  44. const background = $imageEl.attr('data-background');
  45. const src = $imageEl.attr('data-src');
  46. const srcset = $imageEl.attr('data-srcset');
  47. const sizes = $imageEl.attr('data-sizes');
  48. const $pictureEl = $imageEl.parent('picture');
  49. swiper.loadImage($imageEl[0], src || background, srcset, sizes, false, () => {
  50. if (typeof swiper === 'undefined' || swiper === null || !swiper || swiper && !swiper.params || swiper.destroyed) return;
  51. if (background) {
  52. $imageEl.css('background-image', `url("${background}")`);
  53. $imageEl.removeAttr('data-background');
  54. } else {
  55. if (srcset) {
  56. $imageEl.attr('srcset', srcset);
  57. $imageEl.removeAttr('data-srcset');
  58. }
  59. if (sizes) {
  60. $imageEl.attr('sizes', sizes);
  61. $imageEl.removeAttr('data-sizes');
  62. }
  63. if ($pictureEl.length) {
  64. $pictureEl.children('source').each(sourceEl => {
  65. const $source = $(sourceEl);
  66. if ($source.attr('data-srcset')) {
  67. $source.attr('srcset', $source.attr('data-srcset'));
  68. $source.removeAttr('data-srcset');
  69. }
  70. });
  71. }
  72. if (src) {
  73. $imageEl.attr('src', src);
  74. $imageEl.removeAttr('data-src');
  75. }
  76. }
  77. $imageEl.addClass(params.loadedClass).removeClass(params.loadingClass);
  78. $slideEl.find(`.${params.preloaderClass}`).remove();
  79. if (swiper.params.loop && loadInDuplicate) {
  80. const slideOriginalIndex = $slideEl.attr('data-swiper-slide-index');
  81. if ($slideEl.hasClass(swiper.params.slideDuplicateClass)) {
  82. const originalSlide = swiper.$wrapperEl.children(`[data-swiper-slide-index="${slideOriginalIndex}"]:not(.${swiper.params.slideDuplicateClass})`);
  83. loadInSlide(originalSlide.index(), false);
  84. } else {
  85. const duplicatedSlide = swiper.$wrapperEl.children(`.${swiper.params.slideDuplicateClass}[data-swiper-slide-index="${slideOriginalIndex}"]`);
  86. loadInSlide(duplicatedSlide.index(), false);
  87. }
  88. }
  89. emit('lazyImageReady', $slideEl[0], $imageEl[0]);
  90. if (swiper.params.autoHeight) {
  91. swiper.updateAutoHeight();
  92. }
  93. });
  94. emit('lazyImageLoad', $slideEl[0], $imageEl[0]);
  95. });
  96. }
  97. function load() {
  98. const {
  99. $wrapperEl,
  100. params: swiperParams,
  101. slides,
  102. activeIndex
  103. } = swiper;
  104. const isVirtual = swiper.virtual && swiperParams.virtual.enabled;
  105. const params = swiperParams.lazy;
  106. let slidesPerView = swiperParams.slidesPerView;
  107. if (slidesPerView === 'auto') {
  108. slidesPerView = 0;
  109. }
  110. function slideExist(index) {
  111. if (isVirtual) {
  112. if ($wrapperEl.children(`.${swiperParams.slideClass}[data-swiper-slide-index="${index}"]`).length) {
  113. return true;
  114. }
  115. } else if (slides[index]) return true;
  116. return false;
  117. }
  118. function slideIndex(slideEl) {
  119. if (isVirtual) {
  120. return $(slideEl).attr('data-swiper-slide-index');
  121. }
  122. return $(slideEl).index();
  123. }
  124. if (!initialImageLoaded) initialImageLoaded = true;
  125. if (swiper.params.watchSlidesProgress) {
  126. $wrapperEl.children(`.${swiperParams.slideVisibleClass}`).each(slideEl => {
  127. const index = isVirtual ? $(slideEl).attr('data-swiper-slide-index') : $(slideEl).index();
  128. loadInSlide(index);
  129. });
  130. } else if (slidesPerView > 1) {
  131. for (let i = activeIndex; i < activeIndex + slidesPerView; i += 1) {
  132. if (slideExist(i)) loadInSlide(i);
  133. }
  134. } else {
  135. loadInSlide(activeIndex);
  136. }
  137. if (params.loadPrevNext) {
  138. if (slidesPerView > 1 || params.loadPrevNextAmount && params.loadPrevNextAmount > 1) {
  139. const amount = params.loadPrevNextAmount;
  140. const spv = slidesPerView;
  141. const maxIndex = Math.min(activeIndex + spv + Math.max(amount, spv), slides.length);
  142. const minIndex = Math.max(activeIndex - Math.max(spv, amount), 0); // Next Slides
  143. for (let i = activeIndex + slidesPerView; i < maxIndex; i += 1) {
  144. if (slideExist(i)) loadInSlide(i);
  145. } // Prev Slides
  146. for (let i = minIndex; i < activeIndex; i += 1) {
  147. if (slideExist(i)) loadInSlide(i);
  148. }
  149. } else {
  150. const nextSlide = $wrapperEl.children(`.${swiperParams.slideNextClass}`);
  151. if (nextSlide.length > 0) loadInSlide(slideIndex(nextSlide));
  152. const prevSlide = $wrapperEl.children(`.${swiperParams.slidePrevClass}`);
  153. if (prevSlide.length > 0) loadInSlide(slideIndex(prevSlide));
  154. }
  155. }
  156. }
  157. function checkInViewOnLoad() {
  158. const window = getWindow();
  159. if (!swiper || swiper.destroyed) return;
  160. const $scrollElement = swiper.params.lazy.scrollingElement ? $(swiper.params.lazy.scrollingElement) : $(window);
  161. const isWindow = $scrollElement[0] === window;
  162. const scrollElementWidth = isWindow ? window.innerWidth : $scrollElement[0].offsetWidth;
  163. const scrollElementHeight = isWindow ? window.innerHeight : $scrollElement[0].offsetHeight;
  164. const swiperOffset = swiper.$el.offset();
  165. const {
  166. rtlTranslate: rtl
  167. } = swiper;
  168. let inView = false;
  169. if (rtl) swiperOffset.left -= swiper.$el[0].scrollLeft;
  170. const swiperCoord = [[swiperOffset.left, swiperOffset.top], [swiperOffset.left + swiper.width, swiperOffset.top], [swiperOffset.left, swiperOffset.top + swiper.height], [swiperOffset.left + swiper.width, swiperOffset.top + swiper.height]];
  171. for (let i = 0; i < swiperCoord.length; i += 1) {
  172. const point = swiperCoord[i];
  173. if (point[0] >= 0 && point[0] <= scrollElementWidth && point[1] >= 0 && point[1] <= scrollElementHeight) {
  174. if (point[0] === 0 && point[1] === 0) continue; // eslint-disable-line
  175. inView = true;
  176. }
  177. }
  178. const passiveListener = swiper.touchEvents.start === 'touchstart' && swiper.support.passiveListener && swiper.params.passiveListeners ? {
  179. passive: true,
  180. capture: false
  181. } : false;
  182. if (inView) {
  183. load();
  184. $scrollElement.off('scroll', checkInViewOnLoad, passiveListener);
  185. } else if (!scrollHandlerAttached) {
  186. scrollHandlerAttached = true;
  187. $scrollElement.on('scroll', checkInViewOnLoad, passiveListener);
  188. }
  189. }
  190. on('beforeInit', () => {
  191. if (swiper.params.lazy.enabled && swiper.params.preloadImages) {
  192. swiper.params.preloadImages = false;
  193. }
  194. });
  195. on('init', () => {
  196. if (swiper.params.lazy.enabled) {
  197. if (swiper.params.lazy.checkInView) {
  198. checkInViewOnLoad();
  199. } else {
  200. load();
  201. }
  202. }
  203. });
  204. on('scroll', () => {
  205. if (swiper.params.freeMode && swiper.params.freeMode.enabled && !swiper.params.freeMode.sticky) {
  206. load();
  207. }
  208. });
  209. on('scrollbarDragMove resize _freeModeNoMomentumRelease', () => {
  210. if (swiper.params.lazy.enabled) {
  211. if (swiper.params.lazy.checkInView) {
  212. checkInViewOnLoad();
  213. } else {
  214. load();
  215. }
  216. }
  217. });
  218. on('transitionStart', () => {
  219. if (swiper.params.lazy.enabled) {
  220. if (swiper.params.lazy.loadOnTransitionStart || !swiper.params.lazy.loadOnTransitionStart && !initialImageLoaded) {
  221. if (swiper.params.lazy.checkInView) {
  222. checkInViewOnLoad();
  223. } else {
  224. load();
  225. }
  226. }
  227. }
  228. });
  229. on('transitionEnd', () => {
  230. if (swiper.params.lazy.enabled && !swiper.params.lazy.loadOnTransitionStart) {
  231. if (swiper.params.lazy.checkInView) {
  232. checkInViewOnLoad();
  233. } else {
  234. load();
  235. }
  236. }
  237. });
  238. on('slideChange', () => {
  239. const {
  240. lazy,
  241. cssMode,
  242. watchSlidesProgress,
  243. touchReleaseOnEdges,
  244. resistanceRatio
  245. } = swiper.params;
  246. if (lazy.enabled && (cssMode || watchSlidesProgress && (touchReleaseOnEdges || resistanceRatio === 0))) {
  247. load();
  248. }
  249. });
  250. Object.assign(swiper.lazy, {
  251. load,
  252. loadInSlide
  253. });
  254. }