Import source: src/components/FeaturedAlbums/AlbumCard3D
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,94 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'
|
||||
|
||||
export default function AlbumCard3D({ album, onPlay, isActive }) {
|
||||
const cardRef = useRef()
|
||||
const [hovered, setHovered] = useState(false)
|
||||
const x = useMotionValue(0)
|
||||
const y = useMotionValue(0)
|
||||
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [14, -14]), { stiffness: 260, damping: 26 })
|
||||
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-14, 14]), { stiffness: 260, damping: 26 })
|
||||
|
||||
const onMove = (e) => {
|
||||
const r = cardRef.current.getBoundingClientRect()
|
||||
x.set((e.clientX - r.left) / r.width - 0.5)
|
||||
y.set((e.clientY - r.top) / r.height - 0.5)
|
||||
}
|
||||
const onLeave = () => { x.set(0); y.set(0); setHovered(false) }
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={cardRef}
|
||||
onMouseMove={onMove}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={onLeave}
|
||||
style={{ rotateX, rotateY, transformStyle: 'preserve-3d', perspective: 900 }}
|
||||
whileHover={{ scale: 1.04, y: -8 }}
|
||||
transition={{ type: 'spring', stiffness: 280, damping: 24 }}
|
||||
className="relative rounded-2xl overflow-hidden cursor-pointer group shadow-card"
|
||||
onClick={() => onPlay(album)}
|
||||
>
|
||||
{/* Background */}
|
||||
<div className="absolute inset-0 rounded-2xl"
|
||||
style={{ background: 'var(--dark-card)', border: '1px solid var(--dark-border)' }} />
|
||||
|
||||
{/* Hover glow */}
|
||||
<motion.div
|
||||
animate={{ opacity: hovered ? 1 : 0 }}
|
||||
transition={{ duration: 0.35 }}
|
||||
className="absolute inset-0 rounded-2xl pointer-events-none z-10"
|
||||
style={{ boxShadow: 'inset 0 0 0 1px rgba(201,168,76,0.28), 0 0 50px rgba(201,168,76,0.1)' }}
|
||||
/>
|
||||
|
||||
{/* Active indicator */}
|
||||
{isActive && (
|
||||
<div className="absolute top-3 right-3 z-20 flex items-center gap-1.5 px-2.5 py-1 rounded-full glass-gold">
|
||||
<span className="w-1 h-1 rounded-full bg-gold animate-pulse-gold" />
|
||||
<span className="text-xs gold-text font-medium">Playing</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Cover art */}
|
||||
<div className="relative overflow-hidden" style={{ aspectRatio: '1' }}>
|
||||
<img src={album.cover} alt={album.title}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110" />
|
||||
<div className="absolute inset-0"
|
||||
style={{ background: 'linear-gradient(to top, var(--dark-card) 0%, rgba(15,15,18,0.3) 55%, transparent 100%)' }} />
|
||||
|
||||
{/* Play overlay */}
|
||||
<motion.div
|
||||
animate={{ opacity: hovered ? 1 : 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="absolute inset-0 flex items-center justify-center"
|
||||
style={{ background: 'rgba(7,7,9,0.38)' }}
|
||||
>
|
||||
<motion.div
|
||||
animate={{ scale: hovered ? 1 : 0.6 }}
|
||||
transition={{ type: 'spring', stiffness: 350, damping: 28 }}
|
||||
className="w-14 h-14 rounded-full flex items-center justify-center shadow-gold"
|
||||
style={{ background: 'linear-gradient(135deg, #c9a84c, #e8c547)' }}
|
||||
>
|
||||
<svg className="w-5 h-5 ml-1" fill="#070709" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className="relative p-4">
|
||||
<h3 className={`font-display font-semibold text-sm truncate transition-colors duration-200 ${
|
||||
hovered ? 'gold-text' : 'text-white'
|
||||
}`}>{album.title}</h3>
|
||||
<p className="text-secondary text-xs mt-0.5 truncate">{album.artist} · {album.year}</p>
|
||||
<div className="flex items-center justify-between mt-3">
|
||||
<span className="text-xs px-2.5 py-0.5 rounded-full font-medium"
|
||||
style={{ background: 'rgba(201,168,76,0.1)', color: 'var(--gold)', border: '1px solid rgba(201,168,76,0.2)' }}>
|
||||
{album.genre}
|
||||
</span>
|
||||
<span className="text-xs text-muted font-mono">{album.tracks} trk</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user