112 lines
2.7 KiB
Vue
112 lines
2.7 KiB
Vue
|
<template>
|
||
|
<div v-show="showSwiper" class="relative" :class="rootClasses">
|
||
|
<swiper
|
||
|
loop-add-blank-slides
|
||
|
:class="sliderClasses"
|
||
|
:direction="direction"
|
||
|
:space-between="50"
|
||
|
:breakpoints="{
|
||
|
2000: { slidesPerView: 1, spaceBetween: 40 },
|
||
|
}"
|
||
|
@swiper="onSwiper"
|
||
|
@slide-change="onSlideChange"
|
||
|
>
|
||
|
<swiper-slide
|
||
|
v-for="slide in 4"
|
||
|
:key="slide"
|
||
|
class="h-full hover:cursor-pointer"
|
||
|
>
|
||
|
<slot />
|
||
|
</swiper-slide>
|
||
|
</swiper>
|
||
|
<div
|
||
|
class="flex justify-between items-center h-10 cursor-pointer"
|
||
|
:class="counterClasses"
|
||
|
>
|
||
|
<div
|
||
|
v-for="(slide, index) in 4"
|
||
|
:key="index"
|
||
|
:class="[
|
||
|
'bg-BgDark dark:bg-BgLight h-[3px] transition-all duration-500',
|
||
|
index === currentSlideNumber
|
||
|
? 'py-2 w-[16px] rounded-3xl'
|
||
|
: 'w-[25px] rounded-sm md:w-[24px]',
|
||
|
]"
|
||
|
@click="goToSlide(index), stopAutoSlide()"
|
||
|
></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import { ref, computed, defineProps, withDefaults, onMounted } from "vue";
|
||
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||
|
import type { Swiper as swiper } from "swiper/types";
|
||
|
import "swiper/css";
|
||
|
import { twMerge } from "tailwind-merge";
|
||
|
import type { PTAttribs } from "./types";
|
||
|
|
||
|
const showSwiper = ref(false);
|
||
|
const currentSlideNumber = ref(0);
|
||
|
let swiperInstance: swiper | null = null;
|
||
|
let intervalId: number | null = null;
|
||
|
|
||
|
const onSwiper = (swiper: swiper) => {
|
||
|
swiperInstance = swiper;
|
||
|
showSwiper.value = true;
|
||
|
};
|
||
|
|
||
|
const onSlideChange = (swiper: swiper) => {
|
||
|
currentSlideNumber.value = swiper.realIndex;
|
||
|
};
|
||
|
|
||
|
function startAutoSlide() {
|
||
|
intervalId = setInterval(() => {
|
||
|
const nextSlide = (currentSlideNumber.value + 1) % 4;
|
||
|
goToSlide(nextSlide);
|
||
|
}, 5000);
|
||
|
}
|
||
|
|
||
|
function goToSlide(index: number) {
|
||
|
swiperInstance?.slideTo(index);
|
||
|
currentSlideNumber.value = index;
|
||
|
}
|
||
|
|
||
|
function stopAutoSlide() {
|
||
|
if (intervalId !== null) {
|
||
|
clearInterval(intervalId);
|
||
|
intervalId = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onMounted(() => {
|
||
|
startAutoSlide();
|
||
|
});
|
||
|
|
||
|
const props = withDefaults(
|
||
|
defineProps<{
|
||
|
direction: "horizontal" | "vertical";
|
||
|
pt?: PTAttribs;
|
||
|
}>(),
|
||
|
{
|
||
|
direction: "horizontal",
|
||
|
}
|
||
|
);
|
||
|
|
||
|
const classes = ref({
|
||
|
root: { class: `` },
|
||
|
slider: { class: `` },
|
||
|
counter: { class: `mx-auto px-6 container` },
|
||
|
} as PTAttribs);
|
||
|
|
||
|
const rootClasses = computed(() =>
|
||
|
twMerge(classes.value.root?.class, props.pt?.root?.class)
|
||
|
);
|
||
|
const sliderClasses = computed(() =>
|
||
|
twMerge(classes.value.slider?.class, props.pt?.slider?.class)
|
||
|
);
|
||
|
const counterClasses = computed(() =>
|
||
|
twMerge(classes.value.counter?.class, props.pt?.counter?.class)
|
||
|
);
|
||
|
</script>
|