fix : added animation and mobile view in testimonial section

This commit is contained in:
hardik 2025-08-02 12:05:48 +00:00
parent e863382af9
commit 0a31567b38
3 changed files with 437 additions and 116 deletions

View File

@ -1,33 +1,67 @@
import { Box, Typography, Button } from "@mui/material"; import { Box, Typography, Button } from "@mui/material";
import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward"; import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward";
import { motion } from "framer-motion"; import { motion, Variants, Transition } from "framer-motion";
const AboutUs = () => { const AboutUs = () => {
// Animation variants // Type-safe animation variants
const containerVariants = { const containerVariants: Variants = {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
visible: { visible: {
opacity: 1, opacity: 1,
transition: { transition: {
staggerChildren: 0.2, staggerChildren: 0.3,
duration: 0.6, duration: 0.8,
}, },
}, },
}; };
const itemVariants = { const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 }, hidden: { y: 30, opacity: 0 },
visible: { visible: {
y: 0, y: 0,
opacity: 1, opacity: 1,
transition: { transition: {
duration: 0.6, duration: 0.8,
ease: "easeOut" as const, ease: [0.16, 1, 0.3, 1], // Cubic bezier curve for easeOut
},
},
};
const titleVariants: Variants = {
...itemVariants,
hover: {
scale: 1.02,
transition: {
repeat: Infinity,
repeatType: "reverse",
duration: 1.5,
} as Transition,
},
};
const underlineVariants: Variants = {
hidden: { width: 0 },
visible: {
width: "80px",
transition: {
delay: 0.5,
duration: 0.8,
}, },
}, },
hover: { hover: {
scale: 1.02, width: "120px",
transition: { duration: 0.3 }, backgroundColor: "#00e1ff",
},
};
const buttonContainerVariants: Variants = {
...itemVariants,
hover: {
scale: 1.05,
transition: {
type: "spring",
stiffness: 400,
},
}, },
}; };
@ -42,46 +76,57 @@ const AboutUs = () => {
color: "#fff", color: "#fff",
py: { xs: 8, md: 12 }, py: { xs: 8, md: 12 },
px: 3, px: 3,
overflow: "hidden",
}} }}
> >
<Box <Box
component={motion.div} component={motion.div}
variants={containerVariants} variants={containerVariants}
initial="hidden" initial="hidden"
whileInView="visible" animate="visible"
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true }}
textAlign="center" sx={{
maxWidth={800} textAlign: "center",
mx="auto" maxWidth: 800,
mx: "auto",
}}
> >
<Box component={motion.div} variants={itemVariants} whileHover="hover"> {/* Title Section */}
<Box
component={motion.div}
variants={titleVariants}
whileHover="hover"
sx={{ mb: 4 }}
>
<Typography <Typography
variant="h2" variant="h2"
gutterBottom
sx={{ sx={{
fontFamily: "'Cormorant Garamond', serif", fontFamily: "'Cormorant Garamond', serif",
fontWeight: 600, fontWeight: 600,
fontSize: { xs: "2.5rem", md: "3rem" }, fontSize: { xs: "2.5rem", md: "3rem" },
letterSpacing: "1px", letterSpacing: "1px",
mb: 4,
position: "relative", position: "relative",
"&:after": { textShadow: "0 0 8px rgba(0, 225, 255, 0.3)",
content: '""',
display: "block",
width: "80px",
height: "3px",
background: "linear-gradient(90deg, #00FFD1, transparent)",
margin: "20px auto 0",
borderRadius: "2px",
},
}} }}
> >
About Us About Us
<Box
component={motion.div}
variants={underlineVariants}
initial="hidden"
animate="visible"
whileHover="hover"
sx={{
height: "3px",
background: "linear-gradient(90deg, #00e1ff, transparent)",
margin: "20px auto 0",
borderRadius: "2px",
}}
/>
</Typography> </Typography>
</Box> </Box>
<Box component={motion.div} variants={itemVariants}> {/* Content Section */}
<Box component={motion.div} variants={itemVariants} sx={{ mb: 4 }}>
<Typography <Typography
paragraph paragraph
sx={{ sx={{
@ -89,41 +134,67 @@ const AboutUs = () => {
fontSize: { xs: "1rem", md: "1.1rem" }, fontSize: { xs: "1rem", md: "1.1rem" },
lineHeight: 1.9, lineHeight: 1.9,
fontWeight: 300, fontWeight: 300,
mb: 4,
color: "rgba(255,255,255,0.85)", color: "rgba(255,255,255,0.85)",
}} }}
> >
Lorem ipsum dolor sit amet consectetur. Nisl sed sed tortor eros. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum
Enim hendrerit elit interdum malesuada sit dis est pharetra repellendus aperiam, nobis debitis nam nemo dolorum! Suscipit
suspendisse. In dolor venenatis ultricies aliquet lacinia. Faucibus voluptatibus eum tenetur accusantium eveniet iure, optio illum
commodo eu gravida auctor eu et sit ut. Sed praesent sed odio minima, quo mollitia vitae dolorem.
aliquam in fermentum. Sit vitae morbi sodales sem velit eu tempus
velit nunc. Turpis in in luctus et nulla purus nibh ut. Enim sed
fermentum purus molestie parturient morbi nunc aliquet. Aliquam sed
etiam turpis non lectus commodo cras leo gravida.
</Typography> </Typography>
</Box> </Box>
<Box component={motion.div} variants={itemVariants} whileHover="hover"> {/* Button Section */}
<Box
component={motion.div}
variants={buttonContainerVariants}
whileHover="hover"
>
<Button <Button
variant="outlined" variant="outlined"
endIcon={<ArrowOutwardIcon />} endIcon={
<motion.div
animate={{ x: [0, 5, 0] }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: "easeInOut",
}}
>
<ArrowOutwardIcon />
</motion.div>
}
sx={{ sx={{
mt: 2, borderColor: "#00e1ff",
borderColor: "#00FFD1", color: "#00e1ff",
color: "#00FFD1",
borderRadius: "30px", borderRadius: "30px",
fontWeight: 500, fontWeight: 500,
px: 4, px: 3,
py: 1.5, py: 1.1,
fontFamily: "'Montserrat', sans-serif", fontFamily: "'Montserrat', sans-serif",
letterSpacing: "1px", letterSpacing: "1px",
fontSize: "0.9rem", fontSize: "0.9rem",
transition: "all 0.3s ease", textTransform: "uppercase",
position: "relative",
overflow: "hidden",
"&:hover": { "&:hover": {
backgroundColor: "#00FFD1", backgroundColor: "rgba(0, 225, 255, 0.1)",
color: "#000", boxShadow: "0 0 15px rgba(0, 225, 255, 0.4)",
boxShadow: "0 5px 15px rgba(0, 255, 209, 0.4)", "&::before": {
transform: "translateX(0)",
},
},
"&::before": {
content: '""',
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
background:
"linear-gradient(90deg, transparent, rgba(0, 225, 255, 0.2), transparent)",
transform: "translateX(-100%)",
transition: "transform 0.6s ease",
}, },
}} }}
> >

View File

@ -192,8 +192,8 @@ const OurWorks = () => {
variant="outlined" variant="outlined"
sx={{ sx={{
borderRadius: "20px", borderRadius: "20px",
border: "1px solid #00FFD1", border: "1px solid #00E0FF",
color: "#00FFD1", color: "#00E0FF",
px: 4, px: 4,
py: 1, py: 1,
fontWeight: 600, fontWeight: 600,
@ -202,8 +202,8 @@ const OurWorks = () => {
letterSpacing: 0.5, letterSpacing: 0.5,
fontFamily: "'Montserrat', sans-serif", fontFamily: "'Montserrat', sans-serif",
"&:hover": { "&:hover": {
backgroundColor: "rgba(0, 255, 209, 0.1)", backgroundColor: "rgba(0, 225, 255, 0.19)",
border: "1px solid #00FFD1", border: "1px solid #00e1ff",
}, },
transition: "all 0.3s ease", transition: "all 0.3s ease",
}} }}

View File

@ -3,98 +3,348 @@ import {
Box, Box,
Card, Card,
CardContent, CardContent,
Grid,
Typography, Typography,
IconButton,
} from "@mui/material"; } from "@mui/material";
import { motion, AnimatePresence, useAnimation, Variants } from "framer-motion";
import { useEffect, useState } from "react";
import { KeyboardArrowLeft, KeyboardArrowRight } from "@mui/icons-material";
import testimonialImg from "../testomonials/b7bef0298c881712fbc6437106d2aaef27c4fad8.jpg"; import testimonialImg from "../testomonials/b7bef0298c881712fbc6437106d2aaef27c4fad8.jpg";
const testimonials = [ const testimonials = [
{ {
name: "Name", name: "Sarah Johnson",
role: "Role", role: "Marketing Director",
company: "Company Name & Logo", company: "Tech Innovations Inc.",
feedback: feedback: "Working with this team transformed our digital presence.",
"Lorem ipsum dolor sit amet consectetur. Vitae dictum quam senectus posuere sit justo est turpis interdum. Ut vitae platea et adipiscing nisl.",
rating: 5, rating: 5,
}, },
{ {
name: "John Dae", name: "Michael Chen",
role: "Sr Manager", role: "Sr. Manager",
company: "Wells Fargo", company: "Wells Fargo",
feedback: feedback: "Exceptional service from start to finish.",
"Lorem ipsum dolor sit amet consectetur. Vitae dictum quam senectus posuere sit justo est turpis interdum. Ut vitae platea et adipiscing nisl.",
rating: 5, rating: 5,
}, },
{ {
name: "Name", name: "Emily Rodriguez",
role: "Role", role: "Product Owner",
company: "Company Name & Logo", company: "Nexus Enterprises",
feedback: feedback: "The team's technical expertise was outstanding.",
"Lorem ipsum dolor sit amet consectetur. Vitae dictum quam senectus posuere sit justo est turpis interdum. Ut vitae platea et adipiscing nisl.",
rating: 4, rating: 4,
}, },
]; ];
// Properly typed animation variants
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
duration: 0.8,
},
},
};
const itemVariants: Variants = {
hidden: { y: 50, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.6,
ease: "easeOut", // Using string literal instead of array
},
},
hover: {
y: -10,
transition: { duration: 0.3 },
},
};
const Testimonials = () => { const Testimonials = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const [isMobile, setIsMobile] = useState(false);
const controls = useAnimation();
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 600);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
if (!isMobile) return;
const timer = setInterval(() => {
handleNext();
}, 5000);
return () => clearInterval(timer);
}, [currentIndex, isMobile]);
const handleNext = () => {
controls
.start({
x: "-100%",
opacity: 0,
transition: { duration: 0.3 },
})
.then(() => {
setCurrentIndex((prev) => (prev + 1) % testimonials.length);
controls
.start({
x: "100%",
opacity: 0,
})
.then(() => {
controls.start({
x: 0,
opacity: 1,
transition: { duration: 0.5 },
});
});
});
};
const handlePrev = () => {
controls
.start({
x: "100%",
opacity: 0,
transition: { duration: 0.3 },
})
.then(() => {
setCurrentIndex(
(prev) => (prev - 1 + testimonials.length) % testimonials.length
);
controls
.start({
x: "-100%",
opacity: 0,
})
.then(() => {
controls.start({
x: 0,
opacity: 1,
transition: { duration: 0.5 },
});
});
});
};
return ( return (
<Box sx={{ backgroundColor: "#1d2733", color: "#fff", py: 10, px: 3 }}> <Box
<Box textAlign="center" mb={6}> sx={{
<Typography variant="h4" fontWeight="bold" gutterBottom> backgroundColor: "#1d2733",
color: "#fff",
py: { xs: 6, md: 10 },
px: { xs: 2, md: 3 },
overflow: "hidden",
}}
>
{/* Header Section */}
<Box
component={motion.div}
initial="hidden"
animate="visible"
variants={containerVariants}
textAlign="center"
mb={{ xs: 4, md: 6 }}
sx={{ maxWidth: 800, mx: "auto" }}
>
<Typography variant="h3" fontWeight="bold" gutterBottom>
Testimonials Testimonials
</Typography> </Typography>
<Typography variant="subtitle1"> <Typography variant="subtitle1">
Dont just take our word for it -- hear directly from our clients Don't just take our word for it
</Typography> </Typography>
</Box> </Box>
<Grid container spacing={4} justifyContent="center"> {/* Mobile Carousel */}
{testimonials.map((t, idx) => ( {isMobile ? (
<Grid item xs={12} sm={6} md={4} key={idx}> <Box sx={{ position: "relative", height: 450 }}>
<Card <AnimatePresence>
<Box
component={motion.div}
key={currentIndex}
animate={controls}
initial={{ x: 0, opacity: 1 }}
exit={{ x: 0, opacity: 0 }}
sx={{ sx={{
borderRadius: 3, position: "absolute",
maxWidth: 320, width: "100%",
margin: "0 auto", display: "flex",
textAlign: "center", justifyContent: "center",
py: 3,
}} }}
> >
<Avatar <TestimonialCard testimonial={testimonials[currentIndex]} />
src={testimonialImg} </Box>
alt={t.name} </AnimatePresence>
sx={{ width: 100, height: 100, margin: "0 auto", mb: 2 }}
/> <NavigationArrows handlePrev={handlePrev} handleNext={handleNext} />
<CardContent> </Box>
<Typography variant="subtitle1" fontWeight="bold"> ) : (
{t.name} ,{" "} /* Desktop Grid */
<span style={{ fontWeight: "normal" }}>{t.role}</span> <Box
</Typography> component={motion.div}
<Typography variant="body2" color="text.secondary" mb={1}> initial="hidden"
{t.company} animate="visible"
</Typography> variants={containerVariants}
<Typography variant="body2" color="text.secondary" mb={2}> sx={{
{t.feedback} display: "flex",
</Typography> justifyContent: "center",
<Box> flexWrap: "wrap",
{Array.from({ length: 5 }).map((_, i) => ( gap: 4,
<span px: { md: 4 },
key={i} }}
style={{ >
color: i < t.rating ? "#00FFD1" : "#ccc", {testimonials.map((t, idx) => (
fontSize: "1.2rem", <Box
}} key={idx}
> component={motion.div}
variants={itemVariants}
</span> whileHover="hover"
))} sx={{
</Box> width: { md: 350 },
</CardContent> flex: { md: "1 1 300px" },
</Card> }}
</Grid> >
))} <TestimonialCard testimonial={t} />
</Grid> </Box>
))}
</Box>
)}
</Box> </Box>
); );
}; };
// Extracted Card Component
const TestimonialCard = ({
testimonial,
}: {
testimonial: (typeof testimonials)[0];
}) => (
<Card
sx={{
borderRadius: 3,
width: "100%",
minHeight: 400,
textAlign: "center",
py: 3,
px: 2,
background: "linear-gradient(145deg, #1d2733, #232f3e)",
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
border: "1px solid rgba(0, 225, 255, 0.1)",
transition: "all 0.3s ease",
"&:hover": {
boxShadow: "0 12px 40px rgba(0, 225, 255, 0.15)",
borderColor: "rgba(0, 225, 255, 0.3)",
transform: "translateY(-5px)",
},
}}
>
<Avatar
src={testimonialImg}
alt={testimonial.name}
sx={{
width: 80,
height: 80,
margin: "0 auto",
mb: 2,
border: "2px solid #00e1ff",
boxShadow: "0 0 15px rgba(0, 225, 255, 0.4)",
}}
/>
<CardContent>
<Typography variant="h6" fontWeight="bold" color="#00e1ff">
{testimonial.name}
</Typography>
<Typography variant="subtitle1" color="rgba(255,255,255,0.8)" mb={0.5}>
{testimonial.role}
</Typography>
<Typography variant="body2" color="rgba(255,255,255,0.6)" mb={2}>
{testimonial.company}
</Typography>
<Typography
variant="body1"
color="rgba(255,255,255,0.8)"
mb={3}
fontStyle="italic"
>
{testimonial.feedback}
</Typography>
<Box>
{Array.from({ length: 5 }).map((_, i) => (
<Box
component="span"
key={i}
sx={{
color:
i < testimonial.rating ? "#00e1ff" : "rgba(255,255,255,0.2)",
fontSize: "1.5rem",
mx: 0.5,
textShadow:
i < testimonial.rating
? "0 0 8px rgba(0, 225, 255, 0.5)"
: "none",
}}
>
</Box>
))}
</Box>
</CardContent>
</Card>
);
// Extracted Navigation Component
const NavigationArrows = ({
handlePrev,
handleNext,
}: {
handlePrev: () => void;
handleNext: () => void;
}) => (
<>
<IconButton
onClick={handlePrev}
sx={{
position: "absolute",
left: 10,
top: "50%",
transform: "translateY(-50%)",
color: "#00e1ff",
backgroundColor: "rgba(0, 225, 255, 0.1)",
"&:hover": {
backgroundColor: "rgba(0, 225, 255, 0.2)",
},
}}
>
<KeyboardArrowLeft fontSize="large" />
</IconButton>
<IconButton
onClick={handleNext}
sx={{
position: "absolute",
right: 10,
top: "50%",
transform: "translateY(-50%)",
color: "#00e1ff",
backgroundColor: "rgba(0, 225, 255, 0.1)",
"&:hover": {
backgroundColor: "rgba(0, 225, 255, 0.2)",
},
}}
>
<KeyboardArrowRight fontSize="large" />
</IconButton>
</>
);
export default Testimonials; export default Testimonials;