在React中使用Swiper
一般项目中使用轮播图的时候,第一个想到的就是Swiper组件库,当然,如果简单的轮播切换,大多数项目中选用的 UI组件库就已经满足要求了;
这里补充一点:
当前文章安装的Swiper版本为:"swiper": "9.4.1",,多仔细看看文档;
如果出现不起作用或者不对的地方,可以从版本考虑;
还有一点是,在文档的最上头有个警告:
Swiper React components这种方式在未来会被移除,建议如果使用的话,注意锁一下版本;
1. 基本使用
第一步肯定是安装Swiper的库啦:
npm i swiper
然后就可以在项目里使用了,在这里提一嘴,如果你的项目里有多个地方都会用到Swiper组件,建议直接在项目的根文件中引入Swiper的样式文件:swiper/css
然后就可以引入Swiper组件:
import { Swiper, SwiperSlide } from 'swiper/react'
...
<Swiper className={styles.baseSwiperContent}>
{swiperData.map((item, index) => (
<SwiperSlide key={index}>
<img className={styles.baseImg} src={item.img} />
</SwiperSlide>
))}
</Swiper>
基本效果:
ps:(图片来源于网络🐶)
2. 引入Swiper库的子组件
基本的一个注意点和步骤:
- 首先我们要从
'swiper'引入;比如引入Navigation指示器:
import { Navigation } from 'swiper';
这里有个需要注意的地方,现在Swiper的版本已经升级到v10.1.0了,使用的时候注意下你使用的Swiper版本;
我前几天在项目里用的时候还是Swiper9,这里没注意刚开始下的是Swiper10,但是按照文档从swiper/modules引入组件时,控制台一直报错,没有modules这个属性,没找到原因,所以又将Swiper降到了9版本;
Swiper9的话引入子模块是'swiper',主要不要引成swiper/modules;
- 然后引入对应的子组件的样式文件,或者直接将子模块的所有样式文件直接都引入,这样后期使用的时候就不用多次引入了;
// 具体某个子模块
import 'swiper/css/navigation';
// 全部子模块
import 'swiper/css/bundle';
- 然后在Swiper组件中进行安装和使用;
这里贴一个简单的例子:
import { Swiper, SwiperSlide } from 'swiper/react'
import { Pagination } from 'swiper'
...
<Swiper
className={styles.baseSwiperContent}
modules={[Pagination]}
pagination
>
{swiperData.map((item, index) => (
<SwiperSlide key={index}>
<img className={styles.baseImg} src={item.img} />
</SwiperSlide>
))}
</Swiper>
贴一张静态图:
3.自定义子组件
在上面简单记录了下在Swiper中添加子组件的方式,但是,又个问题我没有提到,就是UI问题;
我们用到某些子组件的时候,大概率是不会使用它的默认样式的,就像上面的分页器的默认样式蓝色的小圆点;
一般有以下几种方式:
-
第一个修改它的样式类,简单的可以采用这种方式;
-
第二个就是官方提供的一种解决方案,通过选择器重新绑定子组件;
就拿Pagination举例,可以通过el属性指定为你写的小组件,这个我用的比较少;简单的话也是比较好用的;
-
最后一种就是借助Swiper的事件和方法自己实现子组件的功能;
接下来实现一个比较综合性的Swiper组件;
需求:
swiper循环自动播放;
然后有分页指示器,可以手动指定Swiper的位置;(分页指示器采用第三种方式)
然后有前进/后退的功能;(前进/后退采用第二种方式)
index.tsx:
import { useState } from 'react'
import { Swiper as SwiperRc, SwiperSlide } from 'swiper/react'
import Swiper, { Autoplay, Navigation } from 'swiper'
import arrow from '../../../../assets/images/previous.svg'
import { swiperData } from '../../staticData'
import styles from './index.module.scss'
const BaseSwiper = () => {
const [activeIndex, setActiveIndex] = useState(0)
const [customSwiper, setCustomSwiper] = useState<Swiper>()
const getSwiperIns = (swiperIns: Swiper) => {
setCustomSwiper(swiperIns)
}
/**
* 通过监听slideChange滑块切换得到当前的活动索引
* @param swiperIns
*/
const handleOnSlideChange = (swiperIns: Swiper) => {
// 注意这里取的是Swiper实例的当前活动slide的真实索引,因为开启loop模式的时候,会存在两个索引,一个是当前块的真实索引;
// 一个是对应循环模式的循环索引;
setActiveIndex(swiperIns.realIndex)
}
const handleChangeIndicator = (index: number) => {
customSwiper?.slideToLoop(index)
}
const handlePrev = () => {
customSwiper?.slidePrev()
}
const handleNext = () => {
customSwiper?.slideNext()
}
return (
<div className={styles.baseSwiperWrap}>
<h2>🌈Swiper 的使用</h2>
<SwiperRc
className={styles.baseSwiperContent}
modules={[Autoplay, Navigation]}
// 自动播放
autoplay={{
delay: 2000
}}
// 循环
loop
// 前进后退
navigation={{
nextEl: 'swiper-button-next',
prevEl: 'swiper-button-prev'
}}
// 获取swiper实例
onSwiper={getSwiperIns}
onSlideChange={handleOnSlideChange}
>
{swiperData.map((item, index) => (
<SwiperSlide key={index}>
<img className={styles.baseImg} src={item.img} />
</SwiperSlide>
))}
{/* 前进 后退 */}
<div className={styles.navBox}>
<div className="swiper-button-prev" onClick={handlePrev}>
<img src={arrow} />
</div>
<div className="swiper-button-next" onClick={handleNext}>
<img src={arrow} />
</div>
</div>
</SwiperRc>
{/* 自定义指示器 */}
<ul className={styles.indicatorBox}>
{swiperData.map((item, index) => (
<li
className={[
styles.indicator,
activeIndex == index ? styles.activeindicator : ''
].join(' ')}
key={index}
onClick={() => {
handleChangeIndicator(index)
}}
></li>
))}
</ul>
</div>
)
}
export default BaseSwiper
index.module.scss:
.baseSwiperWrap {
width: 500px;
height: 500px;
h2 {
margin: 36px;
}
.baseImg {
width: 100%;
height: 400px;
object-fit: cover;
border-radius: 8px;
}
.indicatorBox {
display: flex;
justify-content: center;
}
.indicator {
width: 40px;
height: 10px;
margin: 16px;
border-radius: 10px;
background: #eeeeee;
transition: all 0.3s ease-in-out;
cursor: pointer;
}
.indicator.activeindicator {
border-radius: 20px;
background: linear-gradient(45deg, rgb(154, 254, 236), rgb(254, 186, 133));
}
.baseSwiperContent {
position: relative;
.navBox {
}
:global {
.swiper-button-next,
.swiper-button-prev {
img {
width: 50px;
}
&::after {
display: none; // 把原来的效果隐藏掉
}
}
.swiper-button-next {
img {
transform: rotate(90deg);
}
}
.swiper-button-prev {
img {
transform: rotate(-90deg);
}
}
}
}
}
实现的效果:
这里有个点提一下: 如果前进后退的逻辑写在每一个slide里面的时候,会出现前进/后退顺序混乱的问题,应该跟它的循环逻辑有关;建议不要在每一个slide上去绑前进后退的逻辑;