使用swiper库写水平轮播图,带过渡效果
前言
之前是使用element-plus的el-carousel组件改造的官网轮播图。由于element-plus没有滑动切换轮播图的方法,本可以手写,但考虑多学一点,就使用了swiper组件。
关联功能页面
安装方法
npm install swiper // 安装的版本是12.2.0
轮播图组件
<template>
<div class="swiper-horizontal">
<swiper
style="height:100vh"
:modules="modules"
effect="slide"
:speed="700"
:loop="true"
:autoplay="{
delay: 3000, // 切换间隔(毫秒),默认 3000
disableOnInteraction: false, // 是否在用户交互后禁用自动播放,默认 false
}"
:navigation="{
nextEl: '.my-next-btn',
prevEl: '.my-prev-btn'
}"
:pagination="{
clickable: true,
el: '.custom-pagination' // 自定义容器
}"
@swiper="setSwiper"
@slideChange="onSlideChange"
>
<swiper-slide v-for="(item, index) in productList" :key="index">
<div class="swiper-item">
<img :src="item.img" alt="">
</div>
</swiper-slide>
</swiper>
<div class="custom-pagination"></div>
<div
:style="{
width: indicatorWidth + 'px',
height: indicatorHeight + 'px',
left: indicatorLeft + 'px'
}"
class="swiper-indicators">
<div style="padding-top: 0.6vw;">
<div
class="swiper-indicator-arrow iconfont icon-shangjiantou my-prev-btn"
>
</div>
</div>
<div
:style="{ width: miniImageWidth + 'px', height: miniImageWidth + 'px' }"
class="swiper-indicator-item"
:class="{'active': miniIndex === activeIndex}"
v-for="(item, miniIndex) in productList"
:key="miniIndex"
@click="carouselRef.setActiveItem(miniIndex)">
<img :src="item.miniImg" alt="">
</div>
<div style="padding-bottom: 0.6vw;">
<div
class="swiper-indicator-arrow iconfont icon-xiajiantou my-next-btn"
>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { calculateWidth, calculateHeight } from '@/utils/dimensionCalculator'
import { useWindowResize } from '@/composables/useWindowResize'
// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from 'swiper/vue';
// import Swiper core and required modules
import { Autoplay, Pagination, Navigation } from 'swiper/modules'
// 注册模块
const modules = [Autoplay, Pagination, Navigation]
// Import Swiper styles
import 'swiper/css';
// 引入功能模块样式(用了哪个功能就要引入对应的样式)
import 'swiper/css/navigation'
import 'swiper/css/pagination'
const swiperInstance = ref(null)
const activeIndex = ref(0)
const setSwiper = (swiper) => {
// 自定义缓动函数:cubic-bezier(0.22, 0.61, 0.36, 1)
swiper.wrapperEl.style.transitionTimingFunction = 'cubic-bezier(0.42, 0.0, 0.58, 1.0)'
}
// 切换
const onSlideChange = (swiper) => {
activeIndex.value = swiper.realIndex
}
const productList = [
{
img: new URL('@/static/images/product/banner/product1.jpg', import.meta.url).href,
miniImg: new URL('@/static/images/product/banner/product1-mini.png', import.meta.url).href,
},
{
img: new URL('@/static/images/product/banner/product2.jpg', import.meta.url).href,
miniImg: new URL('@/static/images/product/banner/product2-mini.png', import.meta.url).href,
},
{
img: new URL('@/static/images/product/banner/product3.jpg', import.meta.url).href,
miniImg: new URL('@/static/images/product/banner/product3-mini.png', import.meta.url).href,
},
{
img: new URL('@/static/images/product/banner/product4.jpg', import.meta.url).href,
miniImg: new URL('@/static/images/product/banner/product4-mini.png', import.meta.url).href,
},
{
img: new URL('@/static/images/product/banner/product5.jpg', import.meta.url).href,
miniImg: new URL('@/static/images/product/banner/product5-mini.png', import.meta.url).href,
},
]
// 使用窗口大小监听组合式函数
useWindowResize(() => {
calculateDimensions()
})
const resizeFlag = ref(0)
const indicatorWidth = ref(70)
const indicatorHeight = ref(498)
const indicatorLeft = ref(92)
const miniImageWidth = computed(() => {
return indicatorWidth.value > 24 ? indicatorWidth.value - 6 : 18
})
// 计算轮播图高度和小图片尺寸
const calculateDimensions = () => {
const windowWidth = window.innerWidth
// 1920宽度下高度710,按比例计算
indicatorWidth.value = calculateWidth(70, windowWidth)
indicatorHeight.value = calculateHeight(1920, 498, windowWidth)
indicatorLeft.value = calculateWidth(92, windowWidth)
resizeFlag.value++
}
</script>
<style scoped>
.swiper-horizontal {
position: relative;
/* 禁止拖拽图片(针对 img 标签) */
user-select: none;
}
.swiper-item {
height: 100%;
}
.swiper-item img {
width: 100%; /* 宽度占满 */
height: 100%; /* 高度自适应 */
max-height: 100%; /* 不超出容器高度 */
display: block;
}
.swiper-indicators {
position: absolute;
top: 50%;
z-index: 10;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: space-evenly;
flex-direction: column;
gap: 1vw;
background: rgba(25, 52, 11, 0.8);
border-radius: 50px;
min-width: 24px;
min-height: 172px;
}
.swiper-indicator-item {
display: flex;
border-radius: 50%;
cursor: pointer;
}
.swiper-indicator-item:hover {
transform: scale(1.01);
box-shadow: 0 0 10px #fff;
}
.swiper-indicator-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.swiper-indicator-item.active {
box-shadow: 0 0 10px #fff;
}
.swiper-indicator-arrow {
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(20px, 1.25vw, 24px);
color: #fff;
cursor: pointer;
}
.custom-pagination {
display: none;
}
/* 或者直接覆盖 */
:deep(.swiper-pagination-bullet-active) {
background: #a7ca25 !important;
}
@media screen and (max-width: 1200px) {
.swiper-horizontal {
margin-top: 80px;
}
}
@media (max-width: 480px) {
.swiper-horizontal {
margin-top: 70px;
}
.custom-pagination {
height: 20px;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
}
.swiper-indicators {
display: none;
}
}
</style>
运行效果

