virtual.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import $ from '../../shared/dom.js';
  2. import { setCSSProperty } from '../../shared/utils.js';
  3. export default function Virtual(_ref) {
  4. let {
  5. swiper,
  6. extendParams,
  7. on,
  8. emit
  9. } = _ref;
  10. extendParams({
  11. virtual: {
  12. enabled: false,
  13. slides: [],
  14. cache: true,
  15. renderSlide: null,
  16. renderExternal: null,
  17. renderExternalUpdate: true,
  18. addSlidesBefore: 0,
  19. addSlidesAfter: 0
  20. }
  21. });
  22. let cssModeTimeout;
  23. swiper.virtual = {
  24. cache: {},
  25. from: undefined,
  26. to: undefined,
  27. slides: [],
  28. offset: 0,
  29. slidesGrid: []
  30. };
  31. function renderSlide(slide, index) {
  32. const params = swiper.params.virtual;
  33. if (params.cache && swiper.virtual.cache[index]) {
  34. return swiper.virtual.cache[index];
  35. }
  36. const $slideEl = params.renderSlide ? $(params.renderSlide.call(swiper, slide, index)) : $(`<div class="${swiper.params.slideClass}" data-swiper-slide-index="${index}">${slide}</div>`);
  37. if (!$slideEl.attr('data-swiper-slide-index')) $slideEl.attr('data-swiper-slide-index', index);
  38. if (params.cache) swiper.virtual.cache[index] = $slideEl;
  39. return $slideEl;
  40. }
  41. function update(force) {
  42. const {
  43. slidesPerView,
  44. slidesPerGroup,
  45. centeredSlides
  46. } = swiper.params;
  47. const {
  48. addSlidesBefore,
  49. addSlidesAfter
  50. } = swiper.params.virtual;
  51. const {
  52. from: previousFrom,
  53. to: previousTo,
  54. slides,
  55. slidesGrid: previousSlidesGrid,
  56. offset: previousOffset
  57. } = swiper.virtual;
  58. if (!swiper.params.cssMode) {
  59. swiper.updateActiveIndex();
  60. }
  61. const activeIndex = swiper.activeIndex || 0;
  62. let offsetProp;
  63. if (swiper.rtlTranslate) offsetProp = 'right';else offsetProp = swiper.isHorizontal() ? 'left' : 'top';
  64. let slidesAfter;
  65. let slidesBefore;
  66. if (centeredSlides) {
  67. slidesAfter = Math.floor(slidesPerView / 2) + slidesPerGroup + addSlidesAfter;
  68. slidesBefore = Math.floor(slidesPerView / 2) + slidesPerGroup + addSlidesBefore;
  69. } else {
  70. slidesAfter = slidesPerView + (slidesPerGroup - 1) + addSlidesAfter;
  71. slidesBefore = slidesPerGroup + addSlidesBefore;
  72. }
  73. const from = Math.max((activeIndex || 0) - slidesBefore, 0);
  74. const to = Math.min((activeIndex || 0) + slidesAfter, slides.length - 1);
  75. const offset = (swiper.slidesGrid[from] || 0) - (swiper.slidesGrid[0] || 0);
  76. Object.assign(swiper.virtual, {
  77. from,
  78. to,
  79. offset,
  80. slidesGrid: swiper.slidesGrid
  81. });
  82. function onRendered() {
  83. swiper.updateSlides();
  84. swiper.updateProgress();
  85. swiper.updateSlidesClasses();
  86. if (swiper.lazy && swiper.params.lazy.enabled) {
  87. swiper.lazy.load();
  88. }
  89. emit('virtualUpdate');
  90. }
  91. if (previousFrom === from && previousTo === to && !force) {
  92. if (swiper.slidesGrid !== previousSlidesGrid && offset !== previousOffset) {
  93. swiper.slides.css(offsetProp, `${offset}px`);
  94. }
  95. swiper.updateProgress();
  96. emit('virtualUpdate');
  97. return;
  98. }
  99. if (swiper.params.virtual.renderExternal) {
  100. swiper.params.virtual.renderExternal.call(swiper, {
  101. offset,
  102. from,
  103. to,
  104. slides: function getSlides() {
  105. const slidesToRender = [];
  106. for (let i = from; i <= to; i += 1) {
  107. slidesToRender.push(slides[i]);
  108. }
  109. return slidesToRender;
  110. }()
  111. });
  112. if (swiper.params.virtual.renderExternalUpdate) {
  113. onRendered();
  114. } else {
  115. emit('virtualUpdate');
  116. }
  117. return;
  118. }
  119. const prependIndexes = [];
  120. const appendIndexes = [];
  121. if (force) {
  122. swiper.$wrapperEl.find(`.${swiper.params.slideClass}`).remove();
  123. } else {
  124. for (let i = previousFrom; i <= previousTo; i += 1) {
  125. if (i < from || i > to) {
  126. swiper.$wrapperEl.find(`.${swiper.params.slideClass}[data-swiper-slide-index="${i}"]`).remove();
  127. }
  128. }
  129. }
  130. for (let i = 0; i < slides.length; i += 1) {
  131. if (i >= from && i <= to) {
  132. if (typeof previousTo === 'undefined' || force) {
  133. appendIndexes.push(i);
  134. } else {
  135. if (i > previousTo) appendIndexes.push(i);
  136. if (i < previousFrom) prependIndexes.push(i);
  137. }
  138. }
  139. }
  140. appendIndexes.forEach(index => {
  141. swiper.$wrapperEl.append(renderSlide(slides[index], index));
  142. });
  143. prependIndexes.sort((a, b) => b - a).forEach(index => {
  144. swiper.$wrapperEl.prepend(renderSlide(slides[index], index));
  145. });
  146. swiper.$wrapperEl.children('.swiper-slide').css(offsetProp, `${offset}px`);
  147. onRendered();
  148. }
  149. function appendSlide(slides) {
  150. if (typeof slides === 'object' && 'length' in slides) {
  151. for (let i = 0; i < slides.length; i += 1) {
  152. if (slides[i]) swiper.virtual.slides.push(slides[i]);
  153. }
  154. } else {
  155. swiper.virtual.slides.push(slides);
  156. }
  157. update(true);
  158. }
  159. function prependSlide(slides) {
  160. const activeIndex = swiper.activeIndex;
  161. let newActiveIndex = activeIndex + 1;
  162. let numberOfNewSlides = 1;
  163. if (Array.isArray(slides)) {
  164. for (let i = 0; i < slides.length; i += 1) {
  165. if (slides[i]) swiper.virtual.slides.unshift(slides[i]);
  166. }
  167. newActiveIndex = activeIndex + slides.length;
  168. numberOfNewSlides = slides.length;
  169. } else {
  170. swiper.virtual.slides.unshift(slides);
  171. }
  172. if (swiper.params.virtual.cache) {
  173. const cache = swiper.virtual.cache;
  174. const newCache = {};
  175. Object.keys(cache).forEach(cachedIndex => {
  176. const $cachedEl = cache[cachedIndex];
  177. const cachedElIndex = $cachedEl.attr('data-swiper-slide-index');
  178. if (cachedElIndex) {
  179. $cachedEl.attr('data-swiper-slide-index', parseInt(cachedElIndex, 10) + numberOfNewSlides);
  180. }
  181. newCache[parseInt(cachedIndex, 10) + numberOfNewSlides] = $cachedEl;
  182. });
  183. swiper.virtual.cache = newCache;
  184. }
  185. update(true);
  186. swiper.slideTo(newActiveIndex, 0);
  187. }
  188. function removeSlide(slidesIndexes) {
  189. if (typeof slidesIndexes === 'undefined' || slidesIndexes === null) return;
  190. let activeIndex = swiper.activeIndex;
  191. if (Array.isArray(slidesIndexes)) {
  192. for (let i = slidesIndexes.length - 1; i >= 0; i -= 1) {
  193. swiper.virtual.slides.splice(slidesIndexes[i], 1);
  194. if (swiper.params.virtual.cache) {
  195. delete swiper.virtual.cache[slidesIndexes[i]];
  196. }
  197. if (slidesIndexes[i] < activeIndex) activeIndex -= 1;
  198. activeIndex = Math.max(activeIndex, 0);
  199. }
  200. } else {
  201. swiper.virtual.slides.splice(slidesIndexes, 1);
  202. if (swiper.params.virtual.cache) {
  203. delete swiper.virtual.cache[slidesIndexes];
  204. }
  205. if (slidesIndexes < activeIndex) activeIndex -= 1;
  206. activeIndex = Math.max(activeIndex, 0);
  207. }
  208. update(true);
  209. swiper.slideTo(activeIndex, 0);
  210. }
  211. function removeAllSlides() {
  212. swiper.virtual.slides = [];
  213. if (swiper.params.virtual.cache) {
  214. swiper.virtual.cache = {};
  215. }
  216. update(true);
  217. swiper.slideTo(0, 0);
  218. }
  219. on('beforeInit', () => {
  220. if (!swiper.params.virtual.enabled) return;
  221. swiper.virtual.slides = swiper.params.virtual.slides;
  222. swiper.classNames.push(`${swiper.params.containerModifierClass}virtual`);
  223. swiper.params.watchSlidesProgress = true;
  224. swiper.originalParams.watchSlidesProgress = true;
  225. if (!swiper.params.initialSlide) {
  226. update();
  227. }
  228. });
  229. on('setTranslate', () => {
  230. if (!swiper.params.virtual.enabled) return;
  231. if (swiper.params.cssMode && !swiper._immediateVirtual) {
  232. clearTimeout(cssModeTimeout);
  233. cssModeTimeout = setTimeout(() => {
  234. update();
  235. }, 100);
  236. } else {
  237. update();
  238. }
  239. });
  240. on('init update resize', () => {
  241. if (!swiper.params.virtual.enabled) return;
  242. if (swiper.params.cssMode) {
  243. setCSSProperty(swiper.wrapperEl, '--swiper-virtual-size', `${swiper.virtualSize}px`);
  244. }
  245. });
  246. Object.assign(swiper.virtual, {
  247. appendSlide,
  248. prependSlide,
  249. removeSlide,
  250. removeAllSlides,
  251. update
  252. });
  253. }