feat: 轮播图组件

This commit is contained in:
2026-05-14 15:10:57 +08:00
parent 5668ec3747
commit 89ea954743
9 changed files with 361 additions and 400 deletions

View File

@@ -0,0 +1,181 @@
<template>
<section class="carousel-container" aria-label="Featured content">
<KagolCarousel
v-model="activePage"
class="home-carousel"
:autoplay="isAutoplayEnabled"
:interval="5000"
>
<article v-for="slide in slides" :key="slide.id" class="carousel-slide">
<img class="carousel-image" :src="slide.image" :alt="slide.alt" />
</article>
<template #pagination="{ prevPage, nextPage }">
<nav class="carousel-pagination" aria-label="Carousel pagination">
<button
class="carousel-pagination-button carousel-pagination-button-prev"
type="button"
aria-label="Previous slide"
@click="prevPage"
>
<span
class="carousel-pagination-arrow carousel-pagination-arrow-prev"
></span>
</button>
<button
class="carousel-pagination-button carousel-pagination-button-next"
type="button"
aria-label="Next slide"
@click="nextPage"
>
<span
class="carousel-pagination-arrow carousel-pagination-arrow-next"
></span>
</button>
</nav>
</template>
<template #indicator></template>
</KagolCarousel>
</section>
</template>
<script setup lang="ts">
import { Carousel as KagolCarousel } from '@kagol/vue-carousel'
import '@kagol/vue-carousel/dist/style.css'
import { onMounted, shallowRef } from 'vue'
import mainBanner01 from '../../../assets/images/home/mainbanner01.jpg'
import mainBanner02 from '../../../assets/images/home/mainbanner02.jpg'
const activePage = shallowRef(1)
const isAutoplayEnabled = shallowRef(false)
const slides = [
{
id: 'aida',
image: mainBanner01,
alt: 'AiDA product banner'
},
{
id: 'mixi',
image: mainBanner02,
alt: 'Mixi product banner'
}
] as const
onMounted(() => {
isAutoplayEnabled.value = true
})
</script>
<style scoped>
.carousel-container {
width: 100%;
overflow: hidden;
background: #070b14;
}
.home-carousel {
width: 100%;
}
.carousel-slide {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
min-height: 360px;
max-height: 680px;
overflow: hidden;
background: #070b14;
}
.carousel-image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.carousel-pagination {
position: absolute;
right: 0;
bottom: 0;
z-index: 2;
display: flex;
flex-direction: column;
width: 70px;
height: 140px;
transform: translateX(100%);
transition: transform 0.28s ease;
will-change: transform;
}
.carousel-container:hover .carousel-pagination {
transform: translateX(0);
}
.carousel-pagination-button {
position: relative;
display: flex;
flex: 1 1 0;
align-items: center;
justify-content: center;
width: 100%;
padding: 0;
border: 0;
color: #ffffff;
background: #65090c;
cursor: pointer;
appearance: none;
transition: background-color 0.2s ease;
}
.carousel-pagination-button:hover {
background: rgba(255, 255, 255, 0.75);
}
.carousel-pagination-button:focus-visible {
position: relative;
z-index: 1;
outline: 2px solid #ffffff;
outline-offset: -6px;
}
.carousel-pagination-arrow {
position: absolute;
top: 50%;
left: 50%;
display: block;
box-sizing: border-box;
width: 20px;
height: 20px;
border-top: 2.5px solid currentColor;
border-right: 2.5px solid currentColor;
}
.carousel-pagination-arrow-prev {
transform: translate(-50%, -50%) rotate(225deg);
}
.carousel-pagination-arrow-next {
transform: translate(-50%, -50%) rotate(45deg);
}
:deep(.xui-carousel) {
width: 100%;
}
:deep(.xui-carousel-item-container) {
height: 100%;
}
@media (max-width: 768px) {
.carousel-slide {
min-height: 320px;
}
.carousel-pagination-arrow {
width: 18px;
height: 18px;
}
}
</style>