From f890f1e18a72bae78dbb0d445a6f3b95dae70363 Mon Sep 17 00:00:00 2001 From: Vladimir Kharlampidi Date: Wed, 7 Dec 2022 13:11:27 +0300 Subject: [PATCH] feat(virtual): support loop mode with virtual slides --- src/core/events/onResize.js | 4 +++- src/core/loop/loopFix.js | 16 +++++++------ src/core/slide/slideTo.js | 1 - src/core/update/updateActiveIndex.js | 5 +++- src/core/update/updateSlides.js | 22 +++++++++++++---- src/modules/virtual/virtual.js | 16 ++++++------- src/react/virtual.js | 33 ++++++++++++++++++++------ src/vue/virtual.js | 35 +++++++++++++++++++++------- 8 files changed, 92 insertions(+), 40 deletions(-) diff --git a/src/core/events/onResize.js b/src/core/events/onResize.js index 95fbc7245..9b881ba25 100644 --- a/src/core/events/onResize.js +++ b/src/core/events/onResize.js @@ -24,11 +24,13 @@ export default function onResize() { swiper.updateSlides(); swiper.updateSlidesClasses(); + const isVirtualLoop = isVirtual && params.loop; if ( (params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && - !swiper.params.centeredSlides + !swiper.params.centeredSlides && + !isVirtualLoop ) { swiper.slideTo(swiper.slides.length - 1, 0, false, true); } else { diff --git a/src/core/loop/loopFix.js b/src/core/loop/loopFix.js index 70fa199ea..692d228ad 100644 --- a/src/core/loop/loopFix.js +++ b/src/core/loop/loopFix.js @@ -3,15 +3,17 @@ export default function loopFix(slideRealIndex, slideTo = true) { if (!swiper.params.loop) return; swiper.emit('beforeLoopFix'); - const { slides, allowSlidePrev, allowSlideNext, $slidesEl } = swiper; + const { slides, allowSlidePrev, allowSlideNext, $slidesEl, params } = swiper; swiper.allowSlidePrev = true; swiper.allowSlideNext = true; - if (swiper.virtual && swiper.params.virtual.enabled) { + if (swiper.virtual && params.virtual.enabled) { if (slideTo) { - if (swiper.snapIndex === 0) { + if (!params.centeredSlides && swiper.snapIndex === 0) { swiper.slideTo(swiper.virtual.slides.length, 0, false, true); + } else if (params.centeredSlides && swiper.snapIndex < params.slidesPerView) { + swiper.slideTo(swiper.virtual.slides.length + swiper.snapIndex, 0, false, true); } else if (swiper.snapIndex === swiper.snapGrid.length - 1) { swiper.slideTo(swiper.virtual.slidesBefore, 0, false, true); } @@ -23,12 +25,12 @@ export default function loopFix(slideRealIndex, slideTo = true) { } const slidesPerView = - swiper.params.slidesPerView === 'auto' + params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() - : Math.ceil(parseFloat(swiper.params.slidesPerView, 10)); + : Math.ceil(parseFloat(params.slidesPerView, 10)); let loopedSlides = slidesPerView; - if (loopedSlides % swiper.params.slidesPerGroup !== 0) { - loopedSlides += swiper.params.slidesPerGroup - (loopedSlides % swiper.params.slidesPerGroup); + if (loopedSlides % params.slidesPerGroup !== 0) { + loopedSlides += params.slidesPerGroup - (loopedSlides % params.slidesPerGroup); } swiper.loopedSlides = loopedSlides; diff --git a/src/core/slide/slideTo.js b/src/core/slide/slideTo.js index e40fc28c6..0522c10ca 100644 --- a/src/core/slide/slideTo.js +++ b/src/core/slide/slideTo.js @@ -38,7 +38,6 @@ export default function slideTo( if (snapIndex >= snapGrid.length) snapIndex = snapGrid.length - 1; const translate = -snapGrid[snapIndex]; - // Normalize slideIndex if (params.normalizeSlideIndex) { for (let i = 0; i < slidesGrid.length; i += 1) { diff --git a/src/core/update/updateActiveIndex.js b/src/core/update/updateActiveIndex.js index e29717926..f885f0ff6 100644 --- a/src/core/update/updateActiveIndex.js +++ b/src/core/update/updateActiveIndex.js @@ -38,7 +38,10 @@ export default function updateActiveIndex(newActiveIndex) { const getVirtualRealIndex = (aIndex) => { let realIndex = aIndex - swiper.virtual.slidesBefore; if (realIndex < 0) { - realIndex = swiper.virtual.slides.length - 1; + realIndex = swiper.virtual.slides.length + realIndex; + } + if (realIndex >= swiper.virtual.slides.length) { + realIndex -= swiper.virtual.slides.length; } return realIndex; }; diff --git a/src/core/update/updateSlides.js b/src/core/update/updateSlides.js index add7622ac..28db6aa4c 100644 --- a/src/core/update/updateSlides.js +++ b/src/core/update/updateSlides.js @@ -210,11 +210,23 @@ export default function updateSlides() { } } if (isVirtual && params.loop) { - snapGrid.push( - snapGrid[snapGrid.length - 1] + slidesSizesGrid[0] + spaceBetween, - snapGrid[snapGrid.length - 1] + (slidesSizesGrid[0] + spaceBetween) * 2, - ); - slidesGrid.push(slidesGrid[slidesGrid.length - 1] + slidesSizesGrid[0] + spaceBetween); + const size = slidesSizesGrid[0] + spaceBetween; + if (params.slidesPerGroup > 1) { + const groups = Math.ceil( + (swiper.virtual.slidesBefore + swiper.virtual.slidesAfter) / params.slidesPerGroup, + ); + const groupSize = size * params.slidesPerGroup; + for (let i = 0; i < groups; i += 1) { + snapGrid.push(snapGrid[snapGrid.length - 1] + groupSize); + } + } + for (let i = 0; i < swiper.virtual.slidesBefore + swiper.virtual.slidesAfter; i += 1) { + if (params.slidesPerGroup === 1) { + snapGrid.push(snapGrid[snapGrid.length - 1] + size); + } + slidesGrid.push(slidesGrid[slidesGrid.length - 1] + size); + swiper.virtualSize += size; + } } if (snapGrid.length === 0) snapGrid = [0]; diff --git a/src/modules/virtual/virtual.js b/src/modules/virtual/virtual.js index f9ab488bc..7b4eea23d 100644 --- a/src/modules/virtual/virtual.js +++ b/src/modules/virtual/virtual.js @@ -71,23 +71,21 @@ export default function Virtual({ swiper, extendParams, on, emit }) { slidesBefore = Math.floor(slidesPerView / 2) + slidesPerGroup + addSlidesBefore; } else { slidesAfter = slidesPerView + (slidesPerGroup - 1) + addSlidesAfter; - slidesBefore = slidesPerGroup + addSlidesBefore; + slidesBefore = (isLoop ? slidesPerView : slidesPerGroup) + addSlidesBefore; } let from = activeIndex - slidesBefore; - if (!isLoop) { - from = Math.max(from, 0); - } - if (activeIndex >= slidesBefore) { - // from -= 1; - } let to = activeIndex + slidesAfter; if (!isLoop) { + from = Math.max(from, 0); to = Math.min(to, slides.length - 1); } let offset = (swiper.slidesGrid[from] || 0) - (swiper.slidesGrid[0] || 0); - if (activeIndex >= slidesBefore) { + if (isLoop && activeIndex >= slidesBefore) { from -= slidesBefore; - offset += swiper.slidesGrid[0]; + if (!centeredSlides) offset += swiper.slidesGrid[0]; + } else if (isLoop && activeIndex < slidesBefore) { + from = -slidesBefore; + if (centeredSlides) offset += swiper.slidesGrid[0]; } Object.assign(swiper.virtual, { diff --git a/src/react/virtual.js b/src/react/virtual.js index 3cd49bb61..b00247b76 100644 --- a/src/react/virtual.js +++ b/src/react/virtual.js @@ -2,6 +2,18 @@ import React from 'react'; function renderVirtual(swiper, slides, virtualData) { if (!virtualData) return null; + + const getSlideIndex = (index) => { + let slideIndex = index; + if (index < 0) { + slideIndex = slides.length + index; + } else if (slideIndex >= slides.length) { + // eslint-disable-next-line + slideIndex = slideIndex - slides.length; + } + return slideIndex; + }; + const style = swiper.isHorizontal() ? { [swiper.rtlTranslate ? 'right' : 'left']: `${virtualData.offset}px`, @@ -9,14 +21,21 @@ function renderVirtual(swiper, slides, virtualData) { : { top: `${virtualData.offset}px`, }; - return slides - .filter((child, index) => index >= virtualData.from && index <= virtualData.to) - .map((child) => { - return React.cloneElement(child, { - swiper, - style, - }); + const { from, to } = virtualData; + const loopFrom = swiper.params.loop ? -slides.length : 0; + const loopTo = swiper.params.loop ? slides.length * 2 : slides.length; + const slidesToRender = []; + for (let i = loopFrom; i < loopTo; i += 1) { + if (i >= from && i <= to) { + slidesToRender.push(slides[getSlideIndex(i)]); + } + } + return slidesToRender.map((child) => { + return React.cloneElement(child, { + swiper, + style, }); + }); } export { renderVirtual }; diff --git a/src/vue/virtual.js b/src/vue/virtual.js index 1a7a9e714..a0d4fd1e5 100644 --- a/src/vue/virtual.js +++ b/src/vue/virtual.js @@ -2,6 +2,16 @@ import { h } from 'vue'; function renderVirtual(swiperRef, slides, virtualData) { if (!virtualData) return null; + const getSlideIndex = (index) => { + let slideIndex = index; + if (index < 0) { + slideIndex = slides.length + index; + } else if (slideIndex >= slides.length) { + // eslint-disable-next-line + slideIndex = slideIndex - slides.length; + } + return slideIndex; + }; const style = swiperRef.value.isHorizontal() ? { [swiperRef.value.rtlTranslate ? 'right' : 'left']: `${virtualData.offset}px`, @@ -9,15 +19,22 @@ function renderVirtual(swiperRef, slides, virtualData) { : { top: `${virtualData.offset}px`, }; - return slides - .filter((slide, index) => index >= virtualData.from && index <= virtualData.to) - .map((slide) => { - if (!slide.props) slide.props = {}; - if (!slide.props.style) slide.props.style = {}; - slide.props.swiperRef = swiperRef; - slide.props.style = style; - return h(slide.type, { ...slide.props }, slide.children); - }); + const { from, to } = virtualData; + const loopFrom = swiperRef.value.params.loop ? -slides.length : 0; + const loopTo = swiperRef.value.params.loop ? slides.length * 2 : slides.length; + const slidesToRender = []; + for (let i = loopFrom; i < loopTo; i += 1) { + if (i >= from && i <= to) { + slidesToRender.push(slides[getSlideIndex(i)]); + } + } + return slidesToRender.map((slide) => { + if (!slide.props) slide.props = {}; + if (!slide.props.style) slide.props.style = {}; + slide.props.swiperRef = swiperRef; + slide.props.style = style; + return h(slide.type, { ...slide.props }, slide.children); + }); } export { renderVirtual };