I am building my own carousel from scratch and everything works perfectly fine except for when you click the navigation circles.
If you have the interval/infinite loop prop, the circles update to the correct active slide and the same goes for pressing the side left and right buttons. However, when I click the circles directly, things get all messed up and the carousel breaks.
It obviously has something to do with the switchSlide
function I've written for that action, but I cannot figure it out for the life of me.
Any help or advice would be greatly appreciated!
Cheers!
Here is the Codesandbox I've made
NOTE: I just put the index inside of each circle just to maake sure the proper amount were rendering.
Carousel.vue
<template>
<div >
<div >
<div v-if="navigation" >
<button
v-for="(fileInfo, index) in modelValue"
:style="{ background: `${color}` }"
:
:key="index"
:name="index"
@click="(index) => switchSlide(index)"
></button>
</div>
<TransitionGroup :name="transitionEffect">
<div
v-show="currentSlide === index"
v-for="(fileInfo, index) in modelValue"
:key="index"
:name="index"
@mouseenter="() => stopSlideTimer()"
@mouseout="() => startSlideTimer()"
>
<img
v-if="fileInfo.type.startsWith('image')"
:src="fileInfo.url"
draggable="false"
/>
</div>
</TransitionGroup>
<div v-if="controls">
<button
:style="{ color: `${color}` }"
@click="() => prev()"
>
←
</button>
<button
:style="{ color: `${color}` }"
@click="() => next()"
>
→
</button>
</div>
</div>
</div>
<!-- {{modelValue}} -->
</template>
<script>
export default {
name: "CAuditionHeroCarousel2",
props: {
/**
* @type {{ type: 'image' | 'video' | 'youtube', url: string }[]}
*/
modelValue: { type: Array },
controls: {
type: Boolean,
default: false,
},
navigation: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 5000,
},
infinite: {
type: Boolean,
default: true,
},
color: {
type: String,
default: "#9759ff",
},
},
data() {
return {
currentSlide: 0,
direction: "right",
};
},
methods: {
setCurrentSlide(index) {
this.currentSlide = index;
},
prev(step = -1) {
const index =
this.currentSlide > 0
? this.currentSlide step
: this.modelValue.length - 1;
this.setCurrentSlide(index);
this.direction = "left";
this.startSlideTimer();
},
_next(step = 1) {
const index =
this.currentSlide < this.modelValue.length - 1
? this.currentSlide step
: 0;
this.setCurrentSlide(index);
this.direction = "right";
},
next(step = 1) {
this._next(step);
this.startSlideTimer();
},
startSlideTimer() {
if (this.infinite) {
this.stopSlideTimer();
setInterval(() => {
this._next();
}, this.interval);
}
},
stopSlideTimer() {
clearInterval(this.interval);
},
switchSlide(index) {
const step = index - this.currentSlide;
if (step > 0) {
this.next(step);
} else {
this.prev(step);
}
},
},
computed: {
transitionEffect() {
return this.direction === "right" ? "slide-out" : "slide-in";
},
},
mounted() {
if (this.infinite) {
this.startSlideTimer();
}
},
beforeUnmount() {
this.stopSlideTimer();
},
};
</script>
<style lang="sass" scoped>
// Carousel Main
.audition-hero-carousel
display: flex
justify-content: center
min-width: 0px
width: 450px
height: 200px
.carousel-inner
position: relative
width: 100%
height: 100%
overflow: hidden
.carousel-circles
position: absolute
display: flex
justify-content: center
transform: translateX(-50%)
left: 50%
bottom: 2em
width: 100%
z-index: 2
.carousel-circle-item
width: 15px
height: 15px
border: none
opacity: 0.65
margin-right: 20px
border-radius: 50%
cursor: pointer
&:hover
cursor: pointer
&:last-child
margin-right: 0px
._img,
._video
width: 100%
height: 100%
._img
object-fit: cover
._video > *
height: 100%
width: 100%
.active
opacity: 1
/**
Carousel Item styles
*/
.carousel-item
position: absolute
top: 0
left: 0
right: 0
bottom: 0
.slide-in-enter-active,
.slide-in-leave-active,
.slide-out-enter-active,
.slide-out-leave-active
transition: all 300ms ease-in-out
.slide-in-enter-from
transform: translateX(-100%)
.slide-in-leave-to
transform: translateX(100%)
.slide-out-enter-from
transform: translateX(100%)
.slide-out-leave-to
transform: translateX(-100%)
/**
Controls
*/
.carousel-control
outline: none
border: none
background: transparent
display: inline-block
position: absolute
height: 50px
width: 70px
top: calc(50% - 20px)
cursor: pointer
.left
left: 0
.right
right: 0
</style>
App.vue
<template>
<Carousel
:modelValue="slides"
:interval="3000"
:navigation="true"
:controls="true"
:infinite="false"
:color="'#fff'"
/>
</template>
<script>
import Carousel from "./components/Carousel.vue";
export default {
name: "App",
components: {
Carousel,
},
data() {
return {
slides: [
{ type: "image", url: "https://picsum.photos/id/1032/900/400" },
{ type: "image", url: "https://picsum.photos/id/1033/900/400" },
{ type: "image", url: "https://picsum.photos/id/1037/900/400" },
{ type: "image", url: "https://picsum.photos/id/1035/900/400" },
{ type: "image", url: "https://picsum.photos/id/1036/900/400" },
],
};
},
};
</script>
CodePudding user response:
Try to change method call, from:
@click="(index) => switchSlide(index)"
to:
@click="switchSlide(index)"