Import source: src/components/GenreExplorer/GenreExplorer

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:07:56 +03:30
parent a5498a23f3
commit 5140b87cb0
@@ -0,0 +1,129 @@
import { useRef } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'
const genres = [
{ name: 'Ambient', count: '12.4K', icon: '◉' },
{ name: 'Electronic', count: '48.2K', icon: '◈' },
{ name: 'Cinematic', count: '9.1K', icon: '◎' },
{ name: 'Dark Wave', count: '7.8K', icon: '◆' },
{ name: 'Synthwave', count: '31.5K', icon: '◇' },
{ name: 'Lo-Fi', count: '22.9K', icon: '○' },
{ name: 'Jazz Fusion', count: '5.3K', icon: '◐' },
{ name: 'Neo-Soul', count: '18.7K', icon: '◑' },
]
function RotatingRing() {
const ref = useRef()
useFrame(({ clock }) => {
ref.current.rotation.x = clock.getElapsedTime() * 0.12
ref.current.rotation.y = clock.getElapsedTime() * 0.18
})
return (
<group ref={ref}>
<mesh>
<torusGeometry args={[2.2, 0.015, 8, 120]} />
<meshBasicMaterial color="#c9a84c" transparent opacity={0.35} />
</mesh>
<mesh rotation={[Math.PI / 3, 0, Math.PI / 4]}>
<torusGeometry args={[2.2, 0.015, 8, 120]} />
<meshBasicMaterial color="#e8c547" transparent opacity={0.2} />
</mesh>
<mesh rotation={[-Math.PI / 4, Math.PI / 3, 0]}>
<torusGeometry args={[2.2, 0.015, 8, 120]} />
<meshBasicMaterial color="#8a6f2e" transparent opacity={0.25} />
</mesh>
<mesh>
<sphereGeometry args={[0.6, 32, 32]} />
<meshStandardMaterial color="#c9a84c" emissive="#c9a84c" emissiveIntensity={0.4} roughness={0.05} metalness={1} />
</mesh>
</group>
)
}
function GenreCard({ genre, index }) {
const x = useMotionValue(0)
const y = useMotionValue(0)
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [10, -10]), { stiffness: 250, damping: 25 })
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-10, 10]), { stiffness: 250, damping: 25 })
const cardRef = useRef()
const handleMouseMove = (e) => {
const rect = cardRef.current.getBoundingClientRect()
x.set((e.clientX - rect.left) / rect.width - 0.5)
y.set((e.clientY - rect.top) / rect.height - 0.5)
}
return (
<motion.div
ref={cardRef}
onMouseMove={handleMouseMove}
onMouseLeave={() => { x.set(0); y.set(0) }}
style={{ rotateX, rotateY, transformStyle: 'preserve-3d', perspective: 600 }}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.07 }}
whileHover={{ scale: 1.05, y: -4 }}
className="relative cursor-pointer group rounded-2xl p-5 flex flex-col justify-between"
style={{ background: '#101010', border: '1px solid #1e1e1e', height: '130px' }}
>
<div className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-400"
style={{ boxShadow: 'inset 0 0 0 1px rgba(201,168,76,0.25), 0 0 30px rgba(201,168,76,0.08)' }} />
<div className="flex items-start justify-between">
<span className="text-2xl" style={{ color: 'rgba(201,168,76,0.6)' }}>{genre.icon}</span>
<span className="text-xs text-gray-700 font-mono">{genre.count}</span>
</div>
<div>
<h3 className="font-display font-semibold text-white text-sm group-hover:gold-text transition-colors">{genre.name}</h3>
<p className="text-xs text-gray-700 mt-0.5">tracks</p>
</div>
</motion.div>
)
}
export default function GenreExplorer() {
return (
<section id="discover" className="py-28 px-6" style={{ background: '#0a0a0a' }}>
<div className="section-divider mb-0" />
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center mt-14">
{/* Left: 3D orbit animation */}
<div className="order-2 lg:order-1" style={{ height: '380px' }}>
<Canvas camera={{ position: [0, 0, 6], fov: 50 }} gl={{ alpha: true, antialias: true }} style={{ background: 'transparent' }}>
<ambientLight intensity={0.1} />
<pointLight position={[0, 0, 5]} intensity={3} color="#c9a84c" />
<pointLight position={[4, 3, 2]} intensity={1.5} color="#e8c547" />
<RotatingRing />
</Canvas>
</div>
{/* Right: genre cards */}
<div className="order-1 lg:order-2">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="mb-10"
>
<p className="text-xs tracking-[0.25em] uppercase gold-text mb-3">Browse</p>
<h2 className="font-display font-bold text-4xl md:text-5xl text-white">
Explore by <span className="gold-text">Genre</span>
</h2>
</motion.div>
<div className="grid grid-cols-2 gap-3">
{genres.map((genre, i) => (
<GenreCard key={genre.name} genre={genre} index={i} />
))}
</div>
</div>
</div>
</div>
</section>
)
}