"use client"; import { useId, useRef, useState } from "react"; import { motion, useAnimationFrame, useMotionValue, useReducedMotion } from "motion/react"; type VinylSleeveProps = { title: string; artist?: string; /** Sleeve art: paper, middle arc, front arc. The disc label takes the middle one. */ colors?: [string, string, string]; /** Catalogue number printed on the sleeve. */ catalogue?: string; /** Text on the round shop sticker. Leave out for none. */ price?: string; /** Spin speed in turns a minute. */ rpm?: number; onToggle?: (playing: boolean) => void; className?: string; }; /* VinylSleeve: a worn record sleeve with the disc just showing. Hover slides the record out, press sets the arm down and it spins, then slows to a stop. */ export function VinylSleeve({ title, artist = "", colors = ["#e9dcc0", "#d6532c", "#22333b"], catalogue, price, rpm = 33, onToggle, className = "", }: VinylSleeveProps) { const reduce = useReducedMotion(); const [over, setOver] = useState(false); const [playing, setPlaying] = useState(false); const spin = useMotionValue(0); const speed = useRef(0); const ring = useId(); const out = over || playing; const [paper, mid, front] = colors; const slide = reduce ? { duration: 0 } : { type: "spring" as const, stiffness: 120, damping: 17 }; useAnimationFrame((_, dt) => { if (reduce) return; const goal = playing ? rpm * 6 : 0; if (!goal && speed.current < 0.5) return; speed.current += (goal - speed.current) * Math.min(1, dt * (playing ? 0.004 : 0.0016)); spin.set((spin.get() + (speed.current * dt) / 1000) % 360); }); function toggle() { onToggle?.(!playing); setPlaying(!playing); } return (
); }