Files
rsxm-master/src/views/recruitment/components/carousel.vue

325 lines
6.9 KiB
Vue
Raw Normal View History

2025-08-29 18:48:16 +08:00
<template>
<div class="swiper-container">
<div class="swiper-wrapper">
<div
class="swiper-slide"
:class="getSlideClass(index)"
v-for="(slide, index) in slides"
:key="index"
:style="getSlideStyle(index)"
>
<!-- 使用具名插槽传递每个slide的内容和索引 -->
<div class="slide-content">
<div class="slide-image">
<slot name="slide" :slide="slide" :index="index" />
</div>
<!-- <div class="slide-text">-->
<!-- <h3>{{ slide.title }}</h3>-->
<!-- <p>{{ slide.description }}</p>-->
<!-- </div>-->
</div>
</div>
</div>
<!-- <div class="swiper-controls">-->
<!-- <button class="swiper-button" @click="prevSlide"></button>-->
<!-- <button class="swiper-button" @click="nextSlide"></button>-->
<!-- </div>-->
<!-- <div class="swiper-pagination">-->
<!-- <div-->
<!-- class="pagination-bullet"-->
<!-- v-for="(slide, index) in slides"-->
<!-- :key="index"-->
<!-- :class="{ active: currentIndex === index }"-->
<!-- @click="goToSlide(index)"-->
<!-- ></div>-->
<!-- </div>-->
</div>
</template>
<script>
import { defineComponent, onMounted, onBeforeUnmount, ref } from 'vue'
export default defineComponent({
name: 'Carousel',
props: {
slides: {
type: Array,
required: true,
default: () => [
{
title: '左侧卡片',
description: '淡入效果从左侧进入',
image: 'https://via.placeholder.com/600x400/3498db/ffffff?text=Left'
},
{
title: '中间卡片',
description: '淡入效果居中放大',
image: 'https://via.placeholder.com/600x400/e74c3c/ffffff?text=Center'
},
{
title: '右侧卡片',
description: '淡入效果从右侧进入',
image: 'https://via.placeholder.com/600x400/2ecc71/ffffff?text=Right'
}
]
},
autoPlayInterval: {
type: Number,
default: 5000
}
},
setup(props) {
const currentIndex = ref(0)
const autoPlayTimer = ref(null)
const getSlideClass = (index) => {
const diff = index - currentIndex.value
if (diff === 0) return 'center'
if (diff === -1 || (currentIndex.value === 0 && index === props.slides.length - 1)) return 'left'
if (diff === 1 || (currentIndex.value === props.slides.length - 1 && index === 0)) return 'right'
return ''
}
const getSlideStyle = (index) => {
const diff = index - currentIndex.value
if (
Math.abs(diff) > 1 &&
!(currentIndex.value === 0 && index === props.slides.length - 1) &&
!(currentIndex.value === props.slides.length - 1 && index === 0)
) {
return { display: 'none' }
}
return {}
}
const prevSlide = () => {
currentIndex.value = (currentIndex.value - 1 + props.slides.length) % props.slides.length
resetAutoPlay()
}
const nextSlide = () => {
currentIndex.value = (currentIndex.value + 1) % props.slides.length
resetAutoPlay()
}
const goToSlide = (index) => {
currentIndex.value = index
resetAutoPlay()
}
const startAutoPlay = () => {
pauseAutoPlay()
autoPlayTimer.value = window.setInterval(() => {
nextSlide()
}, props.autoPlayInterval)
}
const pauseAutoPlay = () => {
if (autoPlayTimer.value) {
clearInterval(autoPlayTimer.value)
autoPlayTimer.value = null
}
}
const resetAutoPlay = () => {
pauseAutoPlay()
startAutoPlay()
}
onMounted(() => {
// const container = document.querySelector('.swiper-container')
// if (container) {
// container.addEventListener('mouseenter', pauseAutoPlay)
// container.addEventListener('mouseleave', startAutoPlay)
// }
// startAutoPlay()
})
onBeforeUnmount(() => {
const container = document.querySelector('.swiper-container')
if (container) {
container.removeEventListener('mouseenter', pauseAutoPlay)
container.removeEventListener('mouseleave', startAutoPlay)
}
pauseAutoPlay()
})
return {
currentIndex,
getSlideClass,
getSlideStyle,
prevSlide,
nextSlide,
goToSlide
}
}
})
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.swiper-container {
width: 17.03125vw;
position: relative;
//padding: 2.083vw 0;
}
.swiper-wrapper {
display: flex;
position: relative;
//height: 400px;
}
.swiper-slide {
width: 17.03125vw;
transition: all 0.5s ease;
position: absolute;
opacity: 0;
}
.swiper-slide.left {
left: 2vw;
transform: scale(0.9);
z-index: 1;
opacity: 0.8;
animation: fadeInLeft 0.5s ease forwards;
}
.swiper-slide.center {
left: 50%;
transform: translateX(-50%) scale(1);
z-index: 2;
opacity: 0;
animation: fadeInCenter 0.5s ease forwards;
}
.swiper-slide.right {
right: 2vw;
transform: scale(0.9);
z-index: 1;
opacity: 0.8;
animation: fadeInRight 0.5s ease forwards;
}
@keyframes fadeInLeft {
from {
opacity: 0;
transform: scale(0.8) translateX(-20px);
}
to {
opacity: 0.8;
transform: scale(0.9) translateX(0);
}
}
@keyframes fadeInCenter {
from {
opacity: 0;
transform: translateX(-50%) scale(0.95);
}
to {
opacity: 1;
transform: translateX(-50%) scale(1);
}
}
@keyframes fadeInRight {
from {
opacity: 0;
transform: scale(0.8) translateX(20px);
}
to {
opacity: 0.8;
transform: scale(0.9) translateX(0);
}
}
.slide-content {
background: white;
border-radius: 0.208vw;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.slide-image {
background: url("~@/assets/recruitment/card_bg.svg") no-repeat;
height: 16.5625vw;
background-size: 100% 100%;
}
.slide-text {
padding: 20px;
flex-grow: 1;
}
.slide-text h3 {
margin-bottom: 10px;
color: #333;
font-size: 18px;
}
.slide-text p {
color: #666;
font-size: 14px;
line-height: 1.5;
}
.swiper-controls {
display: flex;
justify-content: center;
margin-top: 30px;
}
.swiper-button {
width: 40px;
height: 40px;
border-radius: 50%;
background: #333;
color: white;
border: none;
margin: 0 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: all 0.3s ease;
}
.swiper-button:hover {
background: #555;
transform: scale(1.1);
}
.swiper-pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination-bullet {
width: 12px;
height: 12px;
border-radius: 50%;
background: #ccc;
margin: 0 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.pagination-bullet.active {
background: #333;
transform: scale(1.2);
}
</style>