diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 59731f8e0..710dccf0c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,4 +1,5 @@ const rules = { + 'no-lonely-if': 'off', 'no-param-reassign': 'off', 'no-underscore-dangle': 'off', 'prefer-object-spread': 'off', diff --git a/src/components-shared/params-list.js b/src/components-shared/params-list.js index d45b4f8c2..bb1c2ff3e 100644 --- a/src/components-shared/params-list.js +++ b/src/components-shared/params-list.js @@ -63,11 +63,6 @@ const paramsList = [ 'preventClicksPropagation', '_slideToClickedSlide', '_loop', - '_loopAdditionalSlides', - '_loopedSlides', - '_loopedSlidesLimit', - '_loopFillGroupWithBlank', - 'loopPreventsSlide', '_rewind', '_allowSlidePrev', '_allowSlideNext', @@ -78,7 +73,6 @@ const paramsList = [ 'passiveListeners', 'containerModifierClass', 'slideClass', - 'slideBlankClass', 'slideActiveClass', 'slideDuplicateActiveClass', 'slideVisibleClass', diff --git a/src/components-shared/update-swiper.js b/src/components-shared/update-swiper.js index 8d7fc9344..9f1dc767d 100644 --- a/src/components-shared/update-swiper.js +++ b/src/components-shared/update-swiper.js @@ -137,6 +137,9 @@ function updateSwiper({ virtual.slides = slides; virtual.update(true); } + if (changedParams.includes('children') && slides && currentParams.loop) { + loopNeedReloop = true; + } if (needThumbsInit) { const initialized = thumbs.init(); diff --git a/src/core/breakpoints/setBreakpoint.js b/src/core/breakpoints/setBreakpoint.js index efede5e3a..db12e89e0 100644 --- a/src/core/breakpoints/setBreakpoint.js +++ b/src/core/breakpoints/setBreakpoint.js @@ -6,7 +6,7 @@ const isGridEnabled = (swiper, params) => { export default function setBreakpoint() { const swiper = this; - const { activeIndex, initialized, loopedSlides = 0, params, $el } = swiper; + const { realIndex, initialized, params, $el } = swiper; const breakpoints = params.breakpoints; if (!breakpoints || (breakpoints && Object.keys(breakpoints).length === 0)) return; @@ -80,9 +80,8 @@ export default function setBreakpoint() { if (needsReLoop && initialized) { swiper.loopDestroy(); - swiper.loopCreate(); + swiper.loopCreate(realIndex); swiper.updateSlides(); - swiper.slideTo(activeIndex - loopedSlides + swiper.loopedSlides, 0, false); } swiper.emit('breakpoint', breakpointParams); diff --git a/src/core/core.js b/src/core/core.js index ce336636e..f1774629b 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -211,6 +211,12 @@ class Swiper { return swiper; } + recalcSlides() { + const swiper = this; + const { $slidesEl, params } = swiper; + swiper.slides = $slidesEl.children(`.${params.slideClass}, swiper-slide`); + } + enable() { const swiper = this; if (swiper.enabled) return; @@ -476,6 +482,7 @@ class Swiper { el, $wrapperEl, wrapperEl: $wrapperEl[0], + $slidesEl: swiper.isElement ? $el : $wrapperEl, mounted: true, // RTL @@ -506,11 +513,6 @@ class Swiper { // Add Classes swiper.addClasses(); - // Create loop - if (swiper.params.loop) { - swiper.loopCreate(); - } - // Update size swiper.updateSize(); @@ -527,16 +529,11 @@ class Swiper { } // Slide To Initial Slide + swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true); + + // Create loop if (swiper.params.loop) { - swiper.slideTo( - swiper.params.initialSlide + swiper.loopedSlides, - 0, - swiper.params.runCallbacksOnInit, - false, - true, - ); - } else { - swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true); + swiper.loopCreate(); } // Attach events diff --git a/src/core/defaults.js b/src/core/defaults.js index b01267434..ede736004 100644 --- a/src/core/defaults.js +++ b/src/core/defaults.js @@ -98,11 +98,6 @@ export default { // loop loop: false, - loopAdditionalSlides: 0, - loopedSlides: null, - loopedSlidesLimit: true, - loopFillGroupWithBlank: false, - loopPreventsSlide: true, // rewind rewind: false, @@ -123,7 +118,6 @@ export default { // NS containerModifierClass: 'swiper-', // NEW slideClass: 'swiper-slide', - slideBlankClass: 'swiper-slide-invisible-blank', slideActiveClass: 'swiper-slide-active', slideDuplicateActiveClass: 'swiper-slide-duplicate-active', slideVisibleClass: 'swiper-slide-visible', diff --git a/src/core/events/onResize.js b/src/core/events/onResize.js index f31a4a8ed..5f1e80c0b 100644 --- a/src/core/events/onResize.js +++ b/src/core/events/onResize.js @@ -29,7 +29,11 @@ export default function onResize() { ) { swiper.slideTo(swiper.slides.length - 1, 0, false, true); } else { - swiper.slideTo(swiper.activeIndex, 0, false, true); + if (swiper.params.loop) { + swiper.slideToLoop(swiper.realIndex, 0, false, true); + } else { + swiper.slideTo(swiper.activeIndex, 0, false, true); + } } if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) { diff --git a/src/core/loop/loopCreate.js b/src/core/loop/loopCreate.js index 17e8bfa0c..b7cedb00a 100644 --- a/src/core/loop/loopCreate.js +++ b/src/core/loop/loopCreate.js @@ -1,67 +1,16 @@ -import { getDocument } from 'ssr-window'; import $ from '../../shared/dom.js'; -export default function loopCreate() { +export default function loopCreate(slideRealIndex) { const swiper = this; - const document = getDocument(); - const { params, $wrapperEl } = swiper; - // Remove duplicated slides - const $selector = - $wrapperEl.children().length > 0 ? $($wrapperEl.children()[0].parentNode) : $wrapperEl; - $selector - .children( - `.${params.slideClass}.${params.slideDuplicateClass}, swiper-slide.${params.slideDuplicateClass}`, - ) - .remove(); + const { params, $slidesEl } = swiper; + if (!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return; - let slides = $selector.children(`.${params.slideClass}, swiper-slide`); - - if (params.loopFillGroupWithBlank) { - const blankSlidesNum = params.slidesPerGroup - (slides.length % params.slidesPerGroup); - if (blankSlidesNum !== params.slidesPerGroup) { - for (let i = 0; i < blankSlidesNum; i += 1) { - let blankNode; - if (swiper.isElement) { - blankNode = $(document.createElement('swiper-slide')).addClass( - `${params.slideBlankClass}`, - ); - } else { - blankNode = $(document.createElement('div')).addClass( - `${params.slideClass} ${params.slideBlankClass}`, - ); - } - $selector.append(blankNode); - } - slides = $selector.children(`.${params.slideClass}, swiper-slide`); - } - } - - if (params.slidesPerView === 'auto' && !params.loopedSlides) params.loopedSlides = slides.length; - - swiper.loopedSlides = Math.ceil(parseFloat(params.loopedSlides || params.slidesPerView, 10)); - swiper.loopedSlides += params.loopAdditionalSlides; - if (swiper.loopedSlides > slides.length && swiper.params.loopedSlidesLimit) { - swiper.loopedSlides = slides.length; - } - - const prependSlides = []; - const appendSlides = []; + const slides = $slidesEl.children(`.${params.slideClass}, swiper-slide`); slides.each((el, index) => { const slide = $(el); slide.attr('data-swiper-slide-index', index); }); - for (let i = 0; i < swiper.loopedSlides; i += 1) { - const index = i - Math.floor(i / slides.length) * slides.length; - appendSlides.push(slides.eq(index)[0]); - prependSlides.unshift(slides.eq(slides.length - index - 1)[0]); - } - - for (let i = 0; i < appendSlides.length; i += 1) { - $selector.append($(appendSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass)); - } - for (let i = prependSlides.length - 1; i >= 0; i -= 1) { - $selector.prepend($(prependSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass)); - } + swiper.loopFix(slideRealIndex); } diff --git a/src/core/loop/loopDestroy.js b/src/core/loop/loopDestroy.js index 421331390..bbe71609a 100644 --- a/src/core/loop/loopDestroy.js +++ b/src/core/loop/loopDestroy.js @@ -1,10 +1,21 @@ export default function loopDestroy() { const swiper = this; - const { $wrapperEl, params, slides } = swiper; - $wrapperEl - .children( - `.${params.slideClass}.${params.slideDuplicateClass},.${params.slideClass}.${params.slideBlankClass},swiper-slide.${params.slideDuplicateClass},swiper-slide.${params.slideBlankClass}`, - ) - .remove(); + const { slides, params, $slidesEl } = swiper; + if (!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return; + swiper.recalcSlides(); + + const newSlidesOrder = []; + slides.forEach((slideEl) => { + const index = + typeof slideEl.swiperSlideIndex === 'undefined' + ? slideEl.getAttribute('data-swiper-slide-index') * 1 + : slideEl.swiperSlideIndex; + newSlidesOrder[index] = slideEl; + }); slides.removeAttr('data-swiper-slide-index'); + newSlidesOrder.forEach((slideEl) => { + $slidesEl.append(slideEl); + }); + swiper.recalcSlides(); + swiper.slideTo(swiper.realIndex, 0); } diff --git a/src/core/loop/loopFix.js b/src/core/loop/loopFix.js index e762c2b8a..d4890c332 100644 --- a/src/core/loop/loopFix.js +++ b/src/core/loop/loopFix.js @@ -1,41 +1,73 @@ -export default function loopFix() { +export default function loopFix(slideRealIndex, slideTo = true) { const swiper = this; + if (!swiper.params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return; + swiper.emit('beforeLoopFix'); - const { - activeIndex, - slides, - loopedSlides, - allowSlidePrev, - allowSlideNext, - snapGrid, - rtlTranslate: rtl, - } = swiper; - let newIndex; + const { slides, allowSlidePrev, allowSlideNext, $slidesEl } = swiper; + swiper.allowSlidePrev = true; swiper.allowSlideNext = true; - const snapTranslate = -snapGrid[activeIndex]; - const diff = snapTranslate - swiper.getTranslate(); + const slidesPerView = + swiper.params.slidesPerView === 'auto' + ? swiper.slidesPerViewDynamic() + : Math.ceil(parseFloat(swiper.params.slidesPerView, 10)); + let loopedSlides = slidesPerView; + if (loopedSlides % swiper.params.slidesPerGroup !== 0) { + loopedSlides += swiper.params.slidesPerGroup - (loopedSlides % swiper.params.slidesPerGroup); + } + swiper.loopedSlides = loopedSlides; + + const prependSlidesIndexes = []; + const appendSlidesIndexes = []; + + const activeSlideIndex = swiper.slides + .filter((el) => el.classList.contains('swiper-slide-active')) + .index(); - // Fix For Negative Oversliding - if (activeIndex < loopedSlides) { - newIndex = slides.length - loopedSlides * 3 + activeIndex; - newIndex += loopedSlides; - const slideChanged = swiper.slideTo(newIndex, 0, false, true); - if (slideChanged && diff !== 0) { - swiper.setTranslate((rtl ? -swiper.translate : swiper.translate) - diff); + let slidesPrepended = 0; + let slidesAppended = 0; + // prepend last slides before start + if (activeSlideIndex < loopedSlides) { + slidesPrepended = loopedSlides - activeSlideIndex; + for (let i = 0; i < loopedSlides - activeSlideIndex; i += 1) { + const index = i - Math.floor(i / slides.length) * slides.length; + prependSlidesIndexes.push(slides.length - index - 1); } - } else if (activeIndex >= slides.length - loopedSlides) { - // Fix For Positive Oversliding - newIndex = -slides.length + activeIndex + loopedSlides; - newIndex += loopedSlides; - const slideChanged = swiper.slideTo(newIndex, 0, false, true); - if (slideChanged && diff !== 0) { - swiper.setTranslate((rtl ? -swiper.translate : swiper.translate) - diff); + } else if (activeSlideIndex /* + slidesPerView */ > swiper.slides.length - loopedSlides * 2) { + slidesAppended = activeSlideIndex - (swiper.slides.length - loopedSlides * 2); + for (let i = 0; i < slidesAppended; i += 1) { + const index = i - Math.floor(i / slides.length) * slides.length; + appendSlidesIndexes.push(index); } } + + prependSlidesIndexes.forEach((index) => { + $slidesEl.prepend(swiper.slides.eq(index)); + }); + appendSlidesIndexes.forEach((index) => { + $slidesEl.append(swiper.slides.eq(index)); + }); + swiper.recalcSlides(); + + if (slideTo) { + if (prependSlidesIndexes.length > 0) { + if (typeof slideRealIndex === 'undefined') { + swiper.slideTo(swiper.activeIndex + slidesPrepended, 0, false, true); + } else { + swiper.slideToLoop(slideRealIndex, 0, false, true); + } + } else if (appendSlidesIndexes.length > 0) { + if (typeof slideRealIndex === 'undefined') { + swiper.slideTo(swiper.activeIndex - slidesAppended, 0, false, true); + } else { + swiper.slideToLoop(slideRealIndex, 0, false, true); + } + } + } + swiper.allowSlidePrev = allowSlidePrev; swiper.allowSlideNext = allowSlideNext; diff --git a/src/core/slide/slideNext.js b/src/core/slide/slideNext.js index 466e89120..4514566d6 100644 --- a/src/core/slide/slideNext.js +++ b/src/core/slide/slideNext.js @@ -9,7 +9,7 @@ export default function slideNext(speed = this.params.speed, runCallbacks = true } const increment = swiper.activeIndex < params.slidesPerGroupSkip ? 1 : perGroup; if (params.loop) { - if (animating && params.loopPreventsSlide) return false; + if (animating) return false; swiper.loopFix(); // eslint-disable-next-line swiper._clientLeft = swiper.$wrapperEl[0].clientLeft; diff --git a/src/core/slide/slidePrev.js b/src/core/slide/slidePrev.js index d7040f432..ce71d3371 100644 --- a/src/core/slide/slidePrev.js +++ b/src/core/slide/slidePrev.js @@ -5,7 +5,7 @@ export default function slidePrev(speed = this.params.speed, runCallbacks = true if (!enabled) return swiper; if (params.loop) { - if (animating && params.loopPreventsSlide) return false; + if (animating) return false; swiper.loopFix(); // eslint-disable-next-line swiper._clientLeft = swiper.$wrapperEl[0].clientLeft; diff --git a/src/core/slide/slideTo.js b/src/core/slide/slideTo.js index bad8f9583..e40fc28c6 100644 --- a/src/core/slide/slideTo.js +++ b/src/core/slide/slideTo.js @@ -7,35 +7,8 @@ export default function slideTo( internal, initial, ) { - if (typeof index !== 'number' && typeof index !== 'string') { - throw new Error( - `The 'index' argument cannot have type other than 'number' or 'string'. [${typeof index}] given.`, - ); - } - if (typeof index === 'string') { - /** - * The `index` argument converted from `string` to `number`. - * @type {number} - */ - const indexAsNumber = parseInt(index, 10); - - /** - * Determines whether the `index` argument is a valid `number` - * after being converted from the `string` type. - * @type {boolean} - */ - const isValidNumber = isFinite(indexAsNumber); - - if (!isValidNumber) { - throw new Error( - `The passed-in 'index' (string) couldn't be converted to 'number'. [${index}] given.`, - ); - } - - // Knowing that the converted `index` is a valid number, - // we can update the original argument's value. - index = indexAsNumber; + index = parseInt(index, 10); } const swiper = this; @@ -103,7 +76,9 @@ export default function slideTo( translate > swiper.translate && translate > swiper.maxTranslate() ) { - if ((activeIndex || 0) !== slideIndex) return false; + if ((activeIndex || 0) !== slideIndex) { + return false; + } } } @@ -164,7 +139,6 @@ export default function slideTo( } return true; } - swiper.setTransition(speed); swiper.setTranslate(translate); swiper.updateActiveIndex(slideIndex); diff --git a/src/core/slide/slideToLoop.js b/src/core/slide/slideToLoop.js index d426cf2d9..8e7918a4f 100644 --- a/src/core/slide/slideToLoop.js +++ b/src/core/slide/slideToLoop.js @@ -5,34 +5,17 @@ export default function slideToLoop( internal, ) { if (typeof index === 'string') { - /** - * The `index` argument converted from `string` to `number`. - * @type {number} - */ const indexAsNumber = parseInt(index, 10); - /** - * Determines whether the `index` argument is a valid `number` - * after being converted from the `string` type. - * @type {boolean} - */ - const isValidNumber = isFinite(indexAsNumber); - - if (!isValidNumber) { - throw new Error( - `The passed-in 'index' (string) couldn't be converted to 'number'. [${index}] given.`, - ); - } - - // Knowing that the converted `index` is a valid number, - // we can update the original argument's value. index = indexAsNumber; } const swiper = this; let newIndex = index; if (swiper.params.loop) { - newIndex += swiper.loopedSlides; + newIndex = swiper.slides + .filter((slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === newIndex) + .index(); } return swiper.slideTo(newIndex, speed, runCallbacks, internal); diff --git a/src/core/update/updateActiveIndex.js b/src/core/update/updateActiveIndex.js index db708fc44..33580e0a2 100644 --- a/src/core/update/updateActiveIndex.js +++ b/src/core/update/updateActiveIndex.js @@ -1,8 +1,31 @@ +export function getActiveIndexByTranslate(swiper) { + const { slidesGrid, params } = swiper; + const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate; + let activeIndex; + for (let i = 0; i < slidesGrid.length; i += 1) { + if (typeof slidesGrid[i + 1] !== 'undefined') { + if ( + translate >= slidesGrid[i] && + translate < slidesGrid[i + 1] - (slidesGrid[i + 1] - slidesGrid[i]) / 2 + ) { + activeIndex = i; + } else if (translate >= slidesGrid[i] && translate < slidesGrid[i + 1]) { + activeIndex = i + 1; + } + } else if (translate >= slidesGrid[i]) { + activeIndex = i; + } + } + // Normalize slideIndex + if (params.normalizeSlideIndex) { + if (activeIndex < 0 || typeof activeIndex === 'undefined') activeIndex = 0; + } + return activeIndex; +} export default function updateActiveIndex(newActiveIndex) { const swiper = this; const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate; const { - slidesGrid, snapGrid, params, activeIndex: previousIndex, @@ -12,24 +35,7 @@ export default function updateActiveIndex(newActiveIndex) { let activeIndex = newActiveIndex; let snapIndex; if (typeof activeIndex === 'undefined') { - for (let i = 0; i < slidesGrid.length; i += 1) { - if (typeof slidesGrid[i + 1] !== 'undefined') { - if ( - translate >= slidesGrid[i] && - translate < slidesGrid[i + 1] - (slidesGrid[i + 1] - slidesGrid[i]) / 2 - ) { - activeIndex = i; - } else if (translate >= slidesGrid[i] && translate < slidesGrid[i + 1]) { - activeIndex = i + 1; - } - } else if (translate >= slidesGrid[i]) { - activeIndex = i; - } - } - // Normalize slideIndex - if (params.normalizeSlideIndex) { - if (activeIndex < 0 || typeof activeIndex === 'undefined') activeIndex = 0; - } + activeIndex = getActiveIndexByTranslate(swiper); } if (snapGrid.indexOf(translate) >= 0) { snapIndex = snapGrid.indexOf(translate); diff --git a/src/core/update/updateProgress.js b/src/core/update/updateProgress.js index a448e6ec8..736febd7d 100644 --- a/src/core/update/updateProgress.js +++ b/src/core/update/updateProgress.js @@ -7,7 +7,7 @@ export default function updateProgress(translate) { } const params = swiper.params; const translatesDiff = swiper.maxTranslate() - swiper.minTranslate(); - let { progress, isBeginning, isEnd } = swiper; + let { progress, isBeginning, isEnd, progressLoop } = swiper; const wasBeginning = isBeginning; const wasEnd = isEnd; if (translatesDiff === 0) { @@ -19,8 +19,29 @@ export default function updateProgress(translate) { isBeginning = progress <= 0; isEnd = progress >= 1; } + + if (params.loop) { + const firstSlideIndex = swiper.slides + .filter((el) => el.getAttribute('data-swiper-slide-index') === '0') + .index(); + const lastSlideIndex = swiper.slides + .filter((el) => el.getAttribute('data-swiper-slide-index') * 1 === swiper.slides.length - 1) + .index(); + const firstSlideTranslate = swiper.slidesGrid[firstSlideIndex]; + const lastSlideTranslate = swiper.slidesGrid[lastSlideIndex]; + const translateMax = swiper.slidesGrid[swiper.slidesGrid.length - 1]; + const translateAbs = Math.abs(translate); + if (translateAbs >= firstSlideTranslate) { + progressLoop = (translateAbs - firstSlideTranslate) / translateMax; + } else { + progressLoop = (translateAbs + translateMax - lastSlideTranslate) / translateMax; + } + if (progressLoop > 1) progressLoop -= 1; + } + Object.assign(swiper, { progress, + progressLoop, isBeginning, isEnd, }); diff --git a/src/element/get-params.js b/src/element/get-params.js index b3ee9a711..302471cbd 100644 --- a/src/element/get-params.js +++ b/src/element/get-params.js @@ -5,6 +5,7 @@ import defaults from '../core/defaults.js'; const formatValue = (val) => { if (parseFloat(val) === Number(val)) return Number(val); if (val === 'true') return true; + if (val === '') return true; if (val === 'false') return false; if (val === 'null') return null; if (val === 'undefined') return undefined; diff --git a/src/modules/controller/controller.js b/src/modules/controller/controller.js index 6fbca38c3..abf7d9c46 100644 --- a/src/modules/controller/controller.js +++ b/src/modules/controller/controller.js @@ -123,6 +123,7 @@ export default function Controller({ swiper, extendParams, on }) { } c.$wrapperEl.transitionEnd(() => { if (!controlled) return; + if (swiper.params.loop) swiper.loopFix(undefined, false); if (c.params.loop && swiper.params.controller.by === 'slide') { c.loopFix(); } diff --git a/src/modules/manipulation/methods/addSlide.js b/src/modules/manipulation/methods/addSlide.js index fbfcb5b24..78c0ffe43 100644 --- a/src/modules/manipulation/methods/addSlide.js +++ b/src/modules/manipulation/methods/addSlide.js @@ -1,11 +1,11 @@ export default function addSlide(index, slides) { const swiper = this; - const { $wrapperEl, params, activeIndex } = swiper; + const { params, activeIndex, $slidesEl } = swiper; let activeIndexBuffer = activeIndex; if (params.loop) { activeIndexBuffer -= swiper.loopedSlides; swiper.loopDestroy(); - swiper.slides = $wrapperEl.children(`.${params.slideClass}, swiper-slide`); + swiper.recalcSlides(); } const baseLength = swiper.slides.length; if (index <= 0) { @@ -27,18 +27,20 @@ export default function addSlide(index, slides) { if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { - if (slides[i]) $wrapperEl.append(slides[i]); + if (slides[i]) $slidesEl.append(slides[i]); } newActiveIndex = activeIndexBuffer > index ? activeIndexBuffer + slides.length : activeIndexBuffer; } else { - $wrapperEl.append(slides); + $slidesEl.append(slides); } for (let i = 0; i < slidesBuffer.length; i += 1) { - $wrapperEl.append(slidesBuffer[i]); + $slidesEl.append(slidesBuffer[i]); } + swiper.recalcSlides(); + if (params.loop) { swiper.loopCreate(); } diff --git a/src/modules/manipulation/methods/appendSlide.js b/src/modules/manipulation/methods/appendSlide.js index 7876c2414..f35835da0 100644 --- a/src/modules/manipulation/methods/appendSlide.js +++ b/src/modules/manipulation/methods/appendSlide.js @@ -1,16 +1,18 @@ export default function appendSlide(slides) { const swiper = this; - const { $wrapperEl, params } = swiper; + const { params, $slidesEl } = swiper; + if (params.loop) { swiper.loopDestroy(); } if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { - if (slides[i]) $wrapperEl.append(slides[i]); + if (slides[i]) $slidesEl.append(slides[i]); } } else { - $wrapperEl.append(slides); + $slidesEl.append(slides); } + swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } diff --git a/src/modules/manipulation/methods/prependSlide.js b/src/modules/manipulation/methods/prependSlide.js index cc5f0f838..0ce45c72c 100644 --- a/src/modules/manipulation/methods/prependSlide.js +++ b/src/modules/manipulation/methods/prependSlide.js @@ -1,6 +1,6 @@ export default function prependSlide(slides) { const swiper = this; - const { params, $wrapperEl, activeIndex } = swiper; + const { params, activeIndex, $slidesEl } = swiper; if (params.loop) { swiper.loopDestroy(); @@ -8,12 +8,13 @@ export default function prependSlide(slides) { let newActiveIndex = activeIndex + 1; if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { - if (slides[i]) $wrapperEl.prepend(slides[i]); + if (slides[i]) $slidesEl.prepend(slides[i]); } newActiveIndex = activeIndex + slides.length; } else { - $wrapperEl.prepend(slides); + $slidesEl.prepend(slides); } + swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } diff --git a/src/modules/manipulation/methods/removeSlide.js b/src/modules/manipulation/methods/removeSlide.js index d30af9676..17cb9865e 100644 --- a/src/modules/manipulation/methods/removeSlide.js +++ b/src/modules/manipulation/methods/removeSlide.js @@ -1,12 +1,11 @@ export default function removeSlide(slidesIndexes) { const swiper = this; - const { params, $wrapperEl, activeIndex } = swiper; + const { params, activeIndex } = swiper; let activeIndexBuffer = activeIndex; if (params.loop) { activeIndexBuffer -= swiper.loopedSlides; swiper.loopDestroy(); - swiper.slides = $wrapperEl.children(`.${params.slideClass}, swiper-slide`); } let newActiveIndex = activeIndexBuffer; let indexToRemove; @@ -25,6 +24,7 @@ export default function removeSlide(slidesIndexes) { newActiveIndex = Math.max(newActiveIndex, 0); } + swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } diff --git a/src/modules/pagination/pagination.js b/src/modules/pagination/pagination.js index 771f4319c..e6fb3e744 100644 --- a/src/modules/pagination/pagination.js +++ b/src/modules/pagination/pagination.js @@ -67,25 +67,22 @@ export default function Pagination({ swiper, extendParams, on, emit }) { const rtl = swiper.rtl; const params = swiper.params.pagination; if (isPaginationDisabled()) return; + + const $el = swiper.pagination.$el; + // Current/Total + let current; const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : swiper.slides.length; - const $el = swiper.pagination.$el; - // Current/Total - let current; const total = swiper.params.loop - ? Math.ceil((slidesLength - swiper.loopedSlides * 2) / swiper.params.slidesPerGroup) + ? Math.ceil(slidesLength / swiper.params.slidesPerGroup) : swiper.snapGrid.length; if (swiper.params.loop) { - current = Math.ceil( - (swiper.activeIndex - swiper.loopedSlides) / swiper.params.slidesPerGroup, - ); - if (current > slidesLength - 1 - swiper.loopedSlides * 2) { - current -= slidesLength - swiper.loopedSlides * 2; - } - if (current > total - 1) current -= total; - if (current < 0 && swiper.params.paginationType !== 'bullets') current = total + current; + current = + swiper.params.slidesPerGroup > 1 + ? Math.floor(swiper.realIndex / swiper.params.slidesPerGroup) + : swiper.realIndex; } else if (typeof swiper.snapIndex !== 'undefined') { current = swiper.snapIndex; } else { @@ -108,7 +105,7 @@ export default function Pagination({ swiper, extendParams, on, emit }) { `${bulletSize * (params.dynamicMainBullets + 4)}px`, ); if (params.dynamicMainBullets > 1 && swiper.previousIndex !== undefined) { - dynamicBulletIndex += current - (swiper.previousIndex - swiper.loopedSlides || 0); + dynamicBulletIndex += current - (swiper.previousIndex || 0); if (dynamicBulletIndex > params.dynamicMainBullets - 1) { dynamicBulletIndex = params.dynamicMainBullets - 1; } else if (dynamicBulletIndex < 0) { @@ -145,7 +142,6 @@ export default function Pagination({ swiper, extendParams, on, emit }) { }); } else { const $bullet = bullets.eq(current); - const bulletIndex = $bullet.index(); $bullet.addClass(params.bulletActiveClass); if (params.dynamicBullets) { const $firstDisplayedBullet = bullets.eq(firstIndex); @@ -153,22 +149,9 @@ export default function Pagination({ swiper, extendParams, on, emit }) { for (let i = firstIndex; i <= lastIndex; i += 1) { bullets.eq(i).addClass(`${params.bulletActiveClass}-main`); } - if (swiper.params.loop) { - if (bulletIndex >= bullets.length) { - for (let i = params.dynamicMainBullets; i >= 0; i -= 1) { - bullets.eq(bullets.length - i).addClass(`${params.bulletActiveClass}-main`); - } - bullets - .eq(bullets.length - params.dynamicMainBullets - 1) - .addClass(`${params.bulletActiveClass}-prev`); - } else { - setSideBullets($firstDisplayedBullet, 'prev'); - setSideBullets($lastDisplayedBullet, 'next'); - } - } else { - setSideBullets($firstDisplayedBullet, 'prev'); - setSideBullets($lastDisplayedBullet, 'next'); - } + + setSideBullets($firstDisplayedBullet, 'prev'); + setSideBullets($lastDisplayedBullet, 'next'); } } if (params.dynamicBullets) { @@ -228,12 +211,11 @@ export default function Pagination({ swiper, extendParams, on, emit }) { let paginationHTML = ''; if (params.type === 'bullets') { let numberOfBullets = swiper.params.loop - ? Math.ceil((slidesLength - swiper.loopedSlides * 2) / swiper.params.slidesPerGroup) + ? Math.ceil(slidesLength / swiper.params.slidesPerGroup) : swiper.snapGrid.length; if ( swiper.params.freeMode && swiper.params.freeMode.enabled && - !swiper.params.loop && numberOfBullets > slidesLength ) { numberOfBullets = slidesLength; @@ -322,9 +304,12 @@ export default function Pagination({ swiper, extendParams, on, emit }) { if (params.clickable) { $el.on('click', classesToSelector(params.bulletClass), function onClick(e) { e.preventDefault(); - let index = $(this).index() * swiper.params.slidesPerGroup; - if (swiper.params.loop) index += swiper.loopedSlides; - swiper.slideTo(index); + const index = $(this).index() * swiper.params.slidesPerGroup; + if (swiper.params.loop) { + swiper.slideToLoop(index); + } else { + swiper.slideTo(index); + } }); } @@ -363,28 +348,16 @@ export default function Pagination({ swiper, extendParams, on, emit }) { } }); on('activeIndexChange', () => { - if (swiper.params.loop) { - update(); - } else if (typeof swiper.snapIndex === 'undefined') { + if (typeof swiper.snapIndex === 'undefined') { update(); } }); on('snapIndexChange', () => { - if (!swiper.params.loop) { - update(); - } - }); - on('slidesLengthChange', () => { - if (swiper.params.loop) { - render(); - update(); - } + update(); }); on('snapGridLengthChange', () => { - if (!swiper.params.loop) { - render(); - update(); - } + render(); + update(); }); on('destroy', () => { destroy(); diff --git a/src/modules/scrollbar/scrollbar.js b/src/modules/scrollbar/scrollbar.js index b7a492348..53da11bf2 100644 --- a/src/modules/scrollbar/scrollbar.js +++ b/src/modules/scrollbar/scrollbar.js @@ -38,9 +38,10 @@ export default function Scrollbar({ swiper, extendParams, on, emit }) { function setTranslate() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; - const { scrollbar, rtlTranslate: rtl, progress } = swiper; + const { scrollbar, rtlTranslate: rtl } = swiper; const { $dragEl, $el } = scrollbar; const params = swiper.params.scrollbar; + const progress = swiper.params.loop ? swiper.progressLoop : swiper.progress; let newSize = dragSize; let newPos = (trackSize - dragSize) * progress; diff --git a/src/modules/thumbs/thumbs.js b/src/modules/thumbs/thumbs.js index 38b3b3862..a617ffa36 100644 --- a/src/modules/thumbs/thumbs.js +++ b/src/modules/thumbs/thumbs.js @@ -75,6 +75,7 @@ export default function Thumb({ swiper, extendParams, on }) { watchSlidesProgress: true, slideToClickedSlide: false, }); + swiper.thumbs.swiper.update(); } else if (isObject(thumbsParams.swiper)) { const thumbsSwiperParams = Object.assign({}, thumbsParams.swiper); Object.assign(thumbsSwiperParams, { diff --git a/src/react/loop.js b/src/react/loop.js deleted file mode 100644 index d487a8116..000000000 --- a/src/react/loop.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import { calcLoopedSlides } from '../shared/calc-looped-slides.js'; - -function renderLoop(swiper, slides, swiperParams) { - const modifiedSlides = slides.map((child, index) => { - return React.cloneElement(child, { swiper, 'data-swiper-slide-index': index }); - }); - - function duplicateSlide(child, index, position) { - return React.cloneElement(child, { - key: `${child.key}-duplicate-${index}-${position}`, - className: `${child.props.className || ''} ${swiperParams.slideDuplicateClass}`, - }); - } - - if (swiperParams.loopFillGroupWithBlank) { - const blankSlidesNum = - swiperParams.slidesPerGroup - (modifiedSlides.length % swiperParams.slidesPerGroup); - if (blankSlidesNum !== swiperParams.slidesPerGroup) { - for (let i = 0; i < blankSlidesNum; i += 1) { - const blankSlide = ( -
- ); - modifiedSlides.push(blankSlide); - } - } - } - - if (swiperParams.slidesPerView === 'auto' && !swiperParams.loopedSlides) { - swiperParams.loopedSlides = modifiedSlides.length; - } - - const loopedSlides = calcLoopedSlides(modifiedSlides, swiperParams); - - const prependSlides = []; - const appendSlides = []; - for (let i = 0; i < loopedSlides; i += 1) { - const index = i - Math.floor(i / modifiedSlides.length) * modifiedSlides.length; - appendSlides.push(duplicateSlide(modifiedSlides[index], i, 'append')); - prependSlides.unshift( - duplicateSlide(modifiedSlides[modifiedSlides.length - index - 1], i, 'prepend'), - ); - } - if (swiper) { - swiper.loopedSlides = loopedSlides; - } - - return [...prependSlides, ...modifiedSlides, ...appendSlides]; -} - -export { calcLoopedSlides, renderLoop }; diff --git a/src/react/swiper-slide.js b/src/react/swiper-slide.js index 4094e26cb..cd062ee78 100644 --- a/src/react/swiper-slide.js +++ b/src/react/swiper-slide.js @@ -5,7 +5,17 @@ import { SwiperSlideContext } from './context.js'; const SwiperSlide = forwardRef( ( - { tag: Tag = 'div', children, className = '', swiper, zoom, lazy, virtualIndex, ...rest } = {}, + { + tag: Tag = 'div', + children, + className = '', + swiper, + zoom, + lazy, + virtualIndex, + swiperSlideIndex, + ...rest + } = {}, externalRef, ) => { const slideElRef = useRef(null); @@ -17,6 +27,9 @@ const SwiperSlide = forwardRef( } } useIsomorphicLayoutEffect(() => { + if (typeof swiperSlideIndex !== 'undefined') { + slideElRef.current.swiperSlideIndex = swiperSlideIndex; + } if (externalRef) { externalRef.current = slideElRef.current; } diff --git a/src/react/swiper.js b/src/react/swiper.js index d84f5ea6d..16e8efe9b 100644 --- a/src/react/swiper.js +++ b/src/react/swiper.js @@ -9,7 +9,6 @@ import { uniqueClasses, extend, } from '../components-shared/utils.js'; -import { renderLoop, calcLoopedSlides } from './loop.js'; import { getChangedParams } from '../components-shared/get-changed-params.js'; import { getChildren } from './get-children.js'; import { updateSwiper } from '../components-shared/update-swiper.js'; @@ -64,11 +63,6 @@ const Swiper = forwardRef( Object.assign(swiperParams.on, events); eventsAssigned = true; swiperRef.current = new SwiperCore(swiperParams); - swiperRef.current.loopCreate = () => {}; - swiperRef.current.loopDestroy = () => {}; - if (swiperParams.loop) { - swiperRef.current.loopedSlides = calcLoopedSlides(slides, swiperParams); - } if (swiperRef.current.virtual && swiperRef.current.params.virtual.enabled) { swiperRef.current.virtual.slides = slides; const extendWith = { @@ -189,12 +183,9 @@ const Swiper = forwardRef( if (swiperParams.virtual) { return renderVirtual(swiperRef.current, slides, virtualData); } - if (!swiperParams.loop || (swiperRef.current && swiperRef.current.destroyed)) { - return slides.map((child) => { - return React.cloneElement(child, { swiper: swiperRef.current }); - }); - } - return renderLoop(swiperRef.current, slides, swiperParams); + return slides.map((child, index) => { + return React.cloneElement(child, { swiper: swiperRef.current, swiperSlideIndex: index }); + }); } return ( diff --git a/src/shared/calc-looped-slides.js b/src/shared/calc-looped-slides.js deleted file mode 100644 index 84cb0b437..000000000 --- a/src/shared/calc-looped-slides.js +++ /dev/null @@ -1,21 +0,0 @@ -import Swiper from 'swiper'; - -export const calcLoopedSlides = (slides, swiperParams) => { - let slidesPerViewParams = swiperParams.slidesPerView; - if (swiperParams.breakpoints) { - const breakpoint = Swiper.prototype.getBreakpoint(swiperParams.breakpoints); - const breakpointOnlyParams = - breakpoint in swiperParams.breakpoints ? swiperParams.breakpoints[breakpoint] : undefined; - if (breakpointOnlyParams && breakpointOnlyParams.slidesPerView) { - slidesPerViewParams = breakpointOnlyParams.slidesPerView; - } - } - let loopedSlides = Math.ceil(parseFloat(swiperParams.loopedSlides || slidesPerViewParams, 10)); - - loopedSlides += swiperParams.loopAdditionalSlides; - - if (loopedSlides > slides.length && swiperParams.loopedSlidesLimit) { - loopedSlides = slides.length; - } - return loopedSlides; -}; diff --git a/src/types/swiper-options.d.ts b/src/types/swiper-options.d.ts index 142131136..c799e07d6 100644 --- a/src/types/swiper-options.d.ts +++ b/src/types/swiper-options.d.ts @@ -264,7 +264,6 @@ export interface SwiperOptions { /** * Number of slides per view (slides visible at the same time on slider's container). - * @note If you use it with "auto" value and along with `loop: true` then you need to specify `loopedSlides` parameter with amount of slides to loop (duplicate) * @note `slidesPerView: 'auto'` is currently not compatible with multirow mode, when `grid.rows` > 1 * * @default 1 @@ -589,7 +588,6 @@ export interface SwiperOptions { * * @default false * - * @note If you use it along with `slidesPerView: 'auto'` then you need to specify `loopedSlides` parameter with amount of slides to loop (duplicate). Should not be used together with `rewind` mode */ loop?: boolean; @@ -602,40 +600,6 @@ export interface SwiperOptions { */ rewind?: boolean; - /** - * Addition number of slides that will be cloned after creating of loop - * - * @default 0 - */ - loopAdditionalSlides?: number; - - /** - * If you use `slidesPerView:'auto'` with loop mode you should tell to Swiper how many slides it should loop (duplicate) using this parameter - * - * @default null - */ - loopedSlides?: number | null; - - /** - * When enabled then amount of duplicated slides will not exceed amount of original slides. Useful to disable and increase `loopedSlides` when you have a lot of slides per view and not sufficient amount of original slides - * - * @default true - */ - loopedSlidesLimit?: boolean; - - /** - * Enable and loop mode will fill groups with insufficient number of slides with blank slides. Good to be used with `slidesPerGroup` parameter - * - * @default false - */ - loopFillGroupWithBlank?: boolean; - /** - * When enabled it prevents Swiper slide prev/next transitions when transitions is already in progress (has effect when `loop` enabled) - * - * @default true - */ - loopPreventsSlide?: boolean; - /** * Allows to set different parameter for different responsive breakpoints (screen sizes). Not all parameters can be changed in breakpoints, only those which do not require different layout and logic, like `slidesPerView`, `slidesPerGroup`, `spaceBetween`, `grid.rows`. Such parameters like `loop` and `effect` won't work * @@ -830,17 +794,6 @@ export interface SwiperOptions { */ slideDuplicatePrevClass?: string; - /** - * CSS class name of blank slide append to fill groups in loop mode when `loopFillGroupWithBlank` is also enabled - * - * @default 'swiper-slide-invisible-blank' - * - * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes - * - * @note Not supported in Swiper React/Vue - */ - slideBlankClass?: string; - /** * CSS class name of slides' wrapper * diff --git a/src/vue/swiper-slide.js b/src/vue/swiper-slide.js index 83e77189a..58f1186f9 100644 --- a/src/vue/swiper-slide.js +++ b/src/vue/swiper-slide.js @@ -18,8 +18,9 @@ const SwiperSlide = { default: 'div', }, swiperRef: { type: Object, required: false }, - zoom: { type: Boolean, default: undefined }, - lazy: { type: Boolean, default: false }, + swiperSlideIndex: { type: Number, default: undefined, required: false }, + zoom: { type: Boolean, default: undefined, required: false }, + lazy: { type: Boolean, default: false, required: false }, virtualIndex: { type: [String, Number], default: undefined, @@ -52,6 +53,9 @@ const SwiperSlide = { onUpdated(() => { if (!slideElRef.value || !swiperRef || !swiperRef.value) return; + if (typeof props.swiperSlideIndex !== 'undefined') { + slideElRef.value.swiperSlideIndex = props.swiperSlideIndex; + } if (swiperRef.value.destroyed) { if (slideClasses.value !== 'swiper-slide') { slideClasses.value = 'swiper-slide'; @@ -89,7 +93,13 @@ const SwiperSlide = { { class: uniqueClasses(`${slideClasses.value}`), ref: slideElRef, - 'data-swiper-slide-index': props.virtualIndex, + 'data-swiper-slide-index': + typeof props.virtualIndex === 'undefined' && + swiperRef && + swiperRef.value && + swiperRef.value.params.loop + ? props.swiperSlideIndex + : props.virtualIndex, onLoadCapture: onLoad, }, props.zoom diff --git a/src/vue/swiper-vue.d.ts b/src/vue/swiper-vue.d.ts index 0e453531d..fef0c4659 100644 --- a/src/vue/swiper-vue.d.ts +++ b/src/vue/swiper-vue.d.ts @@ -248,22 +248,6 @@ declare const Swiper: DefineComponent< default: undefined; }; loop: { type: BooleanConstructor; default: undefined }; - loopAdditionalSlides: { - type: NumberConstructor; - default: undefined; - }; - loopedSlides: { - type: NumberConstructor; - default: undefined; - }; - loopFillGroupWithBlank: { - type: BooleanConstructor; - default: undefined; - }; - loopPreventsSlide: { - type: BooleanConstructor; - default: undefined; - }; rewind: { type: BooleanConstructor; default: undefined }; allowSlidePrev: { type: BooleanConstructor; @@ -301,10 +285,6 @@ declare const Swiper: DefineComponent< type: StringConstructor; default: undefined; }; - slideBlankClass: { - type: StringConstructor; - default: undefined; - }; slideActiveClass: { type: StringConstructor; default: undefined; diff --git a/src/vue/swiper.js b/src/vue/swiper.js index 7c4ef2970..3730e62f3 100644 --- a/src/vue/swiper.js +++ b/src/vue/swiper.js @@ -9,7 +9,6 @@ import { uniqueClasses, extend, } from '../components-shared/utils.js'; -import { renderLoop, calcLoopedSlides } from './loop.js'; import { getChangedParams } from '../components-shared/get-changed-params.js'; import { getChildren } from './get-children.js'; import { updateSwiper } from '../components-shared/update-swiper.js'; @@ -87,11 +86,6 @@ const Swiper = { preventClicksPropagation: { type: Boolean, default: undefined }, slideToClickedSlide: { type: Boolean, default: undefined }, loop: { type: Boolean, default: undefined }, - loopAdditionalSlides: { type: Number, default: undefined }, - loopedSlides: { type: Number, default: undefined }, - loopedSlidesLimit: { type: Boolean, default: true }, - loopFillGroupWithBlank: { type: Boolean, default: undefined }, - loopPreventsSlide: { type: Boolean, default: undefined }, rewind: { type: Boolean, default: undefined }, allowSlidePrev: { type: Boolean, default: undefined }, allowSlideNext: { type: Boolean, default: undefined }, @@ -102,7 +96,6 @@ const Swiper = { passiveListeners: { type: Boolean, default: undefined }, containerModifierClass: { type: String, default: undefined }, slideClass: { type: String, default: undefined }, - slideBlankClass: { type: String, default: undefined }, slideActiveClass: { type: String, default: undefined }, slideDuplicateActiveClass: { type: String, default: undefined }, slideVisibleClass: { type: String, default: undefined }, @@ -270,11 +263,6 @@ const Swiper = { // init Swiper swiperRef.value = new SwiperCore(swiperParams); - swiperRef.value.loopCreate = () => {}; - swiperRef.value.loopDestroy = () => {}; - if (swiperParams.loop) { - swiperRef.value.loopedSlides = calcLoopedSlides(slidesRef.value, swiperParams); - } if (swiperRef.value.virtual && swiperRef.value.params.virtual.enabled) { swiperRef.value.virtual.slides = slidesRef.value; const extendWith = { @@ -363,14 +351,12 @@ const Swiper = { if (swiperParams.virtual) { return renderVirtual(swiperRef, slides, virtualData.value); } - if (!swiperParams.loop || (swiperRef.value && swiperRef.value.destroyed)) { - slides.forEach((slide) => { - if (!slide.props) slide.props = {}; - slide.props.swiperRef = swiperRef; - }); - return slides; - } - return renderLoop(swiperRef, slides, swiperParams); + slides.forEach((slide, index) => { + if (!slide.props) slide.props = {}; + slide.props.swiperRef = swiperRef; + slide.props.swiperSlideIndex = index; + }); + return slides; } return () => {