Skip to content

Commit

Permalink
feat(core): loop support for grid, new loopAddBlankSlides parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
nolimits4web committed Oct 2, 2023
1 parent 7b21583 commit b5db223
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 102 deletions.
2 changes: 2 additions & 0 deletions src/components-shared/params-list.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const paramsList = [
'_slideToClickedSlide',
'_loop',
'loopedSlides',
'loopAddBlankSlides',
'loopPreventsSliding',
'_rewind',
'_allowSlidePrev',
Expand All @@ -85,6 +86,7 @@ const paramsList = [
'slideFullyVisibleClass',
'slideNextClass',
'slidePrevClass',
'slideBlankClass',
'wrapperClass',
'lazyPreloaderClass',
'lazyPreloadPrevNext',
Expand Down
17 changes: 17 additions & 0 deletions src/core/core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,23 @@ class Swiper {
return swiper;
}

getDirectionLabel(property) {
if (this.isHorizontal()) {
return property;
}
// prettier-ignore
return {
'width': 'height',
'margin-top': 'margin-left',
'margin-bottom ': 'margin-right',
'margin-left': 'margin-top',
'margin-right': 'margin-bottom',
'padding-left': 'padding-top',
'padding-right': 'padding-bottom',
'marginRight': 'marginBottom',
}[property];
}

getSlideIndex(slideEl) {
const { slidesEl, params } = this;
const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
Expand Down
2 changes: 2 additions & 0 deletions src/core/defaults.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default {

// loop
loop: false,
loopAddBlankSlides: true,
loopedSlides: null,
loopPreventsSliding: true,

Expand All @@ -122,6 +123,7 @@ export default {
// NS
containerModifierClass: 'swiper-', // NEW
slideClass: 'swiper-slide',
slideBlankClass: 'swiper-slide-blank',
slideActiveClass: 'swiper-slide-active',
slideVisibleClass: 'swiper-slide-visible',
slideFullyVisibleClass: 'swiper-slide-fully-visible',
Expand Down
57 changes: 47 additions & 10 deletions src/core/loop/loopCreate.mjs
Original file line number Diff line number Diff line change
@@ -1,24 +1,61 @@
import { elementChildren } from '../../shared/utils.mjs';
import { createElement, elementChildren, showWarning } from '../../shared/utils.mjs';

export default function loopCreate(slideRealIndex) {
const swiper = this;
const { params, slidesEl } = swiper;
if (!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return;

const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
const initSlides = () => {
const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);

slides.forEach((el, index) => {
el.setAttribute('data-swiper-slide-index', index);
});
slides.forEach((el, index) => {
el.setAttribute('data-swiper-slide-index', index);
});
};

if (swiper.slides.length % params.slidesPerGroup !== 0) {
try {
console.warn(
const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;

const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1);

const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0;
const shouldFillGrid = gridEnabled && swiper.slides.length % params.grid.rows !== 0;

const addBlankSlides = (amountOfSlides) => {
for (let i = 0; i < amountOfSlides; i += 1) {
const slideEl = swiper.isElement
? createElement('swiper-slide', [params.slideBlankClass])
: createElement('div', [params.slideClass, params.slideBlankClass]);
swiper.slidesEl.append(slideEl);
}
};

if (shouldFillGroup) {
if (params.loopAddBlankSlides) {
const slidesToAdd = slidesPerGroup - (swiper.slides.length % slidesPerGroup);
addBlankSlides(slidesToAdd);
swiper.recalcSlides();
swiper.updateSlides();
} else {
showWarning(
'Swiper Loop Warning: The number of slides is not even to slidesPerGroup, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)',
);
} catch (err) {
// err
}

initSlides();
} else if (shouldFillGrid) {
if (params.loopAddBlankSlides) {
const slidesToAdd = params.grid.rows - (swiper.slides.length % params.grid.rows);
addBlankSlides(slidesToAdd);
swiper.recalcSlides();
swiper.updateSlides();
} else {
showWarning(
'Swiper Loop Warning: The number of slides is not even to grid.rows, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)',
);
}
initSlides();
} else {
initSlides();
}

swiper.loopFix({ slideRealIndex, direction: params.centeredSlides ? undefined : 'next' });
Expand Down
91 changes: 61 additions & 30 deletions src/core/loop/loopFix.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { showWarning } from '../../shared/utils.mjs';

export default function loopFix({
slideRealIndex,
slideTo = true,
Expand Down Expand Up @@ -45,16 +47,14 @@ export default function loopFix({
loopedSlides += params.slidesPerGroup - (loopedSlides % params.slidesPerGroup);
}
swiper.loopedSlides = loopedSlides;
const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;

if (swiper.slides.length < slidesPerView + loopedSlides) {
try {
console.warn(
'Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled and not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters',
);
return;
} catch (err) {
// err
}
if (slides.length < slidesPerView + loopedSlides) {
showWarning(
'Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled and not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters',
);
} else if (gridEnabled && params.grid.fill === 'row') {
showWarning('Swiper Loop Warning: Loop mode is not compatible with grid.fill = `row`');
}

const prependSlidesIndexes = [];
Expand All @@ -64,7 +64,7 @@ export default function loopFix({

if (typeof activeSlideIndex === 'undefined') {
activeSlideIndex = swiper.getSlideIndex(
swiper.slides.filter((el) => el.classList.contains(params.slideActiveClass))[0],
slides.filter((el) => el.classList.contains(params.slideActiveClass))[0],
);
} else {
activeIndex = activeSlideIndex;
Expand All @@ -75,46 +75,71 @@ export default function loopFix({

let slidesPrepended = 0;
let slidesAppended = 0;
const activeSlideIndexWithShift =
activeSlideIndex +

const cols = gridEnabled ? Math.ceil(slides.length / params.grid.rows) : slides.length;
const activeColIndex = gridEnabled ? slides[activeSlideIndex].column : activeSlideIndex;
const activeColIndexWithShift =
activeColIndex +
(centeredSlides && typeof setTranslate === 'undefined' ? -slidesPerView / 2 + 0.5 : 0);
// prepend last slides before start
if (activeSlideIndexWithShift < loopedSlides) {
slidesPrepended = Math.max(loopedSlides - activeSlideIndexWithShift, params.slidesPerGroup);
for (let i = 0; i < loopedSlides - activeSlideIndexWithShift; i += 1) {
const index = i - Math.floor(i / slides.length) * slides.length;
prependSlidesIndexes.push(slides.length - index - 1);
if (activeColIndexWithShift < loopedSlides) {
slidesPrepended = Math.max(loopedSlides - activeColIndexWithShift, params.slidesPerGroup);
for (let i = 0; i < loopedSlides - activeColIndexWithShift; i += 1) {
const index = i - Math.floor(i / cols) * cols;
if (gridEnabled) {
const colIndexToPrepend = cols - index - 1;
for (let i = slides.length - 1; i >= 0; i -= 1) {
if (slides[i].column === colIndexToPrepend) prependSlidesIndexes.push(i);
}
// slides.forEach((slide, slideIndex) => {
// if (slide.column === colIndexToPrepend) prependSlidesIndexes.push(slideIndex);
// });
} else {
prependSlidesIndexes.push(cols - index - 1);
}
}
} else if (activeSlideIndexWithShift + slidesPerView > swiper.slides.length - loopedSlides) {
} else if (activeColIndexWithShift + slidesPerView > cols - loopedSlides) {
slidesAppended = Math.max(
activeSlideIndexWithShift - (swiper.slides.length - loopedSlides * 2),
activeColIndexWithShift - (cols - loopedSlides * 2),
params.slidesPerGroup,
);
for (let i = 0; i < slidesAppended; i += 1) {
const index = i - Math.floor(i / slides.length) * slides.length;
appendSlidesIndexes.push(index);
const index = i - Math.floor(i / cols) * cols;
if (gridEnabled) {
slides.forEach((slide, slideIndex) => {
if (slide.column === index) appendSlidesIndexes.push(slideIndex);
});
} else {
appendSlidesIndexes.push(index);
}
}
}

if (isPrev) {
prependSlidesIndexes.forEach((index) => {
swiper.slides[index].swiperLoopMoveDOM = true;
slides[index].swiperLoopMoveDOM = true;

slidesEl.prepend(swiper.slides[index]);
swiper.slides[index].swiperLoopMoveDOM = false;
slidesEl.prepend(slides[index]);
slides[index].swiperLoopMoveDOM = false;
});
}
if (isNext) {
appendSlidesIndexes.forEach((index) => {
swiper.slides[index].swiperLoopMoveDOM = true;
slidesEl.append(swiper.slides[index]);
swiper.slides[index].swiperLoopMoveDOM = false;
slides[index].swiperLoopMoveDOM = true;
slidesEl.append(slides[index]);
slides[index].swiperLoopMoveDOM = false;
});
}

swiper.recalcSlides();
if (params.slidesPerView === 'auto') {
swiper.updateSlides();
} else if (
gridEnabled &&
((prependSlidesIndexes.length > 0 && isPrev) || (appendSlidesIndexes.length > 0 && isNext))
) {
swiper.slides.forEach((slide, slideIndex) => {
swiper.grid.updateSlide(slideIndex, slide, swiper.slides);
});
}
if (params.watchSlidesProgress) {
swiper.updateSlidesOffset();
Expand All @@ -137,7 +162,10 @@ export default function loopFix({
}
} else {
if (setTranslate) {
swiper.slideTo(swiper.activeIndex + prependSlidesIndexes.length, 0, false, true);
const shift = gridEnabled
? prependSlidesIndexes.length / params.grid.rows
: prependSlidesIndexes.length;
swiper.slideTo(swiper.activeIndex + shift, 0, false, true);
swiper.touchEventsData.currentTranslate = swiper.translate;
}
}
Expand All @@ -157,7 +185,10 @@ export default function loopFix({
}
}
} else {
swiper.slideTo(swiper.activeIndex - appendSlidesIndexes.length, 0, false, true);
const shift = gridEnabled
? appendSlidesIndexes.length / params.grid.rows
: appendSlidesIndexes.length;
swiper.slideTo(swiper.activeIndex - shift, 0, false, true);
}
}
}
Expand Down
37 changes: 29 additions & 8 deletions src/core/slide/slideToLoop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,28 @@ export default function slideToLoop(

index = indexAsNumber;
}

const swiper = this;
const gridEnabled = swiper.grid && swiper.params.grid && swiper.params.grid.rows > 1;
let newIndex = index;
if (swiper.params.loop) {
if (swiper.virtual && swiper.params.virtual.enabled) {
// eslint-disable-next-line
newIndex = newIndex + swiper.virtual.slidesBefore;
} else {
const targetSlideIndex = swiper.getSlideIndexByData(newIndex);
const slides = swiper.slides.length;
let targetSlideIndex;
if (gridEnabled) {
const slideIndex = newIndex * swiper.params.grid.rows;
targetSlideIndex = swiper.slides.filter(
(slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === slideIndex,
)[0].column;
} else {
targetSlideIndex = swiper.getSlideIndexByData(newIndex);
}

const cols = gridEnabled
? Math.ceil(swiper.slides.length / swiper.params.grid.rows)
: swiper.slides.length;

const { centeredSlides } = swiper.params;
let slidesPerView = swiper.params.slidesPerView;
if (slidesPerView === 'auto') {
Expand All @@ -29,7 +41,7 @@ export default function slideToLoop(
slidesPerView = slidesPerView + 1;
}
}
let needLoopFix = slides - targetSlideIndex < slidesPerView;
let needLoopFix = cols - targetSlideIndex < slidesPerView;
if (centeredSlides) {
needLoopFix = needLoopFix || targetSlideIndex < Math.ceil(slidesPerView / 2);
}
Expand All @@ -41,19 +53,28 @@ export default function slideToLoop(
: targetSlideIndex - swiper.activeIndex - 1 < swiper.params.slidesPerView
? 'next'
: 'prev';

swiper.loopFix({
direction,
slideTo: true,
activeSlideIndex:
direction === 'next' ? targetSlideIndex + 1 : targetSlideIndex - slides + 1,
direction === 'next' ? targetSlideIndex + 1 : targetSlideIndex - cols + 1,
slideRealIndex: direction === 'next' ? swiper.realIndex : undefined,
});
}

newIndex = swiper.getSlideIndexByData(newIndex);
if (gridEnabled) {
const slideIndex = newIndex * swiper.params.grid.rows;
newIndex = swiper.slides.filter(
(slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === slideIndex,
)[0].column;
} else {
newIndex = swiper.getSlideIndexByData(newIndex);
}
}
}

return swiper.slideTo(newIndex, speed, runCallbacks, internal);
requestAnimationFrame(() => {
swiper.slideTo(newIndex, speed, runCallbacks, internal);
});
return;
}
24 changes: 20 additions & 4 deletions src/core/update/updateActiveIndex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,31 @@ export default function updateActiveIndex(newActiveIndex) {
return;
}

const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;

const normalizeSlideIndexToColumn = (index) => {
if (!gridEnabled) return index;
return Math.floor(index / params.grid.rows);
};

// Get real index
let realIndex;
if (swiper.virtual && params.virtual.enabled && params.loop) {
realIndex = getVirtualRealIndex(activeIndex);
} else if (gridEnabled) {
const firstSlideInColumn = swiper.slides.filter((slideEl) => slideEl.column === activeIndex)[0];
let activeSlideIndex = parseInt(firstSlideInColumn.getAttribute('data-swiper-slide-index'), 10);
if (Number.isNaN(activeSlideIndex)) {
activeSlideIndex = Math.max(swiper.slides.indexOf(firstSlideInColumn), 0);
}
realIndex = Math.floor(activeSlideIndex / params.grid.rows);
} else if (swiper.slides[activeIndex]) {
realIndex = parseInt(
swiper.slides[activeIndex].getAttribute('data-swiper-slide-index') || activeIndex,
10,
);
const slideIndex = swiper.slides[activeIndex].getAttribute('data-swiper-slide-index');
if (slideIndex) {
realIndex = parseInt(slideIndex, 10);
} else {
realIndex = activeIndex;
}
} else {
realIndex = activeIndex;
}
Expand Down
Loading

0 comments on commit b5db223

Please sign in to comment.