MVp
This commit is contained in:
175
components/ScrollHero.tsx
Normal file
175
components/ScrollHero.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user