175 lines
6.1 KiB
TypeScript
175 lines
6.1 KiB
TypeScript
'use client'
|
|
import { useEffect, useRef } from 'react'
|
|
import { gsap } from 'gsap'
|
|
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
|
import { animateSplit } from '@/lib/animateSplit'
|
|
import ParallaxLayer from './ParallaxLayer'
|
|
import MagneticButton from './MagneticButton'
|
|
import Image from 'next/image'
|
|
|
|
if (typeof window !== 'undefined') {
|
|
gsap.registerPlugin(ScrollTrigger)
|
|
}
|
|
|
|
export default function ScrollHero() {
|
|
const heroRef = useRef<HTMLDivElement>(null)
|
|
const titleRef = useRef<HTMLHeadingElement>(null)
|
|
const subtitleRef = useRef<HTMLParagraphElement>(null)
|
|
const scrollHintRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
const hero = heroRef.current
|
|
const title = titleRef.current
|
|
const subtitle = subtitleRef.current
|
|
const scrollHint = scrollHintRef.current
|
|
|
|
if (!hero || !title || !subtitle || !scrollHint) return
|
|
|
|
// Check for reduced motion
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
|
|
const ctx = gsap.context(() => {
|
|
// Initial setup
|
|
gsap.set([title, subtitle, scrollHint], { opacity: 0 })
|
|
|
|
if (prefersReducedMotion) {
|
|
// Simple fade in for reduced motion
|
|
const tl = gsap.timeline({ delay: 0.5 })
|
|
tl.to(title, { opacity: 1, y: 0, duration: 0.8, ease: 'power2.out' })
|
|
.to(subtitle, { opacity: 1, y: 0, duration: 0.6, ease: 'power2.out' }, '-=0.4')
|
|
.to(scrollHint, { opacity: 1, duration: 0.4 }, '-=0.2')
|
|
} else {
|
|
// Full animation
|
|
const tl = gsap.timeline({ delay: 0.8 })
|
|
|
|
tl.add(() => {
|
|
gsap.set(title, { opacity: 1 })
|
|
animateSplit(title, { stagger: 0.02, duration: 1 })
|
|
})
|
|
.to(subtitle, {
|
|
opacity: 1,
|
|
y: 0,
|
|
duration: 0.8,
|
|
ease: 'power2.out'
|
|
}, '-=0.5')
|
|
.to(scrollHint, {
|
|
opacity: 1,
|
|
duration: 0.6,
|
|
ease: 'power2.out'
|
|
}, '-=0.3')
|
|
|
|
// Scroll hint animation
|
|
gsap.to(scrollHint, {
|
|
y: 10,
|
|
duration: 1.5,
|
|
ease: 'power2.inOut',
|
|
yoyo: true,
|
|
repeat: -1
|
|
})
|
|
|
|
// Hero parallax on scroll
|
|
gsap.to(hero, {
|
|
yPercent: -50,
|
|
ease: 'none',
|
|
scrollTrigger: {
|
|
trigger: hero,
|
|
start: 'top top',
|
|
end: 'bottom top',
|
|
scrub: true
|
|
}
|
|
})
|
|
}
|
|
}, hero)
|
|
|
|
return () => ctx.revert()
|
|
}, [])
|
|
|
|
return (
|
|
<section
|
|
ref={heroRef}
|
|
className="relative min-h-screen flex items-center justify-center overflow-hidden"
|
|
>
|
|
{/* Background layers */}
|
|
<div className="absolute inset-0 bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900" />
|
|
|
|
{/* Noise texture */}
|
|
<div
|
|
className="absolute inset-0 opacity-[0.03] mix-blend-soft-light"
|
|
style={{
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
|
|
}}
|
|
/>
|
|
|
|
{/* Parallax background elements */}
|
|
<ParallaxLayer depth={0.2} className="absolute inset-0">
|
|
<div className="absolute top-1/4 left-1/4 w-64 h-64 bg-purple-500/10 rounded-full blur-3xl" />
|
|
</ParallaxLayer>
|
|
|
|
<ParallaxLayer depth={0.4} className="absolute inset-0">
|
|
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl" />
|
|
</ParallaxLayer>
|
|
|
|
{/* Hero image */}
|
|
<ParallaxLayer depth={0.6} className="absolute inset-0 flex items-center justify-end pr-12">
|
|
<div className="relative w-96 h-96 opacity-20">
|
|
<Image
|
|
src="/michael-peskov-magier-taschendieb-453624.jpeg"
|
|
alt="Michael Peskov performing magic"
|
|
fill
|
|
className="object-cover rounded-2xl"
|
|
priority
|
|
/>
|
|
</div>
|
|
</ParallaxLayer>
|
|
|
|
{/* Content */}
|
|
<div className="relative z-10 text-center px-6 max-w-6xl mx-auto">
|
|
<h1
|
|
ref={titleRef}
|
|
className="text-6xl md:text-8xl lg:text-9xl font-bold text-white mb-8 leading-none"
|
|
style={{ willChange: 'transform' }}
|
|
>
|
|
Magic That
|
|
<br />
|
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-cyan-400">
|
|
Mesmerizes
|
|
</span>
|
|
</h1>
|
|
|
|
<p
|
|
ref={subtitleRef}
|
|
className="text-xl md:text-2xl text-gray-300 mb-12 max-w-3xl mx-auto leading-relaxed"
|
|
style={{ transform: 'translateY(30px)', willChange: 'transform' }}
|
|
>
|
|
Experience the wonder of modern magic with Michael Peskov.
|
|
From intimate close-up performances to grand stage illusions,
|
|
every moment is crafted to leave your audience spellbound.
|
|
</p>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-6 justify-center items-center">
|
|
<MagneticButton className="bg-gradient-to-r from-purple-600 to-cyan-600 text-white px-8 py-4 rounded-full text-lg font-semibold hover:shadow-2xl transition-shadow">
|
|
Book a Show
|
|
</MagneticButton>
|
|
|
|
<MagneticButton className="border border-white/30 text-white px-8 py-4 rounded-full text-lg font-semibold hover:bg-white/10 transition-colors">
|
|
Watch Showreel
|
|
</MagneticButton>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Scroll hint */}
|
|
<div
|
|
ref={scrollHintRef}
|
|
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 text-white/60"
|
|
style={{ willChange: 'transform' }}
|
|
>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-sm mb-2">Scroll to explore</span>
|
|
<div className="w-6 h-10 border border-white/30 rounded-full flex justify-center">
|
|
<div className="w-1 h-3 bg-white/60 rounded-full mt-2" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
} |