lazy.js 9.3 KB

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