I need to make a carousel which look like this
I have been able to achieve something like this with reactjs and css
But I have not been able to get the exact curve effect. Here is the code for the carousel I have been working on.
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];
}
Code for a Slide
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;
}
I tried adjusting the height of specific slides using The :nth-child()
CSS pseudo-class. But it doesn’t seem to be working.
3
Answers
I will not give you a ready code, but will try to explain you the problem.
This image shows what you want to achieve
The pink lines are your Slides, the green dotted circle is a Carousel. For each Slide you need to calculate:
tan alpha = delta z / delta x
(this is not exact value, but for bigger circle radiuses, the error will be very small)When you have this, the css implementation will be easy, just transform X by delta X, transform Z by delta Z and rotate Y by alpha angle
Your problem is not about react, it is just about CSS transforms.
Take a look at the below implementation.
You can play with the perspective value to adjust the final look.
The Swiper Slider has a similar effect. You can change the angle or depth in
coverflowEffect
option. I pasted and slightly modified the basic example: