davidjerleke / embla-carousel

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

[Feat]: Add a fade plugin #39

Closed dmorda closed 4 months ago

dmorda commented 4 years ago

Love the carousel and was able to get it working right away. The one thing that I need that it doesn't provide is a way to have a scroll option for single/multiple slides and a fade option to handle next/previous transition when it's just showing a single slide at a time.

Is that something that has been requested before?

davidjerleke commented 4 years ago

Hi Damon (@dmorda), Merry Christmas, and thank you for opening this issue ๐ŸŽ….

About:

A way to have a scroll option for single/multiple slides

Would you mind explaining what you mean a bit more in detail or provide an example? Because I need more information to answer this question, but I'm going to take a guess and ask if you've checked out the slidesToScroll option? Here's a CodeSandbox that demonstrates how you can achieve this.

The answer to your question about the fade option is no, it has not been requested yet. I'm not sure what you mean by fade option because it can be done in many different ways so would you mind explaining a bit further? I'm guessing here too but maybe you can achieve this by hooking onto the classname toggling for selected slides? Please have a look at the documentation here.

Thank you in advance!

Best, David

dmorda commented 4 years ago

Thanks for the quick response and a Merry Christmas to you as well. What I'm trying to achieve is a Fade effect , as an example view this page and search for the word "Fade". That will provide an example of what I'm trying to accomplish.

Let me know if that helps!

davidjerleke commented 4 years ago

Hi again Damon (@dmorda), Thank you for the clarification, it helps ๐Ÿ‘.

The vision for Embla is carousels that move in a fluid and natural manner. The assumption here is that the carousel should be scrollable, and that doesn't play well with fade sliders that don't feel natural when dragging/swiping. This is why a fade option is not a part of the Embla vision and won't be incorporated into the Embla core.

But with that said, I've setup a CodeSandbox example for you, where I've created the fade effect just by tweaking some CSS.

I hope that you'll find it useful!

Best, David

dmorda commented 4 years ago

That small tweak now lets me re-use Embla for both the fade version of a carousel and the normal sliding version. Thanks so much, happy holidays!

davidjerleke commented 4 years ago

Hi Damon (@dmorda),

Iโ€™m glad to hear that ๐Ÿ™‚. Thank you and happy holidays to you too!

Kindly, David

vntw commented 3 years ago

Hey @davidjerleke, sorry for digging up an old issue but I found your sandbox for a fade slider (thanks!). While playing around with it, I noticed that every slide is seen as "in view" by the slider and subsequently gets the selectedClass when the window is resized, meaning no more slides.

I added a reInit and resize listener to the sandbox here to fix this. If this is the correct change, it might make sense to update your original sandbox for future users that may find this?

Happy to hear your thoughts on this ๐Ÿ™‚ Thanks!

davidjerleke commented 3 years ago

Hi @vntw,

Thank you for noticing this.

I added a reInit and resize listener to the sandbox here to fix this. If this is the correct change, it might make sense to update your original sandbox for future users that may find this?

Your suggestion does the trick, nice work ๐Ÿ‘. I've updated the original CodeSandbox. I also added the skipSnaps: false; option because skipping slides when vigorous dragging occurs might lead to confusing UX for fade sliders. From the docs:

Allow the carousel to skip scroll snaps if it's dragged vigorously. Note that this option will be ignored if the dragFree option is set to true.

Thank you for your efforts!

Best, David

silllli commented 3 years ago

Thank you @davidjerleke!

davidjerleke commented 2 years ago

Reopening this:

As of v.6 itโ€™s possible to provide plugins to Embla. A Fade plugin will be added in the future:

aKandzior commented 1 year ago

Great solution with the fade effect!

I have an improvement suggestion for the CSS used in the SandBox.

I was applying the example CSS code to my project and noticed that since positon: absolute; is used to stack the slides, it is also required to set the slide height on the .embla__container.

In my case, I wanted the container to automatically use the height as defined by the slide markup itself. By changing the CSS to use display: grid; as per the code below, the height of the slides is still defined by the slide side markup.

.embla__container {
  transform: none !important; /* override Embla */
  display: flex;
}

.embla__slide {
  transform: none !important; /* required if loop is active */
  flex: 0 0 auto;
  width: 100%;
  height: 250px;
  opacity: 0;
  transition: opacity 1s;
  counter-increment: embla;
  border-radius: 0.5rem;
  font-size: 5rem;
}

.embla--is-ready .embla__container {
  display: grid;
}

.embla--is-ready .embla__slide {
  grid-row: 1;
  grid-column: 1;
}
SunpieAgency commented 1 year ago

Great solution with the fade effect! I have an improvement suggestion for the CSS used in the SandBox [...]```

I think best practice would be to do something like this:

.embla.fade.ready .embla__slide { position: absolute; } .embla__slide.is-selected { position: relative; }

This requires the classes module but works perfect for me. Height of the container is automatically adapted to your selected slide.

vettloffah commented 7 months ago

The sandbox linked multiple times here doesn't load anymore. Does anyone have a copy of the code?

davidjerleke commented 7 months ago
davidjerleke commented 6 months ago

Finally got an hour without interruptions to work on the embla-carousel-fade plugin. I have dragging between slides left to implement. Here's a sneak preview for anyone interested:

https://github.com/davidjerleke/embla-carousel/assets/11529148/8011050e-4c12-45fd-b310-1aa2e9160d95

nikolay17538583 commented 6 months ago

Finally got an hour without interruptions to work on the embla-carousel-fade plugin. I have dragging between slides left to implement. Here's a sneak preview for anyone interested:

fade-plugin-wip.mp4

๐Ÿ™๐Ÿผ๐Ÿฅบ

nikolay17538583 commented 6 months ago

Finally got an hour without interruptions to work on the embla-carousel-fade plugin. I have dragging between slides left to implement. Here's a sneak preview for anyone interested:

https://github.com/davidjerleke/embla-carousel/assets/11529148/8011050e-4c12-45fd-b310-1aa2e9160d95

David, any info when this will be available? I really need this ๐Ÿฅบ

davidjerleke commented 6 months ago

David, any info when this will be available? I really need this ๐Ÿฅบ

@nikolay17538583 unfortunately no. I'm doing this on my unpaid spare time so I never know when I get my next chance to work on this.

davidjerleke commented 6 months ago

Update

Weโ€™re getting close to a release people. I only have two edge cases left to fix and after that I will clean up some code and add a page to the documentation.

Anyone who wants to help out and speed up the release of this plugin can keep an eye on this issue as I will publish CodeSandboxes here as soon as Iโ€™m done with fixing the edge cases. If you guys test the sandboxes and help out, we might be able to catch some obvious bugs before the release.

Best, David

HIT2022 commented 5 months ago

Let us know if there's a sandbox ready; happy to help test.

mil920 commented 5 months ago

Yes let us know when it's ready i'm interested too :)

AndreiMotinga commented 5 months ago

@davidjerleke any chance to use this plugin now, even the active dev version?

walton-alex commented 5 months ago

Same here, I'd be happy to use an active dev version for now too. I appreciate the work @davidjerleke!

davidjerleke commented 5 months ago

I paused the development on this because I did some bug fixes which were released in v8.0.1 and v8.0.2. But they're released now so I will continue working on the fade plugin. Most things are in place but can't give an ETA of course because I don't have much spare time to begin with.

sitoexpress commented 5 months ago

@davidjerleke we're using splide in our projects and since it seems it's a dead end, we're thinking to move everything on Embla. A fade plugin would really make it the definitive js carousel!

davidjerleke commented 5 months ago

[!IMPORTANT]
This is NOT the finished product but a work in progress. Most things are in place but not everything yet.

React (last edit 2024-05-10)

meirroth commented 5 months ago

@davidjerleke Looks perfect! Do you think it'll be possible to apply the lazy rendering solution discussed here while using this new plugin? Rendering only the slides in view is especially useful for performance when dealing with heavy content like images (note how in your example app above, all images are loaded even though only one is shown), custom components, or even videos where you want the player to be destroyed when it goes out of view.

sarussss commented 5 months ago

Hi @davidjerleke. I think we can modify function positionSlides

  function positionSlides() {
    const { scrollSnaps, slideRegistry } = emblaApi.internalEngine()
    const slides = emblaApi.slideNodes()
    scrollSnaps.forEach((scrollSnap, scrollSnapIndex) => {
      const slidesInSnap = slideRegistry[scrollSnapIndex]
      slidesInSnap.forEach((slideIndex) => {
        slides[slideIndex].style.transform = `translateX(${scrollSnap}px)`
      })
    })
  }

Replace

  function positionSlides() {
    emblaApi.slideNodes().forEach((slide, slideIndex) => {
      slide.style.transform = `translateX(${slideIndex * -100}%)`
    })
  }

What do you think, is this possible? Demo

davidjerleke commented 5 months ago

@sarussss thank you for your input ๐Ÿ‘. My solution works with slides that are less than 100% wide too - They will be centered and also works when slidesToScroll: 'auto'. That's the motivation for my solution.

sarussss commented 5 months ago

@davidjerleke thank you for your feedback. I think it's not really necessary because when using fade, we will adjust the width in the parent element and fade only works well when there is only 1 slide in each viewport.

Don't care too much about these things, these are just some of my thoughts, because I don't understand Embla as deeply as you do, so if anything is incorrect, please forgive me.

P/S: Thanks for your hard work, the fade plugin is awesome.

Demo

ItsFrankieD commented 4 months ago

Out of curiosity, will the fade duration be adjustable? For both autoplay and next / previous trigger, I feel as though a duration adjustment is almost a necessity. Great work thus far!

davidjerleke commented 4 months ago

@ItsFrankieD yes, the default duration option for the carousel constructor changes the fade duration.

davidjerleke commented 4 months ago

Do you think it'll be possible to apply the lazy rendering solution discussed here while using this new plugin? Rendering only the slides in view is especially useful for performance when dealing with heavy content like images (note how in your example app above, all images are loaded even though only one is shown), custom components, or even videos where you want the player to be destroyed when it goes out of view.

@meirroth yes. The plugin will expose the slide opacity states with a method called opacities(). Not sure that opacities will be the final method name when released though so feel free to suggest a name. Let's say you want to load images for all slides that have an opacity greater than 0. You will be able to achieve that like this:

function loadImages(emblaApi) {
  const opacities = emblaApi.plugins().fade.opacities();

  opacities.forEach((opacity, snapIndex) => {
    if (opacity === 0) return // Not visible so bail here
    const slidesInSnap = emblaApi.internalEngine().slideRegistry[snapIndex];

    slidesInSnap.forEach((slideIndex) => { /* Load image for slideIndex */ })
  })
} 

emblaApi.on('scroll', loadImages);
meirroth commented 4 months ago

@davidjerleke Thank you! I look forward to using this plugin on our homepage ๐Ÿ˜ƒ

davidjerleke commented 4 months ago

@meirroth, @sarussss, @ItsFrankieD, @sitoexpress, @walton-alex, @AndreiMotinga, @mil920, @HIT2022 and anyone else interested, feel free to test the fade plugin here.

Let me know how it goes.

mil920 commented 4 months ago

@davidjerleke Thanks i tested it and i adapted it to ts and put it on my shadcn carousel and it work well.

davidjerleke commented 4 months ago

@mil920 thanks for letting me know! The package is written in TypeScript so that won't be a problem once officially released.

meirroth commented 4 months ago

Do you think it'll be possible to apply the lazy rendering solution discussed here while using this new plugin? Rendering only the slides in view is especially useful for performance when dealing with heavy content like images (note how in your example app above, all images are loaded even though only one is shown), custom components, or even videos where you want the player to be destroyed when it goes out of view.

@meirroth yes. The plugin will expose the slide opacity states with a method called opacities(). Not sure that opacities will be the final method name when released though so feel free to suggest a name. Let's say you want to load images for all slides that have an opacity greater than 0. You will be able to achieve that like this:

function loadImages(emblaApi) {
  const opacities = emblaApi.plugins().fade.opacities();

  opacities.forEach((opacity, snapIndex) => {
    if (opacity === 0) return // Not visible so bail here
    const slidesInSnap = emblaApi.internalEngine().slideRegistry[snapIndex];

    slidesInSnap.forEach((slideIndex) => { /* Load image for slideIndex */ })
  })
} 

emblaApi.on('scroll', loadImages);

@davidjerleke I tested again the Fade plugin you shared, and even tried it with the Vue wrapper and works perfectly. Regarding the lazy rendering solution above, would it make sense from API perspective for slidesInView to return only the slides that is currently visible while using this plugin, as supposed to returning ALL the slides like it does now? I think that will keep the lazy rendering solution consistent between Fade and not Fade carousels. https://stackblitz.com/edit/embla-carousel-nuxt-fade-lazy?file=app.vue

davidjerleke commented 4 months ago

Regarding the lazy rendering solution above, would it make sense from API perspective for slidesInView to return only the slides that is currently visible while using this plugin, as supposed to returning ALL the slides like it does now? I think that will keep the lazy rendering solution consistent between Fade and not Fade carousels.

@meirroth thanks for your ideas! I've updated the work in progress plugin here with the following:

Cleanup function

It's now in place so calling emblaApi.destroy() cleans up the inline styles and removes the event listeners that the fade plugin is using.

Slides in view

I was able to shift slides with 0 opacity out of view using transform and it doesn't seem to glitch at all. Try it out if you want and if you agree, this recreates parts of the core Embla functionality which is nice:

// Logging slides in view will work
emblaApi.on('slidesInView', (emblaApi) => {
  console.log(emblaApi.slidesInView())
})

However, I'm not sure it makes sense to try recreating the inViewThreshold option. Because when shifting slides with 0 opacity out of view and > 0 in view they will disappear/appear entirely in contrast to a scrolling carousel where slides gradually scroll into view.

meirroth commented 4 months ago

However, I'm not sure it makes sense to try recreating the inViewThreshold option. Because when shifting slides with 0 opacity out of view and > 0 in view they will disappear/appear entirely in contrast to a scrolling carousel where slides gradually scroll into view.

@davidjerleke Not sure I follow ๐Ÿ˜ฌ But my lazy load implementation above works with your update, and 100% matches non fade carousels which is neat!

sarussss commented 4 months ago

Hi @davidjerleke Effect fade only work on desktop. Mobile will run as a slide effect as usual. Is there any way to achieve that?

davidjerleke commented 4 months ago

@sarussss I donโ€™t have this problem. Are you sure youโ€™ve double checked everything in your code? Try the app view link here. Itโ€™s working as expected for me.

sarussss commented 4 months ago

Hi @davidjerleke. I mean I want to do that:

It's not a bug, I want to do it with embla, but I don't know how to do it.

davidjerleke commented 4 months ago

@sarussss use the breakpoints option:

const emblaNode = document.querySelector('.embla')
const emblaApi = EmblaCarousel(emblaNode, { loop: true }, [Fade({
  breakpoints: {
    '(min-width: 768px)': { active: false }
  }
})])
sarussss commented 4 months ago

@davidjerleke thank of ton.

davidjerleke commented 4 months ago

@sarussss donโ€™t forget to grab the latest updates from the work in progress React sandbox. Just copy the plugin code from the sandbox and replace it with your current plugin code.

davidjerleke commented 4 months ago

Only code cleanup and creating the documentation page for the fade plugin left to do before release. All features done โœ….

silllli commented 4 months ago

Awesome, thank you!

davidjerleke commented 4 months ago

Released in 8.1.0.

ribeiroeder commented 3 months ago

In my tests the fade effect seemed a little stuck, perhaps due to the greater number of children in the item, change duration with 20 and in CSS this rule made it smoother;

.embla__slide {
    transition-timing-function: linear;
}
meirroth commented 3 months ago

@ribeiroeder I suggest you also try lazy loading slides, that might help performance. Here's the implementation from earlier in this thread: https://stackblitz.com/edit/embla-carousel-nuxt-fade-lazy?file=app.vue