swiper: Infinite loop with slidesPerView: auto, loopFix() issues

This is a (multiple allowed):

  • bug

  • enhancement

  • feature-discussion (RFC)

  • Swiper Version: 4.4.6 (also tested 4.4.x versions and the problem persists)

  • Platform/Target and Browser Versions: all platforms and browsers I’ve been testing: Windows/Android and Chrome/Opera/Firefox (the most actual versions)

  • Live Link or JSFiddle/Codepen or website with issue: -“big case” codepen -“small/simplified case” codepen

What I did

I use swiper with the following settings (the “big case”):

autoplay: {
    delay: 0
},
centeredSlides: true,
coverflowEffect: {
    depth: 100,
    modifier: 1,
    rotate: 5,
    stretch: 0
},
effect: 'coverflow',
freeMode: true,
freeModeMomentumBounce: false,
freeModeMomentumRatio: .1,
freeModeMomentumVelocityRatio: .8,
freeModeSticky: true,
grabCursor: true,
loop: true,
loopAdditionalSlides: slidesNum, // slidesNum contains the initial slides number
loopedSlides: slidesNum,
slidesPerView: 'auto',
speed: 20000,
breakpoints: {
    360: {
        slidesPerView: 1
    }
}

+some css styles (I did put them in codepens). After some experiments I’ve managed to recreate the issue only with these settings (the “small case”):

centeredSlides: true,
loop: true,
loopedSlides: slidesNum,
slidesPerView: 'auto',

I don’t know if solving problem for the “small case” will be sufficient for solving the “big case”, but for sure it’s a good way to start.

Behavior

If I slide to the left, everything seems fine (unless the screen is very big or there are too few slides, i.e. 2), but when I swipe to the right, duplicated elements won’t show up on the right side of the slider, which causes a huge empty space after the slides. It’s even worse if I swipe very fast (it may cause the whole container to be empty for a moment). Occasionally the slider updates and the duplicates “jump in” to the right place, but it’s too soon, so it looks bad and may be confusing for the user.

I could find some similar issues here on GitHub, but none of the presented fixes worked for me. For instance I’ve been playing with loopedSlides and loopAdditionalSlides options (i.e. I’ve been trying to put there some huge values like slidesNum, 2 * slidesNum, 10 * slidesNum, 50 or slidesNum - 1, where slidesNum contains the initial slides number). It didn’t help.

Moreover I’ve got impression that changing loopAdditionalSlides value option is doing literally nothing. In the docs it’s said that it’s the “number of slides that will be cloned after creating of loop”, but as far as I could see, number of cloned slides is always equal to the number of original slides - please correct me if I’m wrong. So technically, how does this option work?

The other thing I’ve tried was translating the slider manually when the very last slide appears on the screen, but then I had problems with smooth movement/free mode momentum (see “Big case”).

Attemption to fix this

I’ve been trying to modify swiper.js script in order to make it work. First of all I’ve noticed that the method loopFix is responsible for “fixing” the loop (to be more specific it handles the translation of slider if some conditions are met). These conditions look like this:

if (activeIndex < loopedSlides) {
    // Fix For Negative Oversliding
} else if ((params.slidesPerView === 'auto' && activeIndex >= loopedSlides * 2) || (activeIndex >= slides.length - loopedSlides)) {
    // Fix For Positive Oversliding
}

First of all I replaced the 2 multiplier in the second condition with 1 (to tell the truth I couldn’t understand why the 2 is there, as activeIndex >= loopedSlides is the opposite condition to the activeIndex < loopedSlides). Can some good soul explain me why there is 2? Thanks!

Secondly I used the console.log and observed that the first condition for negative oversliding was sometimes fulfilled even though I was swiping to the positive direction, so I decided to fix this by imposing some additional condition here related to the sliding direction. So I got something like this:

var previousIndex = swiper.previousIndex;
var dir = (activeIndex > previousIndex) ? 'right' : 'left';

if (dir === 'left' && activeIndex < loopedSlides) {
    // Fix For Negative Oversliding
} else if (dir === 'right' && ((params.slidesPerView === 'auto' && activeIndex >= loopedSlides) || (activeIndex >= slides.length - loopedSlides))) {
    // Fix For Positive Oversliding
}

In general this fix may not be sufficient for some RTL settings, but it helped a little bit in my case. Here is an example: “attemption to fix” codepen.

As you can see it may seem to work now, but there are still some problems:

  • variable dir has got wrong value after first swipe in the opposite direction (for example if we are swiping left and then we swipe right for once, the dir variable still evaluates to ‘left’ not to ‘right’; it’ll have the correct value ‘right’ only if we swipe right for the second time and further). So the problem still may occur in these kinds of situations.
  • fast swiping can still cause swiper container to be partially empty; in the worst scenario I could limit the maximum slider speed, but it’s not very user friendly
  • (hard to reproduce) I’m not sure if it concerns the small case, but for the big case in Opera and Chrome browsers some random swiping may cause an infinite script loop (more precisely the method swiper.loopFix(); in line 3038 of the swiper.js (ver. 4.4.6) starts to run infinitely, causing the whole slider to freeze).

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 16
  • Comments: 20 (1 by maintainers)

Most upvoted comments

Have the same issue:

const swiperInstance = new Swiper(containerRef.current, {
            direction: 'horizontal',
            slidesPerView: 'auto',
            loopedSlides: 3,
            loop: true
});

With following config Swiper correctly work to the left left, but not to the right.


UPD:

I think the issue reproducible when width of all the slides (not cloned) less than width of “viewport”.

Ok, I think I fixed the major problems in my case (but it may not be sufficient in general).

  1. I modified first condition like this: if (dir === 'left' && activeIndex < loopedSlides && !(activeIndex === previousIndex - 1 && previousIndex > loopedSlides / 2)) {

  2. I added an argument “direction” to the loop fix method, which can be “left”, “right” or undefined: function loopFix (direction) {

  3. I modified the conditions once again, taking the argument into account:

if ((typeof direction === 'undefined' || (typeof direction !== 'undefined' && direction === 'left')) && activeIndex < loopedSlides && !(activeIndex === previousIndex - 1 && previousIndex > loopedSlides / 2)) {
    // Fix For Negative Oversliding
} else if ((typeof direction === 'undefined' || (typeof direction !== 'undefined' && direction === 'right')) && (params.slidesPerView === 'auto' && activeIndex >= loopedSlides) || (activeIndex >= slides.length - loopedSlides)) {
    // Fix For Positive Oversliding
}
  1. I passed an argument to this method in following places:
  • changed swiper.loopFix(); to swiper.loopFix('right'); in slideNext method
  • changed swiper.loopFix(); to swiper.loopFix('left'); in slidePrev method
  • changed swiper.loopFix(); to swiper.loopFix('right'); in the Autoplay variable initialization (but in general the argument here should be dependant on initial slider direction)
  • and the most complicated one; in method onTouchMove I have added var diff = swiper.isHorizontal() ? diffX : diffY; right before the if (!data.isMoved) { condition and then changed the next swiper.loopFix(); to swiper.loopFix(diff > 0 ? 'left' : 'right');

The last point is done like this, because I couldn’t get the correct swipe direction in the loopFix method (as stated in the previous post).

Additionally I commented out this fragment:

if (needsLoopFix) {
    swiper.once('transitionEnd', function () {
        swiper.loopFix();
    });
}

as sometimes it was causing the slider to freeze (the loopFix method was called infinitely, I don’t know why).

codepen: https://codepen.io/stasiak/pen/pGvGaq

I’ve created the following workaround to this problem. It follows the same principle of duplicating the slides but does it with JavaScript before the initialization instead of server-side.

on: {
    beforeInit: function(swiper){
        if(swiper.$el[0].querySelectorAll('.swiper-slide').length == 0) return;

        let getTotalWidth = function(swiper){
            let width = 0;
            swiper.$el[0].querySelectorAll('.swiper-slide').forEach(function(element){
                width += element.offsetWidth;
            });
            return width;
        }

        while(getTotalWidth(swiper) < window.innerWidth){
            swiper.$el[0].querySelectorAll('.swiper-slide').forEach(function(element){
                let clone = element.cloneNode(true);
                swiper.$el[0].querySelectorAll('.swiper-wrapper')[0].appendChild(clone);
            });
        }                            
    }
}

Had the same issue. Fixed mine by duplicating the array of slides with PHP array_merge(), so that there were more slides in total than slides that was showing in the view. Seemed to work for me!

I would like to add that it has definitely something to do with the combined width of the elements and the viewport.

Also, setting centeredSlides to true, everything works but that’s not what i want in my case.

centeredSlidesBounds = true and centeredSlides = true works for me and fixes the problem when I swipe right and my slides exceed slides > 3.

Been facing this issue as well, none of the workarounds above seem to work…

Weirdly enough, like someone said above, it only happens when clicking the slide, which calls the slideTo function. When I swipe it works just fine.

In my case I’m using react with a media query hook to change the max-width of the slide. Like so:

carousel