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.
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:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Swiper demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
<!-- Link Swiper's CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css" />
<!-- Demo styles -->
<style>
html,
body {
position: relative;
height: 100%;
}
body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color: #000;
margin: 0;
padding: 0;
}
.swiper {
width: 100%;
padding-top: 50px;
padding-bottom: 50px;
}
.swiper-slide {
background-position: center;
background-size: cover;
width: 100px;
height: 100px;
}
.swiper-slide img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<!-- Swiper -->
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-1.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-2.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-3.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-4.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-5.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-6.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-7.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-8.jpg" />
</div>
<div class="swiper-slide">
<img src="https://swiperjs.com/demos/images/nature-9.jpg" />
</div>
</div>
<div class="swiper-pagination"></div>
</div>
<!-- Swiper JS -->
<script src="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js"></script>
<!-- Initialize Swiper -->
<script>
var swiper = new Swiper(".mySwiper", {
effect: "coverflow",
grabCursor: true,
centeredSlides: true,
slidesPerView: "auto",
spaceBetween: 10,
loop: true,
loopedSlides: 8,
coverflowEffect: {
rotate: 80,
depth: -100,
modifier: .2,
scale: 1.2,
},
pagination: {
el: ".swiper-pagination",
clickable: true,
},
});
</script>
</body>
</html>
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.
.container {
height: 200px;
transform-style: preserve-3d;
perspective-origin: 600px center;
perspective: 800px;
margin-top: 50px;
}
.card {
border: solid 1px blue;
background-color: lightblue;
border-radius: 10px;
height: 200px;
width: 200px;
position: absolute;
transform-style: preserve-3d;
transform: rotateY(var(--angle));
transform-origin: center 0 1200px;
left: 500px;
}
#c1 {
--angle: 20deg;
}
#c2 {
--angle: 10deg;
}
#c3 {
--angle: 0deg;
}
#c4 {
--angle: -10deg;
}
#c5 {
--angle: -20deg;
}
<div class="container">
<div class="card" id="c1">CARD 1</div>
<div class="card" id="c2">CARD 2</div>
<div class="card" id="c3">CARD 3</div>
<div class="card" id="c4">CARD 4</div>
<div class="card" id="c5">CARD 5</div>
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With