Embla-carousel: Option to swipe only to `n` number of slides (1 by default)

Created on 18 Mar 2021  路  14Comments  路  Source: davidcetinkaya/embla-carousel

First, I would like to say thank you, @davidcetinkaya , for this wonderful implementation of carousel!

I really liked it!

Feature request is related to

  • [x] The Embla Carousel core (all versions)

I am trying to use Embla for navigation through slides, and it works great from the box! But I could not find option or example, how to constraint number of slides, when swipe (or pointer move) is faster then regular one. When swipe (or pointer move) is fast, Embla spins through undetermined number of slides, depending on speed of move. In my case, I just need it to go to only next slide.

I am sure, there are some ways to implement this feature, using existing Embla API + some custom code. But, may be, it would be much more effective to add such an option to the Embla library ?

Thanks, David!

feature request

All 14 comments

Hi Sergey (@bannndi),

Thank you for your feature request. I think this is more or less related to/duplicate of issue #38. I'm intending to re-open issue #38 because it seems like it's getting traction and has been requested a couple of times at this point.

Would an implementation of issue #38 fulfill your needs?

Kindly,
David

Hi @davidcetinkaya !

I hope so. I will try to explain how I am trying to use Embla. I fetch from REST a number of items (future slides), say 5.

Then user must go through all slides one-by-one, no skipping, and on each slide make a decision action (like or skip).

It perfectly works with buttons in your examples, and may be, my use case can be solved by passing custom handler (instead of swiping) with call of emblaApi.scrollPrev() or emblaApi.scrollNext() ?

Hi @bannndi,

Thanks for explaining more in detail. So you want to prevent the user from progressing more than one slide at a time.

and may be, my use case can be solved by passing custom handler (instead of swiping)

I'm not sure what the suggestion here is? Is it acceptable for your use case to disable dragging or am I misunderstanding you?

Kindly,
David

I'm not sure what the suggestion here is? Is it acceptable for your use case to disable dragging or am I misunderstanding you?

My suggestion is may there is a way to call scrollPrev/scrollNext when making drag/swipe (current behavior) ? So that click on button next/prev and drag/swipe were identical.

Hi again Sergey (@bannndi),

Thanks for clarifying. I see what you mean now. I鈥檒l see if there鈥檚 an easy way to achieve this that can help you achieve what you want, without having to wait for a new feature to be implemented.

As you can see here, there鈥檚 quite a lot to consider when creating solid drag handlers, everything from locking page scroll while dragging to not reacting to the right mouse button and so on.

I鈥檒l let you know when when I have tried solving this. Are you using the vanilla JavaScript or React version of Embla?

Best,
David

Hi David !

Thank you!

I am using React version.

Hi Sergey (@bannndi),

Is this the behavior you're looking for?

https://user-images.githubusercontent.com/11529148/112989186-ed5d3d80-9164-11eb-8b7f-f0b9a10ed613.mp4

Kindly,
David

Hi David {@davidcetinkaya)

In your video, when dragging for next slide is happening, sometimes I can see part of the after next slide, which is not what I am looking for.

I think, wanted behavior is when carousel always has only 2 slides at one moment. So, when dragging/swiping with any speed for next slide is happening, next slide always will be the last one. But there of cause must be some state management, so carousel always could accept updated pair of slides.

But there of cause could be some pitfalls, I am not aware of right now.

Hello Sergey (@bannndi),

Thanks for clarifying. In that case, you can pass 2 slides at a time to the carousel component, and reset the carousel when changing slides:

  • Pass the 2 initial slides to the carousel component.
  • As soon as the user is done answering whatever questions inside these 2 slides, pass the next 2 slides.
  • Reset Embla so it picks up the new slides with a useEffect like demonstrated below.
import React, { useEffect } from 'react'
import { useEmblaCarousel } from 'embla-carousel/react'

export const EmblaCarousel = ({ slides }) => {
  const [emblaRef, emblaApi] = useEmblaCarousel()

  useEffect(() => {
    if (emblaApi) {
      emblaApi.reInit() // This will reset Embla as soon as the slides prop changes
    }
  }, [
  emblaApi,
  slides // Passing slides here will trigger this as soon as the slides prop changes
])

  return (
    <div className="embla" ref={emblaRef}>
      <div className="embla__container">
        {slides.map((slide, index) => (
          <div className="embla__slide" key={index}>{slide}</div>
        ))}
      </div>
    </div>
  )
}

This approach should be what you're looking for. But I don't think this is a feature that should be added to the Embla Carousel core. The reason for this is that the embla.reInit() method already offers a way to reset the carousel. I hope this makes sense.

Let me know if it helps you on the right track.

Best,
David

Hello David (@davidcetinkaya),

I think that should work. Closing.

Thank you!

Hi David! (@davidcetinkaya)

Tried many variants based on suggested gist of yours, didn't work for me.. I could make work passing 2 or 3 slides to Carousel (slice of items array, based on index in store), and this worked good. But, as I could judge, reInit method worked not always consistent and predictive in my case. I tried it with and without options.startIndex = 1. Tried to reInit in the start or in the end of on.settle handler. One part of my code was useEffect, which created one on.settle (with or without on.select) listener, in which I updated index of currently used slide of the whole array of items. Based on this index, I sliced this array to get prev?, curr, next? items. This was the whole point, - to restrict carousel with a minimum of items, and re-slice array every time, when on.settle (or on.select) fires, and updates store.index. So that swipe/drag with any speed always changed to only the prev or next slide.

I thought I could use methods emblaApi.previousScrollSnap()/emblaApi.selectedScrollSnap() to figure out in which direction swipe/drag was made, to calculate next store.index. Intent was to store their results to local state, and then use it in on.settle.
In particular, methods emblaApi.previousScrollSnap()/emblaApi.selectedScrollSnap(), I called in on.settle, they returned strange results.

Also, I tried to use on.select, with no luck - handler was called unpredictibly, not when some slide was selected, but sometimes when there was no slides after the last one, and sometimes correctly.

Of course, I could miss something.

Will try some another strategy for now, and if it will work, I'll post result.

David (@davidcetinkaya ),

May be there is a way to disable or override core swipe/drag events ?

If so, I could try to use emblaApi scrollPrev()/scrollNext() methods in my custom events, so that current core behavior of swipes/dragging was completely replaced by result of calling these 2 methods.

Hi Sergey (@bannndi),

May be there is a way to disable or override core swipe/drag events?

You can disable drag events by setting the draggable option to false:

 const [emblaRef, emblaApi] = useEmblaCarousel({ draggable: false })

Embla won't add any mouse or touch events when drag events are disabled. I hope this helps.

Best,
David

Hi David! (@davidcetinkaya)

Thanks, will try.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

th-km picture th-km  路  5Comments

iamchanii picture iamchanii  路  5Comments

davidspiess picture davidspiess  路  4Comments

RectoVersoDev picture RectoVersoDev  路  3Comments

nikrowell picture nikrowell  路  6Comments