UNPKG

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