Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4fa640318a |
@@ -1,6 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { memo } from "react";
|
"use client"
|
||||||
|
|
||||||
|
import { memo, useState, useRef, useEffect } from "react";
|
||||||
import CardStack from "@/components/cardStack/CardStack";
|
import CardStack from "@/components/cardStack/CardStack";
|
||||||
import MediaContent from "@/components/shared/MediaContent";
|
import MediaContent from "@/components/shared/MediaContent";
|
||||||
import { cls } from "@/lib/utils";
|
import { cls } from "@/lib/utils";
|
||||||
@@ -86,8 +88,34 @@ const TestimonialCard = memo(({
|
|||||||
roleClassName = "",
|
roleClassName = "",
|
||||||
companyClassName = "",
|
companyClassName = "",
|
||||||
}: TestimonialCardProps) => {
|
}: TestimonialCardProps) => {
|
||||||
|
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
||||||
|
const [scrollY, setScrollY] = useState(0);
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => setScrollY(window.scrollY);
|
||||||
|
window.addEventListener("scroll", handleScroll);
|
||||||
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!cardRef.current) return;
|
||||||
|
const rect = cardRef.current.getBoundingClientRect();
|
||||||
|
setMousePosition({
|
||||||
|
x: e.clientX - rect.left,
|
||||||
|
y: e.clientY - rect.top,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const parallaxOffset = scrollY * 0.05;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls("relative h-full aspect-[8/10] rounded-theme-capped overflow-hidden group", cardClassName)} style={{animation: "cardEntrance 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards"}}>
|
<div
|
||||||
|
ref={cardRef}
|
||||||
|
className={cls("relative h-full aspect-[8/10] rounded-theme-capped overflow-hidden group", cardClassName)}
|
||||||
|
style={{animation: "cardEntrance 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards"}}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
>
|
||||||
<style>{`
|
<style>{`
|
||||||
@keyframes cardEntrance {
|
@keyframes cardEntrance {
|
||||||
from {
|
from {
|
||||||
@@ -107,11 +135,82 @@ const TestimonialCard = memo(({
|
|||||||
box-shadow: 0 0 40px rgba(var(--accent-rgb), 0.5), 0 25px 50px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 40px rgba(var(--accent-rgb), 0.5), 0 25px 50px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@keyframes rotateBorder {
|
||||||
|
from {
|
||||||
|
background-position: 0% 0%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-position: 360% 360%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes colorShift {
|
||||||
|
0%, 100% {
|
||||||
|
filter: hue-rotate(0deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
filter: hue-rotate(15deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes sparkle {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes subtleBounce {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
.group:hover .testimonial-inner {
|
.group:hover .testimonial-inner {
|
||||||
animation: glowPulse 2s ease-in-out infinite;
|
animation: glowPulse 2s ease-in-out infinite, colorShift 3s ease-in-out infinite, subtleBounce 0.6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.group:hover .rotating-border {
|
||||||
|
animation: rotateBorder 3s linear infinite;
|
||||||
|
}
|
||||||
|
.sparkle-particle {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.sparkle-particle.active {
|
||||||
|
animation: sparkle 0.8s ease-out forwards;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<div className="testimonial-inner relative w-full h-full transition-all duration-500 ease-out group-hover:shadow-2xl group-hover:-translate-y-12 group-hover:scale-105 group-hover:[transform:perspective(1000px)_rotateX(2deg)_rotateY(-3deg)_translateY(-48px)_scale(1.05)]" style={{}}>
|
<div
|
||||||
|
className="testimonial-inner relative w-full h-full transition-all duration-500 ease-out group-hover:shadow-2xl group-hover:-translate-y-12 group-hover:scale-105 group-hover:[transform:perspective(1000px)_rotateX(2deg)_rotateY(-3deg)_translateY(-48px)_scale(1.05)]"
|
||||||
|
style={{transform: `translateY(${parallaxOffset}px)`}}
|
||||||
|
>
|
||||||
|
{/* Rotating border effect */}
|
||||||
|
<div
|
||||||
|
className="rotating-border absolute inset-0 rounded-theme-capped pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
style={{
|
||||||
|
background: "conic-gradient(from 0deg, rgba(var(--accent-rgb), 0.5), rgba(var(--accent-rgb), 0.1), rgba(var(--accent-rgb), 0.5))",
|
||||||
|
backgroundSize: "200% 200%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Sparkle particles */}
|
||||||
|
{[...Array(6)].map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="sparkle-particle active"
|
||||||
|
style={{
|
||||||
|
left: `${mousePosition.x}px`,
|
||||||
|
top: `${mousePosition.y}px`,
|
||||||
|
width: "8px",
|
||||||
|
height: "8px",
|
||||||
|
background: "radial-gradient(circle, rgba(var(--accent-rgb), 0.8), transparent)",
|
||||||
|
borderRadius: "50%",
|
||||||
|
transform: `translate(-50%, -50%) rotate(${(i / 6) * 360}deg) translateX(20px)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{/* Front of card */}
|
{/* Front of card */}
|
||||||
<div className="absolute w-full h-full">
|
<div className="absolute w-full h-full">
|
||||||
<MediaContent
|
<MediaContent
|
||||||
|
|||||||
Reference in New Issue
Block a user