davidjerleke / embla-carousel

A lightweight carousel library with fluid motion and great swipe precision.
https://www.embla-carousel.com
MIT License
5.91k stars 178 forks source link

Infinite Scroll #89

Closed Charuru closed 3 years ago

Charuru commented 4 years ago

Is it possible to add infinite scroll to the carousel?

davidjerleke commented 4 years ago

Hi Charles @Charuru,

Do you have an example (like link to a working example) of infinite scroll so I can better understand your expectations? I would also like to know if you're interested in a vanillaJS or React example?

Kindly David

Charuru commented 4 years ago

Hi @davidjerleke thanks for replying. Would that be an example of a non-carousel infinite scroll or a carousel in my usecase which currently has limited content / not infinite scroll. If the latter do you mind if I DM you with the link? Thanks.

I'm interested in a vanilla example.

davidjerleke commented 4 years ago

Hello again Charles @Charuru, Sorry, I got confused by your answer. You're still interested in an infinite scroll solution right 😊?

Charuru commented 4 years ago

Yes I am still interested. I was asking what type of example you were looking for.

I don't have an infinite scroll carousel example. But I can show you

  1. Infinite scroll non-carousel
  2. My current embla implementation which has non-infinite scroll, but should help you see why infinite scroll would be useful.
davidjerleke commented 4 years ago

@Charuru I see, thanks for clarifying.

So another thing I would like to know is if you're expecting that the carousel should be able to load slides dynamically while still in motion/scrolling?

Charuru commented 4 years ago

I think so? But if that's very hard to do, so long as further slides are loaded and the overall experience doesn't look strange I'm okay with it loading the slides whenever. Thanks again.

davidjerleke commented 4 years ago

Thank you for the additional information. I'll see if this is achievable with the current state of Embla Carousel and let you know when I've done so.

Cheers David

davidjerleke commented 4 years ago

Hello Charles (@Charuru),

I've been testing this a bit and came to the conclusion that it isn't possible to load new slides while the carousel still is in motion. The current state of Embla stops any animation as soon as you re-initialize the carousel in order to pick up added slides/removed slides.

Do you want me to treat this issue as a feature request from now on (meaning investigating how to implement this to the core)?

Best David

Charuru commented 4 years ago

Yes please, it makes sense as a feature request, thank you.

davidjerleke commented 4 years ago

Thank you Charles (@Charuru),

Seems like more people want to see this implemented (for example @etozhkirill and @khannea mentioned this in issue #103). I'm going to look into implementing this when possible. But in order to do that I need a clear picture of how this is supposed to work.

I think the feature of adding/removing slides should be completely separate from the embla.reInit() method. The intended use of the reInit method is and has since its introduction been a hard reset (meaning that any ongoing animation will stop and the carousel will be initialized from scratch). In addition, the reInit method allows for changing the carousel options, which would make adding slides while animating complex and probably quite messy.

So I'm thinking a new method that only checks for slide count changes. Something along these lines:

// This method name is just a draft, feel free to suggest a better name

embla.checkForSlideChanges()

How it should work:

@khannea, you mentioned the following in issue #103:

If I ReInit() the carousel after slides added, the prevent click if isMove is no more working.

I'm not quite sure what you mean by this. isMove is not an Embla method. Would you mind explaining this further?

Kindly, David

khannea commented 4 years ago

As far as I am concerned, I have a homepage with many caroussels and each caroussel can have "a lots of" images.

I did the lazy loading like you, but the problem is that caroussels start lagging when there is too many things in it (like 5 caroussels with 300 items) even if the image is not loaded. So what I would love is to load more data only when an user arrive near or at the end of the set of data in the caroussel.

What could make me happier, ordered by satifaction, would be:

  1. Loading more data dynamically with no motion stop when I arrive near the end of carrousels (but I understand that it is complicated)

  2. Stop at the end of caroussel with a "loading panel", then when it s loaded user can swipe more (That would be also perfect. The stopping caroussel is not a big issue. But caroussel have to stay where it was, and not start at the beginning)

  3. Have a "Load more" panel at the end of caroussel, and if you click on it, more data is loaded and user can continue to swipe the caroussel.

Charuru commented 4 years ago

My usecase is similar to khannea's. 1 and 2 would be acceptable to me, but 3 would not be.

My usecase doesn't involve removing slides. Not reusing reInit makes sense to me.

davidjerleke commented 4 years ago

Thanks for your additional thoughts @Charuru and @khannea.

I'm aiming for an implementation where appending slides dynamically will be possible while the carousel is scrolling/animating. But I also want to handle the case when you remove slides, so if you don't have an idea how this should work, I’ll have to come up with something. I'm going to look into this further and will give you an update when I have something.

Kindly, David

davidjerleke commented 3 years ago

@Charuru would you mind sharing a link to an infinite scroll example you think is acceptable? I need this in order to understand your expectations better.

Thanks in advance.

khannea commented 3 years ago

I can't find a good example of infinite scroll carroussel... I imagine it a bit like this infinite scroll site https://infinite-scroll.com/demo/masonry/ , but with the carroussel. I hope it help, but I am not sure of that...

Charuru commented 3 years ago

Yeah I don't have an example either of carousel + infinite scroll, only have them separately.

callumbooth commented 3 years ago

Will this infinite scroll work like this example? https://react-slick.neostack.com/docs/example/dynamic-slides where the extra slides can be added either on a button click like on this example or via a event like a network request resolving. I think so long as you can add slides and the scroll position persists it should cover most use cases where dynamically adding slides is required.

davidjerleke commented 3 years ago

Thank you for the example link Romain (@khannea), that helps 👍.

Hello Callum (@callumbooth) and welcome to the conversation. I checked out the react-slick link you shared, thanks! From what I can see, the Slick carousel stops instantly if it's scrolling when changing the slides dynamically, while preserving the current slide index if possible. This is already achievable with Embla Carousel. You can do this when the slide count changes (whether its triggered by a button click or network request resolving) by calling the following method:

embla.reInit() // re-initializes the carousel

This method will stop the carousel if it's in motion, and pick up any changes whether you've removed slides or added new slides. However, if I'm not mistaken, this request is about adding a method that allows for a fluid infinite scroll feature. So any slides added should not stop the carousel while scrolling.

Let me know if this explanation helps.

Best, David

davidjerleke commented 3 years ago

Hi again Callum (@callumbooth),

Did this help your use case?

Thanks in advance, David

callumbooth commented 3 years ago

It did thanks.

arihantverma commented 3 years ago

@davidjerleke is this still being worked upon? If yes, I'd like to help in any way possible. For starters, do you still need an example for carousel infinite scroll where motion doesn't stop when slides are added? If yes, I could rummage through and try to find something.

Last point of discussion in this thread was this https://github.com/davidcetinkaya/embla-carousel/issues/89#issuecomment-692877410. Let me know how I can be of assistance and contribute in any way possible - research, brainstorm, discuss, or implement.

This is a use case for us at Goibibo ( India's leading Online Travel Agency ) as well, where we want to dynamically update ( load ) carousel list items in our react page - as user approaches the end slide(s), and still give the user the freedom to keep scrolling.

I'd like to to help think and implement removal of slides as well.

Really excited for this and I have never contributed to open source or collaborated before 😅 Arihant

davidjerleke commented 3 years ago

Hello Arihant (@arihantverma), Thank you for your question. I like the spirit and positive energy. Thanks 🙂.

Yes I'm still working on this. I think the example mentioned in this comment will suffice. So no additional examples are needed unless you think I should consider another example that you have in mind?

Although I'm making good progress on this request, I can't give an ETA for it yet. Public feature requests are currently progressing quite slow right now due to lack of time.

I hope you understand.

Best, David

arihantverma commented 3 years ago

Hey @davidjerleke

Thank you for the clarifications. I think I was not as clear as I wanted in my burst of energy 😅 .

I'd like to help you ship it, work on it with you, if you are open to it and if it's feasible for you.

Let me know what's your preference/want/take on it. It'd be cool either ways :)

Best Arihant

arihantverma commented 3 years ago

Hi @davidjerleke. Been a while :) so I thought I'd comment again to ask the above 👆🏼

davidjerleke commented 3 years ago

Hello @arihantverma,

No harm in asking! I'm close to finishing my draft for this feature so I don't know if it makes sense if we start collaborating at this stage. But I could setup a CodeSandbox with a temporary solution, if that would help you? Note that the temporary solution won't be as fluid as the feature I'm working on, but maybe it will be good enough until it's finished?

Best, David

arihantverma commented 3 years ago

Yes @davidjerleke, it'll definitely help, thank you! If you could set up a codesandbox that's be awesome 🎊

davidjerleke commented 3 years ago

Hello Arihant (@arihantverma),

Update: Release 4.1.2 enables me to finish the CodeSandbox example I mentioned here. I have a little left to do and I'm hoping to finish it soon.

Stay tuned. David

davidjerleke commented 3 years ago

Hello all,

I have an update on this issue for React users (@arihantverma, @khannea, @callumbooth, @etozhkirill). The CodeSandbox demonstrating how to achieve infinite scroll is finished:

I have done some testing on my own but I would appreciate if you guys could take a moment to test it. I hope that it will work as expected for you.

For VanillaJs users: I'm working on a vanilla JS example right now. Stay tuned.

Best, David

callumbooth commented 3 years ago

Hi @davidjerleke,

I've taken a look and it seems to work well, thank you. I noticed that your using useState for the isPointerDown state but since we don't need to re render the component when this value changes you can probably use useRef instead.

davidjerleke commented 3 years ago

Hello Charles (@Charuru),

Here are the links to the Vanilla implementation of infinite scroll:

Please try it out and let me know if it helps.

Best, David

davidjerleke commented 3 years ago

Thanks a lot for testing it Callum (@callumbooth). I'm glad to hear that it's working as intended.

I noticed that your using useState for the isPointerDown state but since we don't need to re render the component when this value changes you can probably use useRef instead.

Maybe I'm not understanding you correctly, but the pointerDown state is intentional. Because I'm delaying the reload of the Embla engine until the user releases the pointer (if the pointer is down when new slides are injected). This is because I don't want to interrupt the user when dragging. And when the pointer is released I want to trigger a state change, that reloads the Embla engine for it to pick up the new slides.

I don't know if my explanation makes sense?

Kindly, David

davidjerleke commented 3 years ago

Hello again all (@Charuru, @callumbooth, @arihantverma, @khannea, @etozhkirill).

I've decided to not build this into the Embla core. Instead, I've created CodeSandboxes that demonstrate how to achieve infinite scroll by extending Embla. I've added these examples to the documentation page.

The reason for my decision is that different implementations are needed for VanillaJS and ReactJS (non-reactive vs reactive). And who knows. Maybe in the future, a VueJS implementation will be added to Embla. Covering all these cases would significantly increase the bundle size and burden users that don't need this feature.

I hope this makes sense. I'm going to give everyone a chance to test this out a little bit and if I don't get additional feedback, I will close this issue in a couple of days.

Thanks! David

Charuru commented 3 years ago

Great job, thanks!

CristianCucunuba commented 3 years ago

Hi @davidjerleke,

I tried adding new slides to the carousel and call embla.reInit() to reset the carousel and be able to scroll to the new slides, but if you try to scroll to the new slide is not posible, here is the reproduction with using the basic example. Do you know if I'm missing something?

Thanks

davidjerleke commented 3 years ago

Hi Cristian (@CristianCucunuba),

Thank you for your question. The issue you're experiencing is because of the asynchronous nature of react setState. React will update the state asynchronously because of performance reasons:

const addSlide = () => {
  setSlides([...slides, 1]); // There's no guarantee that setSlides will finish here
  embla.reInit(); // Before you call embla.reInit() on the next line
};

What you need to do is to create a useEffect that will trigger as soon as the slides prop changes like so:

const addSlide = () => {
  setSlides([...slides, 1]);
};

useEffect(() => {
  if (embla && embla.slideNodes().length !== slides.length) { // Run this when slide count has changed
    embla.reInit(); // Make Embla pick up the changed slide count
    onSelect(); // Update button states
  }
}, [
  embla,
  onSelect,
  slides /* Add slides prop as a dependency that will trigger this function when it changes */
]);

If you're looking for a fluid infinite scroll example that doesn't reset the carousel you can take a look at the Infinite Scroll example on the documentation page.

Let me know if it helps.

Best, David

CristianCucunuba commented 3 years ago

yes that was the error, thank you so much @davidjerleke for the quick help and for this awesome library

davidjerleke commented 3 years ago

I'm glad it helped @CristianCucunuba. Thanks a bunch for supporting Embla ❤️!

felixmpaulus commented 6 months ago

Hey @davidjerleke, thanks for all your input here. I am very interested in a infinite scroll solution as well and noticed the slider is flickering when i update the state while the slider is in motion.

The links you posted (React demo and sandbox) both do not work for me. Could you take a look? I am talking about the links in this comment: https://github.com/davidjerleke/embla-carousel/issues/89#issuecomment-745270679 The demo results in a 404 page and the sandbox never finishes loading.

Again, thanks for your work! :)

davidjerleke commented 6 months ago

@felixmpaulus have you seen this example in the docs?

felixmpaulus commented 6 months ago

@felixmpaulus have you seen this example in the docs?

Somehow missed that. Thanks 🙏🏼

felixmpaulus commented 6 months ago

Got it to work. Thanks again! 😊