为什么单击时轮播上的导航圈没有更新?
Why are my navigation circles on carousel not updating when clicked?
我正在从头开始构建自己的旋转木马,除了单击导航圆圈时,一切都运行良好。
如果你有 interval/infinite 循环道具,圆圈会更新为正确的活动幻灯片,按下左右两侧的按钮也是如此。但是,当我直接单击圆圈时,一切都变得一团糟,轮播中断了。
它显然与我为该操作编写的 switchSlide
函数有关,但我终究无法弄明白。
如有任何帮助或建议,我们将不胜感激!
干杯!
Here is the Codesandbox I've made
注意:我只是将索引放在每个圆圈内,以确保渲染的数量正确。
Carousel.vue
<template>
<div class="audition-hero-carousel">
<div class="carousel-inner">
<div v-if="navigation" class="carousel-circles">
<button
v-for="(fileInfo, index) in modelValue"
class="carousel-circle-item"
:style="{ background: `${color}` }"
:class="{ active: currentSlide === index }"
: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"
class="carousel-item"
@mouseenter="() => stopSlideTimer()"
@mouseout="() => startSlideTimer()"
>
<img
v-if="fileInfo.type.startsWith('image')"
:src="fileInfo.url"
class="_img"
draggable="false"
/>
</div>
</TransitionGroup>
<div v-if="controls">
<button
:style="{ color: `${color}` }"
class="carousel-control left"
@click="() => prev()"
>
←
</button>
<button
:style="{ color: `${color}` }"
class="carousel-control right"
@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>
尝试更改方法调用,来自:
@click="(index) => switchSlide(index)"
至:
@click="switchSlide(index)"
我正在从头开始构建自己的旋转木马,除了单击导航圆圈时,一切都运行良好。
如果你有 interval/infinite 循环道具,圆圈会更新为正确的活动幻灯片,按下左右两侧的按钮也是如此。但是,当我直接单击圆圈时,一切都变得一团糟,轮播中断了。
它显然与我为该操作编写的 switchSlide
函数有关,但我终究无法弄明白。
如有任何帮助或建议,我们将不胜感激!
干杯!
Here is the Codesandbox I've made
注意:我只是将索引放在每个圆圈内,以确保渲染的数量正确。
Carousel.vue
<template>
<div class="audition-hero-carousel">
<div class="carousel-inner">
<div v-if="navigation" class="carousel-circles">
<button
v-for="(fileInfo, index) in modelValue"
class="carousel-circle-item"
:style="{ background: `${color}` }"
:class="{ active: currentSlide === index }"
: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"
class="carousel-item"
@mouseenter="() => stopSlideTimer()"
@mouseout="() => startSlideTimer()"
>
<img
v-if="fileInfo.type.startsWith('image')"
:src="fileInfo.url"
class="_img"
draggable="false"
/>
</div>
</TransitionGroup>
<div v-if="controls">
<button
:style="{ color: `${color}` }"
class="carousel-control left"
@click="() => prev()"
>
←
</button>
<button
:style="{ color: `${color}` }"
class="carousel-control right"
@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>
尝试更改方法调用,来自:
@click="(index) => switchSlide(index)"
至:
@click="switchSlide(index)"