Import source: src/components/Hero/Hero
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { gsap } from 'gsap'
|
||||
import AudioVisualizer3D from './AudioVisualizer3D'
|
||||
import HeroParticles from './HeroParticles'
|
||||
|
||||
const fadeUp = (delay = 0) => ({
|
||||
initial: { opacity: 0, y: 32 },
|
||||
animate: { opacity: 1, y: 0 },
|
||||
transition: { duration: 0.85, ease: [0.22, 1, 0.36, 1], delay },
|
||||
})
|
||||
|
||||
export default function Hero({ isPlaying, onTogglePlay }) {
|
||||
const headingRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
gsap.from('.hero-line', {
|
||||
y: 80,
|
||||
opacity: 0,
|
||||
duration: 1,
|
||||
stagger: 0.14,
|
||||
ease: 'power4.out',
|
||||
delay: 0.2,
|
||||
})
|
||||
})
|
||||
return () => ctx.revert()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section className="relative min-h-screen flex items-center justify-center overflow-hidden"
|
||||
style={{ background: 'var(--dark-base)' }}>
|
||||
|
||||
{/* Particle bg */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<HeroParticles />
|
||||
</div>
|
||||
|
||||
{/* Ambient blobs */}
|
||||
<div className="absolute pointer-events-none"
|
||||
style={{ top: '20%', left: '50%', transform: 'translate(-50%,-50%)',
|
||||
width: 700, height: 700,
|
||||
background: 'radial-gradient(ellipse, rgba(201,168,76,0.055) 0%, transparent 68%)',
|
||||
borderRadius: '50%' }} />
|
||||
<div className="absolute pointer-events-none"
|
||||
style={{ bottom: '10%', right: '8%', width: 380, height: 380,
|
||||
background: 'radial-gradient(ellipse, rgba(232,197,71,0.04) 0%, transparent 70%)',
|
||||
borderRadius: '50%' }} />
|
||||
<div className="absolute pointer-events-none"
|
||||
style={{ top: '30%', left: '5%', width: 300, height: 300,
|
||||
background: 'radial-gradient(ellipse, rgba(201,168,76,0.03) 0%, transparent 70%)',
|
||||
borderRadius: '50%' }} />
|
||||
|
||||
{/* 3D Visualizer */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<AudioVisualizer3D isPlaying={isPlaying} />
|
||||
</div>
|
||||
|
||||
{/* Main content */}
|
||||
<div className="relative z-10 text-center px-6 max-w-5xl mx-auto pt-24">
|
||||
|
||||
{/* Badge */}
|
||||
<motion.div {...fadeUp(0.1)} className="inline-flex items-center gap-2.5 mb-10 px-5 py-2 rounded-full glass-gold">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-gold animate-pulse-gold" />
|
||||
<span className="text-xs tracking-[0.22em] gold-text uppercase font-medium">Now Streaming</span>
|
||||
<span className="text-muted text-xs">·</span>
|
||||
<span className="text-xs text-secondary">40M tracks</span>
|
||||
</motion.div>
|
||||
|
||||
{/* Headline */}
|
||||
<div ref={headingRef} className="overflow-hidden mb-6">
|
||||
<div className="hero-line font-display font-extrabold text-white leading-none"
|
||||
style={{ fontSize: 'clamp(3.5rem, 11vw, 9rem)', letterSpacing: '-0.03em' }}>
|
||||
Feel The
|
||||
</div>
|
||||
<div className="hero-line font-display font-extrabold leading-none gold-gradient-text"
|
||||
style={{ fontSize: 'clamp(3.5rem, 11vw, 9rem)', letterSpacing: '-0.03em' }}>
|
||||
Music
|
||||
</div>
|
||||
<div className="hero-line font-display font-medium text-secondary tracking-wide"
|
||||
style={{ fontSize: 'clamp(1.4rem, 4vw, 3.5rem)', letterSpacing: '0.04em', marginTop: '0.15em' }}>
|
||||
In Three Dimensions
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
<motion.p {...fadeUp(0.55)}
|
||||
className="text-secondary text-base md:text-lg mb-12 max-w-lg mx-auto leading-relaxed">
|
||||
Where sound becomes sculpture. Every frequency, every beat — visualized in real time across a living 3D canvas.
|
||||
</motion.p>
|
||||
|
||||
{/* CTAs */}
|
||||
<motion.div {...fadeUp(0.7)} className="flex items-center justify-center gap-4 flex-wrap mb-16">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05, boxShadow: '0 0 40px rgba(201,168,76,0.4)' }}
|
||||
whileTap={{ scale: 0.96 }}
|
||||
onClick={onTogglePlay}
|
||||
className="flex items-center gap-3 px-8 py-4 rounded-full font-semibold text-sm tracking-wide transition-all duration-200 shadow-card"
|
||||
style={{ background: 'linear-gradient(135deg, #c9a84c, #e8c547)', color: '#070709' }}
|
||||
>
|
||||
{isPlaying
|
||||
? <><PauseIcon /> Pause</>
|
||||
: <><PlayIcon /> Start Listening</>
|
||||
}
|
||||
</motion.button>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.04, borderColor: 'rgba(201,168,76,0.4)', color: 'var(--gold)' }}
|
||||
whileTap={{ scale: 0.96 }}
|
||||
className="flex items-center gap-2 px-8 py-4 rounded-full font-semibold text-sm tracking-wide text-secondary transition-all duration-200 glass"
|
||||
>
|
||||
Explore Collection
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
|
||||
{/* Stats row */}
|
||||
<motion.div {...fadeUp(0.85)}
|
||||
className="flex items-center justify-center gap-0 divide-x divide-dark-border">
|
||||
{[['40M+', 'Tracks'], ['2.1M', 'Artists'], ['180+', 'Countries'], ['99.9%', 'Uptime']].map(([num, label]) => (
|
||||
<div key={label} className="px-6 md:px-10 text-center first:pl-0 last:pr-0">
|
||||
<p className="font-display font-bold text-xl md:text-2xl gold-text">{num}</p>
|
||||
<p className="text-xs text-muted tracking-widest uppercase mt-0.5">{label}</p>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Scroll cue */}
|
||||
<motion.div
|
||||
animate={{ y: [0, 9, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 2.6, ease: 'easeInOut' }}
|
||||
className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-1.5"
|
||||
>
|
||||
<span className="text-xs tracking-widest text-muted uppercase">Scroll</span>
|
||||
<svg className="w-4 h-4 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function PlayIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4 ml-0.5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
function PauseIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user