Swiper

2,376 阅读4分钟

在React中使用Swiper

Swiper的中文官方网站

在React中使用Swiper网站

一般项目中使用轮播图的时候,第一个想到的就是Swiper组件库,当然,如果简单的轮播切换,大多数项目中选用的 UI组件库就已经满足要求了;

这里补充一点:

当前文章安装的Swiper版本为:"swiper": "9.4.1",,多仔细看看文档;

如果出现不起作用或者不对的地方,可以从版本考虑;

还有一点是,在文档的最上头有个警告:

image.png

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:(图片来源于网络🐶)

swiper.gif

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>

贴一张静态图:

image.png

3.自定义子组件

在上面简单记录了下在Swiper中添加子组件的方式,但是,又个问题我没有提到,就是UI问题

我们用到某些子组件的时候,大概率是不会使用它的默认样式的,就像上面的分页器的默认样式蓝色的小圆点;

一般有以下几种方式:

  1. 第一个修改它的样式类,简单的可以采用这种方式;

  2. 第二个就是官方提供的一种解决方案,通过选择器重新绑定子组件;

    就拿Pagination举例,可以通过el属性指定为你写的小组件,这个我用的比较少;简单的话也是比较好用的;

  3. 最后一种就是借助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);
        }
      }
    }
  }
}

实现的效果:

swiper.gif

这里有个点提一下: 如果前进后退的逻辑写在每一个slide里面的时候,会出现前进/后退顺序混乱的问题,应该跟它的循环逻辑有关;建议不要在每一个slide上去绑前进后退的逻辑;