Vue3 + Swiper.js 实现无缝轮播图组件
前言
在数据可视化大屏项目中,轮播图组件是一个常见的需求。本文将详细介绍如何使用 Vue3 和 Swiper.js 开发一个功能完善的无缝轮播图组件,并分享实际项目中的最佳实践。
技术栈
- Vue 3 (Composition API)
- TypeScript
- Swiper.js
- CSS3
组件概述
SeamlessCarousel 是一个基于 Swiper.js 的 Vue 3 轮播图组件,专门用于展示虚拟电厂信息。该组件支持自动播放、手动导航、响应式布局等功能,适用于数据可视化大屏展示。
组件功能预览
我们即将开发的轮播图组件具备以下功能:
✅ 自动播放与手动切换
✅ 中心突出显示效果
✅ 响应式布局
✅ 详细信息展示
✅ 导航按钮
✅ 数据绑定
步骤一:创建项目并安装依赖
npm install swiper
npm install -D @types/swiper # 如果使用TypeScript
步骤二:创建组件
- 创建一个名为SeamlessCarousel.vue 的组件文件,并定义组件的属性、数据、方法、事件等。
vue
<template>
<div class="seamless-carousel-container">
<div class="carousel-wrapper">
<!-- 主轮播 -->
<swiper
ref="mainSwiperRef"
:preload-classes="true"
:lazy="true"
:initial-slide="0"
:modules="modules"
:space-between="180"
:slides-per-view="3"
:centered-slides="true"
:loop="true"
:loop-additional-slides="1"
:grab-cursor="true"
:effect="'slide'"
:speed="800"
:allow-touch-move="true"
:watch-slides-progress="true"
:watch-slides-visibility="true"
:autoplay="{
delay: 3000,
disableOnInteraction: true,
pauseOnMouseEnter: true,
waitForTransition: false,
}"
:navigation="{
nextEl: '.right-arrow',
prevEl: '.left-arrow',
}"
class="main-swiper"
@swiper="setMainSwiper"
@slide-change="onSlideChange"
>
<swiper-slide v-for="(plant, index) in slides" :key="index">
<div class="test-plant-card" :class="{
'slide-center': activeIndex === index,
'slide-side': activeIndex !== index,
'initialized': activeIndex >= 0
}">
<div class="title" :title="plant.name">{{ plant.name }} </div>
<div class="card-content">
<img :src="getImage(plant.image)"/>
<div class="common-content">
<p>位置:{{ plant.location }}</p>
</div>
<div class="full-content">
<p >类型:{{ plant.type }}</p>
<p >数量:{{ plant.num }}台</p>
</div>
</div>
</div>
</swiper-slide>
<img ref="prevRef" class="left-arrow" src="@/assets/arrow-left.png"/>
<img ref="nextRef" class="right-arrow" src="@/assets/arrow-right.png" />
</swiper>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Autoplay, EffectFade, Controller } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/effect-fade';
// Swiper 模块
const modules = [Navigation, Autoplay, EffectFade, Controller];
// Swiper 实例引用
const mainSwiperRef = ref<any>(null);
let mainSwiper: any = null;
// 当前激活的幻灯片索引
const activeIndex = ref(0);
// 获取图片资源
const getImage = (name: string) => {
return new URL(`../assets/${name}.png`, import.meta.url).href
}
interface TestPlant {
name: string
location: string
image?: string
num?: number
type?: string
}
// 定义组件属性
interface Props {
title?: string;
autoPlayDelay?: number;
speed?: number;
hasLoaded?: boolean;
plants: TestPlant[];
}
const props = withDefaults(defineProps<Props>(), {
autoPlayDelay: 3000,
speed: 800,
hasLoaded: false,
plants: []
});
// 定义事件
const emit = defineEmits(['swiper-change'])
// 设置主 Swiper 实例
const setMainSwiper = (swiper: any) => {
mainSwiper = swiper;
};
// 幻灯片切换回调
const onSlideChange = (index) => {
if (mainSwiper) {
activeIndex.value = mainSwiper.realIndex;
emit('swiper-change', slides.value[activeIndex.value]);
}
};
// 使用传入的幻灯片数据或默认数据
const slides = computed(() => props.plants || []);
// 监听数据加载状态
watch(
() => props.hasLoaded,
(newVal) => {
if (newVal && mainSwiper) {
mainSwiper.update();
emit('swiper-change', slides.value[mainSwiper.realIndex]);
}
},
{ immediate: true }
);
</script>
<style scoped>
<style>
.seamless-carousel-container {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.carousel-wrapper {
position: relative;
width: 100%;
height: 100%;
overflow: visible;
padding: 0;
}
.swiper {
height: 170px !important;
}
.swiper .left-arrow {
position: absolute;
left: 0;
top: 0;
bottom: 0;
display: inline-block;
margin: auto;
width: 34px;
height: 34px;
z-index: 99;
cursor: pointer;
}
.swiper .right-arrow {
position: absolute;
right: 0;
top: 0;
bottom: 0;
display: inline-block;
margin: auto;
width: 34px;
height: 34px;
z-index: 99;
cursor: pointer;
}
.main-swiper {
width: 100%;
height: 170px;
}
.main-swiper .swiper-slide {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
height: 170px;
}
.main-swiper .swiper-slide.swiper-slide-active .test-plant-card {
width: 500px !important;
border: 2px solid #FFA200;
}
/* 卡片样式 */
.test-plant-card {
overflow: hidden;
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.main-swiper .swiper-slide .test-plant-card {
width: 190px !important;
height: 170px;
background: white;
border-radius: 8px;
padding: 22px 20px 20px;
box-sizing: border-box;
box-shadow: 0px 0px 10px 0px rgba(6, 0, 1, 0.05);
flex-shrink: 0;
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.main-swiper .swiper-slide .test-plant-card .title {
font-size: 20px;
font-weight: 600;
color: #666666;
font-family: "MiSans-semibold";
margin-bottom: 20px;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/* 激活状态下的样式 */
.main-swiper .swiper-slide.swiper-slide-active .test-plant-card .title {
color: #282828;
margin-bottom: 18px;
}
</style>
使用方法
- 引入组件:在需要使用的地方引入组件:
vue
<script setup>
import SeamlessCarousel from './SeamlessCarousel.vue';
// 如果需要监听卡片切换事件,请定义此方法
const handleSlideChange = (currentPlant) => {
console.log('当前选中的卡片:', currentPlant);
};
</script>
- 定义数据:定义需要展示的卡片数据,可以是静态数据,也可以是动态数据。
vue
<script setup>
const plants = [
{
name: 'Plant 1',
location: 'Location 1',
image: 'plant1.png',
num: 10,
type: 'Type 1'
},
... more plants
]
</script>
- 配置参数:根据需要配置组件的参数,如自动播放延迟、切换速度、数据加载状态等。
- 渲染组件:将组件渲染到页面中。
vue
<template>
<SeamlessCarousel
:auto-play-delay="5000"
:speed="1000"
:has-loaded="true"
@swiper-change="handleSlideChange"
/>
</template>
-
监听卡片切换事件:如果需要监听卡片切换事件,请定义一个方法,并绑定到组件的 swiper-change 事件上。
-
运行程序:在浏览器中运行程序,即可看到效果。
注意事项:
- 确保已安装所需的依赖包。
- 确保已正确配置组件的参数,如自动播放延迟、切换速度、数据加载状态等。
- 确保已正确定义数据源,数据源可以是静态数据,也可以是动态数据。
- 确保已正确渲染组件,并绑定了监听事件。
- 运行程序并查看效果。