如何在ReactJS中通过CSS实现轮播图的弧形效果?
问题描述
我需要制作一个如下样式的轮播图:
我已经用ReactJS和CSS实现了类似效果:
但还没实现精准的弧形效果,以下是我正在开发的代码:
Carousel.js
import React, { useState } from "react"; import Slide from "../CarouselSlide/Slide"; import styles from "./Carousel.module.css"; const Carousel = ({ slides }) => { const [slideIndex, setSlideIndex] = useState(0); const handlePrevSlide = () => { setSlideIndex((prev) => (prev === 0 ? slides.length - 1 : prev - 1)); }; const handleNextSlide = () => { setSlideIndex((prev) => (prev + 1) % slides.length); }; return ( <section className={`${styles.slidesWrapper} w-full`}> <div className={styles.slides}> <button className={`${styles.prevSlideBtn} cursor-pointer`} onClick={handlePrevSlide} > <svg width="113" height="122" viewBox="0 0 113 122" fill="none" xmlns="http://www.w3.org/2000/svg" > <g filter="url(#filter0_d_429_1959)"> <circle cx="51.8531" cy="60.8531" r="20.8531" fill="#F49B00" /> <path d="M53.998 56.2952L49.4501 60.8531L53.998 65.411L52.5979 66.8111L46.6399 60.8531L52.5979 54.8951L53.998 56.2952Z" fill="white" /> </g> </svg> </button> {[...slides, ...slides, ...slides].map((slide, i) => { let offset = slides.length + (slideIndex - i); if (typeof slide === "string") { return ( <Slide image={slide.image} title={slide.title} author={slide.author} year={slide.year} offset={offset} key={i} /> ); } else { return ( <Slide image={slide.image} title={slide.title} author={slide.author} year={slide.year} offset={offset} key={i} /> ); } })} <button className={`${styles.nextSlideBtn} cursor-pointer`} onClick={handleNextSlide} > <svg width="122" height="122" viewBox="0 0 122 122" fill="none" xmlns="http://www.w3.org/2000/svg" > <g filter="url(#filter0_d_429_1955)"> <circle cx="61.147" cy="60.8531" r="20.8531" fill="#F49B00" /> <path d="M57.0161 65.411L61.5641 60.8531L57.0161 56.2952L58.4162 54.8951L64.3743 60.8531L58.4162 66.8111L57.0161 65.411Z" fill="white" /> </g> </svg> </button> </div> </section> ); }; export default Carousel;
Carousel.module.css
.slidesWrapper { @apply min-h-[630px] flex items-center justify-center overflow-hidden; } .slidesWrapper *, .slidesWrapper *::before, .slidesWrapper *::after { @apply relative; } .slides { @apply grid; } .prevSlideBtn, .nextSlideBtn { @apply appearance-none absolute z-[5] self-center; } .prevSlideBtn { @apply -left-[37rem]; } .nextSlideBtn { @apply -right-[35rem]; }
Slide.js
import { useRef, useEffect } from "react"; import CardContent from "@mui/material/CardContent"; import Typography from "@mui/material/Typography"; import CardMedia from "@mui/material/CardMedia"; import Card from "@mui/material/Card"; import styles from "./Slide.module.css"; const Slide = ({ image, title, author, year, offset }) => { return ( <div className={styles.slide} style={{ "--offset": offset, "--dir": offset === 0 ? 0 : offset > 0 ? 0.8 : -0.8, }} > <div className={styles.slideContent}> <Card sx={{ boxShadow: 8, borderRadius: 3 }}> <CardMedia component="img" alt="Self-Portrait with Grey Felt Hat" image={image} sx={{ height: 350, maxWidth: 350, objectFit: "fill", }} /> <CardContent> <Typography gutterBottom variant="subtitle2" fontWeight="bold" component="div" > {title || "Self-Portrait with Grey Felt Hat"} </Typography> <div className="flex"> <Typography variant="body2" color="text.secondary"> {author || "Vincent van Gogh"} </Typography> <Typography component="div" variant="body2" color="text.secondary" className="ml-auto" > {year || "1887"} </Typography> </div> </CardContent> </Card> </div> </div> ); }; export default Slide;
Slide.module.css
.slideContent { @apply transition-transform duration-[0.5s] ease-[ease-in-out] grid content-center rounded-[15px]; transform-style: preserve-3d; transform: perspective(3000px) translateX(calc(100% * var(--offset))) rotateY(calc(-45deg * var(--dir))); } .slide { grid-area: 1 / -1; }
我试过用:nth-child()伪类调整特定卡片高度,但没生效。
解决方案
要实现目标的弧形轮播效果,核心是让卡片在X轴位移的同时,增加Y轴位置偏移和缩放比例变化,模拟弧形排列的透视感。以下是具体修改步骤:
1. 优化Slide组件的样式变量传递
在Slide.js中,根据offset的绝对值定义层级参数,同时过滤掉超出显示范围的卡片:
const Slide = ({ image, title, author, year, offset }) => { // 只保留当前卡片前后各2张的范围,避免无效渲染 if (Math.abs(offset) > 2) return null; return ( <div className={styles.slide} style={{ "--offset": offset, "--level": Math.abs(offset), "--dir": offset > 0 ? 1 : -1, }} > <div className={styles.slideContent}> {/* 原有Card内容保持不变 */} </div> </div> ); };
2. 修改Slide.module.css实现弧形效果
更新slideContent的transform属性,加入Y轴偏移、缩放和层级控制,让弧形更自然:
.slideContent { @apply transition-transform duration-[0.5s] ease-[ease-in-out] grid content-center rounded-[15px]; transform-style: preserve-3d; /* 根据层级设置位移、旋转、缩放和Y轴偏移,模拟弧形 */ transform: perspective(3000px) translateX(calc(120% * var(--offset))) translateY(calc(20% * var(--level) * var(--level))) rotateY(calc(-30deg * var(--dir) * (2 - var(--level)))) scale(calc(1 - 0.15 * var(--level))); /* 层级越高的卡片层级越低,避免遮挡 */ z-index: calc(10 - var(--level)); opacity: calc(1 - 0.1 * var(--level)); } .slide { grid-area: 1 / -1; }
3. 简化Carousel的卡片渲染逻辑
替换原有的三次重复渲染逻辑,只渲染当前卡片前后的有效范围:
// 替换原map代码,只渲染当前slide前后各2张卡片 {slides.map((_, idx) => { const realIndex = (slideIndex + idx - 2 + slides.length) % slides.length; const offset = idx - 2; const slide = slides[realIndex]; return ( <Slide image={slide.image} title={slide.title} author={slide.author} year={slide.year} offset={offset} key={realIndex} /> ); })}
参数调整说明
- 修改
translateY的数值可以改变弧形弯曲程度,数值越大弧度越明显 - 调整
scale的参数可以控制非当前卡片的缩小比例 opacity参数可调整侧边卡片的透明度,增强层次感
内容的提问来源于stack exchange,提问作者Mian Muhammad
相关产品推荐
相关产品推荐

