React.js中如何在Swiper组件外实现首尾导航按钮禁用?
实现自定义Swiper导航按钮的禁用逻辑
要实现和原生Swiper导航一致的禁用逻辑,你可以通过React状态管理按钮的禁用状态,并结合Swiper的回调事件来更新状态,具体实现如下:
- 添加状态变量:用
useState定义左右按钮的禁用状态,初始时左侧按钮默认禁用,右侧按钮根据总幻灯片数量和每页显示数判断是否需要禁用。 - 绑定Swiper回调事件:通过
onInit初始化状态,onSlideChange在幻灯片切换时实时更新按钮禁用状态。 - 关联状态与按钮:将状态值绑定到
IconButton的disabled属性上,并添加禁用状态的样式优化。
修改后的完整代码:
import { useState, useRef } from 'react'; import { Container, Stack, IconButton, Box, Typography, Button } from '@mui/material'; import { ArrowBackRounded, ArrowForwardRounded, ArrowCircleRightRounded } from '@mui/icons-material'; import { styled } from '@mui/material/styles'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Autoplay } from 'swiper/modules'; import { useStaticQuery, graphql } from 'gatsby'; import GatsbyImage from 'gatsby-plugin-image'; import EicrIcon from './path-to-eicr-icon'; // 替换为你的图标实际路径 const Services = () => { const swiperRef = useRef(); const [isLeftDisabled, setIsLeftDisabled] = useState(true); const [isRightDisabled, setIsRightDisabled] = useState(false); const data = useStaticQuery(graphql` query HomeServicesQuery { allFile( filter: { sourceInstanceName: { eq: "services" } } sort: { childMdx: { frontmatter: { id: ASC } } } ) { nodes { childMdx { frontmatter { title slideImage { childImageSharp { gatsbyImageData } } description } } } } } `); const StyledSwiper = styled(Swiper)(({ theme }) => ({ "& .swiper-wrapper": { marginBottom: "20px", }, })); const servicesData = data?.allFile?.nodes; const totalSlides = servicesData?.length || 0; const slidesPerView = 3; // 初始化Swiper时设置按钮状态 const handleSwiperInit = (swiper) => { swiperRef.current = swiper; // 总幻灯片数不足一页时,直接禁用右侧按钮 setIsRightDisabled(totalSlides <= slidesPerView); }; // 幻灯片切换时更新按钮禁用状态 const handleSlideChange = (swiper) => { setIsLeftDisabled(swiper.isBeginning); setIsRightDisabled(swiper.isEnd); }; return ( <Container sx={{ mt: 10 }}> <Stack direction="row" alignItems="center"> <IconButton color="secondary" onClick={() => swiperRef.current?.slidePrev()} disabled={isLeftDisabled} sx={{ border: "1px solid", borderColor: "border.main", marginRight: "20px", "&:hover": { backgroundColor: "primary.main", color: "white", }, "&.Mui-disabled": { opacity: 0.5, cursor: "not-allowed", }, }} > <ArrowBackRounded /> </IconButton> <StyledSwiper modules={[Autoplay]} spaceBetween={25} slidesPerView={slidesPerView} onInit={handleSwiperInit} onSlideChange={handleSlideChange} autoplay > {servicesData?.map((item, index) => ( <SwiperSlide key={index}> <Box> <GatsbyImage image={ item.childMdx.frontmatter.slideImage.childImageSharp .gatsbyImageData } alt={item.childMdx.frontmatter.title} /> </Box> <Box sx={{ margin: "-60px 0 0 45px", backgroundColor: "white", zIndex: 1, position: "relative", padding: "35px 30px 30px", boxShadow: "0 0 20px 0 rgba(6, 22, 58, 0.11)", clipPath: "inset(0px 0px -25px -25px)", }} > <img src={EicrIcon} style={{ width: "50px", marginBottom: "20px" }} alt="" /> <Typography sx={{ fontSize: 22, }} component="h3" variant="h5" color="secondary" > {item.childMdx.frontmatter.title} </Typography> <Typography paragraph my={1} sx={{ color: "text.main", ...theme.typography.body2, mb: 2 }} > {item?.childMdx?.frontmatter?.description?.length > 50 ? item.childMdx.frontmatter.description.slice(0, 47) + "..." : item.childMdx.frontmatter.description} </Typography> <Button disableRipple endIcon={ <ArrowCircleRightRoundedIcon sx={{ fontSize: "16px!important" }} /> } variant="black-text" > Learn More </Button> </Box> </SwiperSlide> ))} </StyledSwiper> <IconButton color="secondary" onClick={() => swiperRef.current?.slideNext()} disabled={isRightDisabled} sx={{ border: "1px solid", borderColor: "border.main", marginLeft: "20px", "&:hover": { backgroundColor: "primary.main", color: "white", }, "&.Mui-disabled": { opacity: 0.5, cursor: "not-allowed", }, }} > <ArrowForwardRounded /> </IconButton> </Stack> </Container> ); }; export default Services;
关键说明:
- 直接使用Swiper实例自带的
isBeginning和isEnd属性判断首尾页,无需手动计算索引,逻辑更可靠 - 初始化时判断总幻灯片数是否小于等于每页显示数,避免出现无意义的导航按钮
- 添加禁用状态的样式调整,让用户能直观感知按钮状态
内容的提问来源于stack exchange,提问作者Imran Kabir
相关产品推荐
相关产品推荐

