diff --git a/src/components/GenreExplorer/GenreExplorer.jsx b/src/components/GenreExplorer/GenreExplorer.jsx new file mode 100644 index 0000000..7d3bf23 --- /dev/null +++ b/src/components/GenreExplorer/GenreExplorer.jsx @@ -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 ( + + + + + + + + + + + + + + + + + + + ) +} + +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 ( + { 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' }} + > + + + + {genre.icon} + {genre.count} + + + + {genre.name} + tracks + + + ) +} + +export default function GenreExplorer() { + return ( + + + + + + {/* Left: 3D orbit animation */} + + + + + + + + + + {/* Right: genre cards */} + + + Browse + + Explore by Genre + + + + + {genres.map((genre, i) => ( + + ))} + + + + + + ) +}
tracks
Browse