michaelpeskov/components/FeatureCards.tsx

208 lines
6.4 KiB
TypeScript

'use client'
import { useEffect, useRef } from 'react'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import Image from 'next/image'
if (typeof window !== 'undefined') {
gsap.registerPlugin(ScrollTrigger)
}
interface Feature {
title: string
description: string
image: string
icon: string
}
const features: Feature[] = [
{
title: "Close-Up Magic",
description: "Intimate table-to-table performances that break the ice and create unforgettable moments for your guests.",
image: "https://images.eventpeppers.com/sites/default/files/imagecache/lightbox-preview/images/13234/michael-peskov-magier-taschendieb-450253.jpeg",
icon: "✨"
},
{
title: "Stage Shows",
description: "Grand illusions and interactive performances that captivate entire audiences with wonder and amazement.",
image: "https://images.eventpeppers.com/sites/default/files/imagecache/lightbox-preview/images/13234/michael-peskov-magier-taschendieb-450255.jpeg",
icon: "🎭"
},
{
title: "Pickpocket Act",
description: "Masterful sleight of hand that entertains while teaching guests how to protect themselves from real pickpockets.",
image: "https://images.eventpeppers.com/sites/default/files/imagecache/lightbox-preview/images/13234/michael-peskov-magier-taschendieb-450254.jpeg",
icon: "🎩"
},
{
title: "Corporate Events",
description: "Professional entertainment that elevates business gatherings and creates memorable experiences for clients.",
image: "https://images.eventpeppers.com/sites/default/files/imagecache/lightbox-preview/images/13234/michael-peskov-magier-taschendieb-450256.jpeg",
icon: "💼"
}
]
export default function FeatureCards() {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const container = containerRef.current
if (!container) return
// Check for reduced motion
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const ctx = gsap.context(() => {
const cards = gsap.utils.toArray<HTMLElement>('.feature-card')
cards.forEach((card, i) => {
const image = card.querySelector('.feature-image')
const content = card.querySelector('.feature-content')
if (prefersReducedMotion) {
// Simple fade for reduced motion
gsap.from(card, {
opacity: 0,
y: 30,
duration: 0.6,
delay: i * 0.1,
scrollTrigger: {
trigger: card,
start: 'top 80%',
toggleActions: 'play none none reverse'
}
})
} else {
// Full animation with perspective and clip-mask
gsap.set(card, {
rotateX: 15,
rotateY: 5,
transformPerspective: 1000,
transformOrigin: 'center center'
})
gsap.set(image, {
clipPath: 'inset(0 0 100% 0)'
})
gsap.set(content, {
opacity: 0,
y: 50
})
const tl = gsap.timeline({
scrollTrigger: {
trigger: card,
start: 'top 75%',
toggleActions: 'play none none reverse'
}
})
tl.to(card, {
rotateX: 0,
rotateY: 0,
duration: 0.8,
ease: 'power2.out'
})
.to(image, {
clipPath: 'inset(0 0 0% 0)',
duration: 0.6,
ease: 'power2.out'
}, '-=0.6')
.to(content, {
opacity: 1,
y: 0,
duration: 0.6,
ease: 'power2.out'
}, '-=0.4')
// Hover effects
const handleMouseEnter = () => {
gsap.to(card, {
rotateX: -5,
rotateY: 5,
scale: 1.02,
duration: 0.3,
ease: 'power2.out'
})
gsap.to(image, {
scale: 1.1,
duration: 0.3,
ease: 'power2.out'
})
}
const handleMouseLeave = () => {
gsap.to(card, {
rotateX: 0,
rotateY: 0,
scale: 1,
duration: 0.3,
ease: 'power2.out'
})
gsap.to(image, {
scale: 1,
duration: 0.3,
ease: 'power2.out'
})
}
card.addEventListener('mouseenter', handleMouseEnter)
card.addEventListener('mouseleave', handleMouseLeave)
}
})
}, container)
return () => ctx.revert()
}, [])
return (
<section ref={containerRef} className="py-32 px-6 bg-slate-900">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-20">
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">
Magical Experiences
</h2>
<p className="text-xl text-gray-300 max-w-3xl mx-auto">
From intimate gatherings to grand celebrations, discover the perfect magical experience for your event.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{features.map((feature, i) => (
<div
key={i}
className="feature-card group relative bg-slate-800/50 rounded-2xl overflow-hidden backdrop-blur-sm border border-slate-700/50"
style={{ willChange: 'transform' }}
>
<div className="feature-image relative h-64 overflow-hidden">
<Image
src={feature.image}
alt={feature.title}
fill
className="object-cover transition-transform duration-300"
style={{ willChange: 'transform' }}
/>
<div className="absolute inset-0 bg-gradient-to-t from-slate-900/80 to-transparent" />
<div className="absolute top-4 left-4 text-3xl">
{feature.icon}
</div>
</div>
<div className="feature-content p-8">
<h3 className="text-2xl font-bold text-white mb-4">
{feature.title}
</h3>
<p className="text-gray-300 leading-relaxed">
{feature.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>
)
}