virtual.mjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import { g as getDocument } from '../shared/ssr-window.esm.mjs';
  2. import { s as setCSSProperty, e as elementChildren, c as createElement } from '../shared/utils.mjs';
  3. 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. const document = getDocument();
  24. swiper.virtual = {
  25. cache: {},
  26. from: undefined,
  27. to: undefined,
  28. slides: [],
  29. offset: 0,
  30. slidesGrid: []
  31. };
  32. const tempDOM = document.createElement('div');
  33. function renderSlide(slide, index) {
  34. const params = swiper.params.virtual;
  35. if (params.cache && swiper.virtual.cache[index]) {
  36. return swiper.virtual.cache[index];
  37. }
  38. // eslint-disable-next-line
  39. let slideEl;
  40. if (params.renderSlide) {
  41. slideEl = params.renderSlide.call(swiper, slide, index);
  42. if (typeof slideEl === 'string') {
  43. tempDOM.innerHTML = slideEl;
  44. slideEl = tempDOM.children[0];
  45. }
  46. } else if (swiper.isElement) {
  47. slideEl = createElement('swiper-slide');
  48. } else {
  49. slideEl = createElement('div', swiper.params.slideClass);
  50. }
  51. slideEl.setAttribute('data-swiper-slide-index', index);
  52. if (!params.renderSlide) {
  53. slideEl.innerHTML = slide;
  54. }
  55. if (params.cache) {
  56. swiper.virtual.cache[index] = slideEl;
  57. }
  58. return slideEl;
  59. }
  60. function update(force, beforeInit) {
  61. const {
  62. slidesPerView,
  63. slidesPerGroup,
  64. centeredSlides,
  65. loop: isLoop,
  66. initialSlide
  67. } = swiper.params;
  68. if (beforeInit && !isLoop && initialSlide > 0) {
  69. return;
  70. }
  71. const {
  72. addSlidesBefore,
  73. addSlidesAfter
  74. } = swiper.params.virtual;
  75. const {
  76. from: previousFrom,
  77. to: previousTo,
  78. slides,
  79. slidesGrid: previousSlidesGrid,
  80. offset: previousOffset
  81. } = swiper.virtual;
  82. if (!swiper.params.cssMode) {
  83. swiper.updateActiveIndex();
  84. }
  85. const activeIndex = swiper.activeIndex || 0;
  86. let offsetProp;
  87. if (swiper.rtlTranslate) offsetProp = 'right';else offsetProp = swiper.isHorizontal() ? 'left' : 'top';
  88. let slidesAfter;
  89. let slidesBefore;
  90. if (centeredSlides) {
  91. slidesAfter = Math.floor(slidesPerView / 2) + slidesPerGroup + addSlidesAfter;
  92. slidesBefore = Math.floor(slidesPerView / 2) + slidesPerGroup + addSlidesBefore;
  93. } else {
  94. slidesAfter = slidesPerView + (slidesPerGroup - 1) + addSlidesAfter;
  95. slidesBefore = (isLoop ? slidesPerView : slidesPerGroup) + addSlidesBefore;
  96. }
  97. let from = activeIndex - slidesBefore;
  98. let to = activeIndex + slidesAfter;
  99. if (!isLoop) {
  100. from = Math.max(from, 0);
  101. to = Math.min(to, slides.length - 1);
  102. }
  103. let offset = (swiper.slidesGrid[from] || 0) - (swiper.slidesGrid[0] || 0);
  104. if (isLoop && activeIndex >= slidesBefore) {
  105. from -= slidesBefore;
  106. if (!centeredSlides) offset += swiper.slidesGrid[0];
  107. } else if (isLoop && activeIndex < slidesBefore) {
  108. from = -slidesBefore;
  109. if (centeredSlides) offset += swiper.slidesGrid[0];
  110. }
  111. Object.assign(swiper.virtual, {
  112. from,
  113. to,
  114. offset,
  115. slidesGrid: swiper.slidesGrid,
  116. slidesBefore,
  117. slidesAfter
  118. });
  119. function onRendered() {
  120. swiper.updateSlides();
  121. swiper.updateProgress();
  122. swiper.updateSlidesClasses();
  123. emit('virtualUpdate');
  124. }
  125. if (previousFrom === from && previousTo === to && !force) {
  126. if (swiper.slidesGrid !== previousSlidesGrid && offset !== previousOffset) {
  127. swiper.slides.forEach(slideEl => {
  128. slideEl.style[offsetProp] = `${offset - Math.abs(swiper.cssOverflowAdjustment())}px`;
  129. });
  130. }
  131. swiper.updateProgress();
  132. emit('virtualUpdate');
  133. return;
  134. }
  135. if (swiper.params.virtual.renderExternal) {
  136. swiper.params.virtual.renderExternal.call(swiper, {
  137. offset,
  138. from,
  139. to,
  140. slides: function getSlides() {
  141. const slidesToRender = [];
  142. for (let i = from; i <= to; i += 1) {
  143. slidesToRender.push(slides[i]);
  144. }
  145. return slidesToRender;
  146. }()
  147. });
  148. if (swiper.params.virtual.renderExternalUpdate) {
  149. onRendered();
  150. } else {
  151. emit('virtualUpdate');
  152. }
  153. return;
  154. }
  155. const prependIndexes = [];
  156. const appendIndexes = [];
  157. const getSlideIndex = index => {
  158. let slideIndex = index;
  159. if (index < 0) {
  160. slideIndex = slides.length + index;
  161. } else if (slideIndex >= slides.length) {
  162. // eslint-disable-next-line
  163. slideIndex = slideIndex - slides.length;
  164. }
  165. return slideIndex;
  166. };
  167. if (force) {
  168. swiper.slides.filter(el => el.matches(`.${swiper.params.slideClass}, swiper-slide`)).forEach(slideEl => {
  169. slideEl.remove();
  170. });
  171. } else {
  172. for (let i = previousFrom; i <= previousTo; i += 1) {
  173. if (i < from || i > to) {
  174. const slideIndex = getSlideIndex(i);
  175. swiper.slides.filter(el => el.matches(`.${swiper.params.slideClass}[data-swiper-slide-index="${slideIndex}"], swiper-slide[data-swiper-slide-index="${slideIndex}"]`)).forEach(slideEl => {
  176. slideEl.remove();
  177. });
  178. }
  179. }
  180. }
  181. const loopFrom = isLoop ? -slides.length : 0;
  182. const loopTo = isLoop ? slides.length * 2 : slides.length;
  183. for (let i = loopFrom; i < loopTo; i += 1) {
  184. if (i >= from && i <= to) {
  185. const slideIndex = getSlideIndex(i);
  186. if (typeof previousTo === 'undefined' || force) {
  187. appendIndexes.push(slideIndex);
  188. } else {
  189. if (i > previousTo) appendIndexes.push(slideIndex);
  190. if (i < previousFrom) prependIndexes.push(slideIndex);
  191. }
  192. }
  193. }
  194. appendIndexes.forEach(index => {
  195. swiper.slidesEl.append(renderSlide(slides[index], index));
  196. });
  197. if (isLoop) {
  198. for (let i = prependIndexes.length - 1; i >= 0; i -= 1) {
  199. const index = prependIndexes[i];
  200. swiper.slidesEl.prepend(renderSlide(slides[index], index));
  201. }
  202. } else {
  203. prependIndexes.sort((a, b) => b - a);
  204. prependIndexes.forEach(index => {
  205. swiper.slidesEl.prepend(renderSlide(slides[index], index));
  206. });
  207. }
  208. elementChildren(swiper.slidesEl, '.swiper-slide, swiper-slide').forEach(slideEl => {
  209. slideEl.style[offsetProp] = `${offset - Math.abs(swiper.cssOverflowAdjustment())}px`;
  210. });
  211. onRendered();
  212. }
  213. function appendSlide(slides) {
  214. if (typeof slides === 'object' && 'length' in slides) {
  215. for (let i = 0; i < slides.length; i += 1) {
  216. if (slides[i]) swiper.virtual.slides.push(slides[i]);
  217. }
  218. } else {
  219. swiper.virtual.slides.push(slides);
  220. }
  221. update(true);
  222. }
  223. function prependSlide(slides) {
  224. const activeIndex = swiper.activeIndex;
  225. let newActiveIndex = activeIndex + 1;
  226. let numberOfNewSlides = 1;
  227. if (Array.isArray(slides)) {
  228. for (let i = 0; i < slides.length; i += 1) {
  229. if (slides[i]) swiper.virtual.slides.unshift(slides[i]);
  230. }
  231. newActiveIndex = activeIndex + slides.length;
  232. numberOfNewSlides = slides.length;
  233. } else {
  234. swiper.virtual.slides.unshift(slides);
  235. }
  236. if (swiper.params.virtual.cache) {
  237. const cache = swiper.virtual.cache;
  238. const newCache = {};
  239. Object.keys(cache).forEach(cachedIndex => {
  240. const cachedEl = cache[cachedIndex];
  241. const cachedElIndex = cachedEl.getAttribute('data-swiper-slide-index');
  242. if (cachedElIndex) {
  243. cachedEl.setAttribute('data-swiper-slide-index', parseInt(cachedElIndex, 10) + numberOfNewSlides);
  244. }
  245. newCache[parseInt(cachedIndex, 10) + numberOfNewSlides] = cachedEl;
  246. });
  247. swiper.virtual.cache = newCache;
  248. }
  249. update(true);
  250. swiper.slideTo(newActiveIndex, 0);
  251. }
  252. function removeSlide(slidesIndexes) {
  253. if (typeof slidesIndexes === 'undefined' || slidesIndexes === null) return;
  254. let activeIndex = swiper.activeIndex;
  255. if (Array.isArray(slidesIndexes)) {
  256. for (let i = slidesIndexes.length - 1; i >= 0; i -= 1) {
  257. if (swiper.params.virtual.cache) {
  258. delete swiper.virtual.cache[slidesIndexes[i]];
  259. // shift cache indexes
  260. Object.keys(swiper.virtual.cache).forEach(key => {
  261. if (key > slidesIndexes) {
  262. swiper.virtual.cache[key - 1] = swiper.virtual.cache[key];
  263. swiper.virtual.cache[key - 1].setAttribute('data-swiper-slide-index', key - 1);
  264. delete swiper.virtual.cache[key];
  265. }
  266. });
  267. }
  268. swiper.virtual.slides.splice(slidesIndexes[i], 1);
  269. if (slidesIndexes[i] < activeIndex) activeIndex -= 1;
  270. activeIndex = Math.max(activeIndex, 0);
  271. }
  272. } else {
  273. if (swiper.params.virtual.cache) {
  274. delete swiper.virtual.cache[slidesIndexes];
  275. // shift cache indexes
  276. Object.keys(swiper.virtual.cache).forEach(key => {
  277. if (key > slidesIndexes) {
  278. swiper.virtual.cache[key - 1] = swiper.virtual.cache[key];
  279. swiper.virtual.cache[key - 1].setAttribute('data-swiper-slide-index', key - 1);
  280. delete swiper.virtual.cache[key];
  281. }
  282. });
  283. }
  284. swiper.virtual.slides.splice(slidesIndexes, 1);
  285. if (slidesIndexes < activeIndex) activeIndex -= 1;
  286. activeIndex = Math.max(activeIndex, 0);
  287. }
  288. update(true);
  289. swiper.slideTo(activeIndex, 0);
  290. }
  291. function removeAllSlides() {
  292. swiper.virtual.slides = [];
  293. if (swiper.params.virtual.cache) {
  294. swiper.virtual.cache = {};
  295. }
  296. update(true);
  297. swiper.slideTo(0, 0);
  298. }
  299. on('beforeInit', () => {
  300. if (!swiper.params.virtual.enabled) return;
  301. let domSlidesAssigned;
  302. if (typeof swiper.passedParams.virtual.slides === 'undefined') {
  303. const slides = [...swiper.slidesEl.children].filter(el => el.matches(`.${swiper.params.slideClass}, swiper-slide`));
  304. if (slides && slides.length) {
  305. swiper.virtual.slides = [...slides];
  306. domSlidesAssigned = true;
  307. slides.forEach((slideEl, slideIndex) => {
  308. slideEl.setAttribute('data-swiper-slide-index', slideIndex);
  309. swiper.virtual.cache[slideIndex] = slideEl;
  310. slideEl.remove();
  311. });
  312. }
  313. }
  314. if (!domSlidesAssigned) {
  315. swiper.virtual.slides = swiper.params.virtual.slides;
  316. }
  317. swiper.classNames.push(`${swiper.params.containerModifierClass}virtual`);
  318. swiper.params.watchSlidesProgress = true;
  319. swiper.originalParams.watchSlidesProgress = true;
  320. update(false, true);
  321. });
  322. on('setTranslate', () => {
  323. if (!swiper.params.virtual.enabled) return;
  324. if (swiper.params.cssMode && !swiper._immediateVirtual) {
  325. clearTimeout(cssModeTimeout);
  326. cssModeTimeout = setTimeout(() => {
  327. update();
  328. }, 100);
  329. } else {
  330. update();
  331. }
  332. });
  333. on('init update resize', () => {
  334. if (!swiper.params.virtual.enabled) return;
  335. if (swiper.params.cssMode) {
  336. setCSSProperty(swiper.wrapperEl, '--swiper-virtual-size', `${swiper.virtualSize}px`);
  337. }
  338. });
  339. Object.assign(swiper.virtual, {
  340. appendSlide,
  341. prependSlide,
  342. removeSlide,
  343. removeAllSlides,
  344. update
  345. });
  346. }
  347. export { Virtual as default };