Embla-carousel: `loop` doesn't work when displaying three slides out of four total slides

Created on 2 Mar 2021  路  7Comments  路  Source: davidcetinkaya/embla-carousel

Hi,

When the total number of slides is set to 4, slidesToShow: 3, and slidesToScroll: 1, the loop option stops working. This was mentioned in the docs, which is probably what's happening

loop
... Automatically falls back to false if slide content isn't enough to loop.

This can be reproduced in the sandbox:
https://codesandbox.io/s/embla-carousel-loop-react-forked-7200i?file=/src/js/index.js

Two questions:

  1. What's the reasoning for this restriction? There's definitely enough slide content to loop. I was expecting to see [1,2,3] on initial render, [2,3,4] on next click, and [3,4,1] on next click.
  2. Is there a workaround for this?

Thanks,
Patrick

not a bug resolved

Most helpful comment

Hello Patrick (@patrickhuang94),

Thank you for your question and the CodeSandbox. The documentation is correct, Embla Carousel will check if there is enough slide content to make the loop effect work without any flickering (white space showing).

  1. What's the reasoning for this restriction? There's definitely enough slide content to loop. I was expecting to see [1,2,3] on initial render, [2,3,4] on next click, and [3,4,1] on next click.

Now, assuming you have 4 slides, you would think that setting the CSS like this:

.embla__slide {
  min-width: 33.33333%;
}

/* ...or a nicer way to put it */

.embla__slide {
  min-width: calc(100% / 3);
}

...would result in enough content to make the loop effect work. But this isn't the case. Because the viewport is 100%. And the slide content for 3 slides (we subtract 1 slide because it's needed to flip around for the loop effect to work) will be 33.33333 * 3 = 99.99999%. Additionally, when I log the slide sizes calculated from the boundingClientRect, the browser gives 33.331691216316074, which isn't even 33.33333%.

I hope this explanation makes sense.

  1. Is there a workaround for this?

There's no workaround for this other than writing your own fallback logic. For example, you can duplicate the slides yourself.

Best,
David

All 7 comments

Hello Patrick (@patrickhuang94),

Thank you for your question and the CodeSandbox. The documentation is correct, Embla Carousel will check if there is enough slide content to make the loop effect work without any flickering (white space showing).

  1. What's the reasoning for this restriction? There's definitely enough slide content to loop. I was expecting to see [1,2,3] on initial render, [2,3,4] on next click, and [3,4,1] on next click.

Now, assuming you have 4 slides, you would think that setting the CSS like this:

.embla__slide {
  min-width: 33.33333%;
}

/* ...or a nicer way to put it */

.embla__slide {
  min-width: calc(100% / 3);
}

...would result in enough content to make the loop effect work. But this isn't the case. Because the viewport is 100%. And the slide content for 3 slides (we subtract 1 slide because it's needed to flip around for the loop effect to work) will be 33.33333 * 3 = 99.99999%. Additionally, when I log the slide sizes calculated from the boundingClientRect, the browser gives 33.331691216316074, which isn't even 33.33333%.

I hope this explanation makes sense.

  1. Is there a workaround for this?

There's no workaround for this other than writing your own fallback logic. For example, you can duplicate the slides yourself.

Best,
David

Great, thanks for the quick response and explanation! Cheers.

Hi again Patrick (@patrickhuang94),

If you want to perform a check to know if slide content is enough to loop or not, there's an unofficial way of doing this. Just in case you want to duplicate slides based on the boolean it returns.

const notEnoughToLoop = !embla.dangerouslyGetEngine().slideLooper.canLoop()

if (notEnoughToLoop) {
  // Duplicate your slides
  embla.reInit(); // Re-init embla to pick up all the slides including the duplicated ones
}

I hope it helps.

Best,
David

Another solution that I just thought of, instead of duplicating slides:

Currently my code looks like this

<div className="embla__container">
    {children.map((slide, index) => (
        <div
            key={index}
            className="embla__slide"
            style={{
                minWidth: `${100 / slidesToShow}%`,
            }}
        >
            <div className="embla__slide__inner">{slide}</div>
        </div>
    ))}
</div>

which, whenever slidesToShow = 3, will resolve to 33.33 * 3 = 99.99% (and thus never hit the required 100%). I can probably add some custom logic that rounds the min width to 33.34% whenever we receive a slidesToShow input of 3.

Something like

const slideWidth = slidesToShow === 3 ? '33.34%' : `${100 / slidesToShow}%`
...
<div style={{ minWidth: slideWidth }}>...</div>

Hi Patrick (@patrickhuang94),

Of course! You can solve this in different ways. But I guess the bottom line is that the library won鈥檛 do it for you, if you鈥檙e looking to make your carousel loop even if the slide content isn鈥檛 enough 馃檪. As I mentioned you can use the unofficial hint I demonstrated in this comment.

Did you solve it for your use case?

Kindly,
David

@patrickhuang94 I'm closing this issue if you don鈥檛 have any objections?

@patrickhuang94 I鈥檓 closing this as resolved. You鈥檙e welcome to open this issue again if you don鈥檛 agree.

Best,
David

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ej-mitchell picture ej-mitchell  路  5Comments

davemullenjnr picture davemullenjnr  路  5Comments

patrickhuang94 picture patrickhuang94  路  3Comments

RectoVersoDev picture RectoVersoDev  路  3Comments

maiconrs95 picture maiconrs95  路  5Comments