ismail9k / vue3-carousel

Vue 3 carousel component
https://ismail9k.github.io/vue3-carousel/
MIT License
653 stars 162 forks source link

Breakpoint Navigation broken #344

Open kode8 opened 7 months ago

kode8 commented 7 months ago

Describe the bug Go to https://ismail9k.github.io/vue3-carousel/examples.html Go to breakpoints example Click the right arrow to the very end, and click a few more ie. 10 times...then click the left arrow. You will notice you have to hit it multiple times before it shifts. Disabled buttons are not disabled and causing a count++

biancajs commented 6 months ago

I second this. It works if there is only one item-to-show but if there are multiple items-to-show AND wrap-around = false this bug occurs (like the official breakpoints example).

In the HTML Code you can see that there must be a mismatch between the items count and the items-to-show because the count acts like items-to-show is set to 1. <div class="carousel__liveregion carousel__sr-only" aria-live="polite" aria-atomic="true">Item 5 of 10</div>

Expected behavior: next and prev know how many slides are set to visible and are working correctly

jsikorjak commented 4 months ago

Hello, I had the same issue and as I find this carousel to be the best available I decided to fix it myself and the patch is not so complicated. The function updateSlidesData that is responsible for handling maxSlideIndex property fires only on a change of the carousel props or the slides collection but the breakpoints config applies later and the given function does not operate on the current configuration changed by the current view (config.itemsToShow is likely being changed on the responsive view). So I have wrapped the body of the function with a watch on the relevant property so its implementation is like this and it finally works as desired:

function updateSlidesData() { watch(() => config.itemsToShow, () => { if (slidesCount.value <= 0) return; middleSlideIndex.value = Math.ceil((slidesCount.value - 1) / 2); maxSlideIndex.value = getMaxSlideIndex({ config, slidesCount: slidesCount.value }); minSlideIndex.value = getMinSlideIndex({ config, slidesCount: slidesCount.value }); if (!config.wrapAround) { currentSlideIndex.value = getNumberInRange({ val: currentSlideIndex.value, max: maxSlideIndex.value, min: minSlideIndex.value, }); } }, { immediate: true }); }

ottz0 commented 4 months ago

Hello, I had the same issue and as I find this carousel to be the best available I decided to fix it myself and the patch is not so complicated. The function updateSlidesData that is responsible for handling maxSlideIndex property fires only on a change of the carousel props or the slides collection but the breakpoints config applies later and the given function does not operate on the current configuration changed by the current view (config.itemsToShow is likely being changed on the responsive view). So I have wrapped the body of the function with a watch on the relevant property so its implementation is like this and it finally works as desired:

function updateSlidesData() { watch(() => config.itemsToShow, () => { if (slidesCount.value <= 0) return; middleSlideIndex.value = Math.ceil((slidesCount.value - 1) / 2); maxSlideIndex.value = getMaxSlideIndex({ config, slidesCount: slidesCount.value }); minSlideIndex.value = getMinSlideIndex({ config, slidesCount: slidesCount.value }); if (!config.wrapAround) { currentSlideIndex.value = getNumberInRange({ val: currentSlideIndex.value, max: maxSlideIndex.value, min: minSlideIndex.value, }); } }, { immediate: true }); }

I'm trying to use this in a blade template,so it would be good to have this patch written into the component....Can you fork this and do it? Or can you show the full code.

AndreyMyagkov commented 2 months ago

My solution - updateSlidesData on mount and window resize

<carousel 
        :items-to-show="1"
        :breakpoints="breakpoints"
        snapAlign="start" 
        ref="slider"
    >
...
const debounce = function (fn, delay) {
  let timerId;
  return function (...args) {
    if (timerId) {
      clearTimeout(timerId);
    }
    timerId = setTimeout(() => {
      fn(...args);
      timerId = null;
    }, delay);
  };
}

//.......

methods: {
    onResize() {
      if (!this.$refs.slider) {
        return
      }
      this.$refs.slider.updateSlidesData();
    }

  },
  mounted() {
    window.addEventListener('resize', debounce(this.onResize, 200), {
      passive: true,
    })
    setTimeout(() => {
      this.onResize();
    }, 500)
  },
  beforeUnmount() {
    window.removeEventListener("resize", this.onResize);
  },