nex_music/components/Turntable.tsx

105 lines
4.9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
interface TurntableProps {
isPlaying: boolean;
coverUrl: string;
}
const Turntable: React.FC<TurntableProps> = ({ isPlaying, coverUrl }) => {
const [rotation, setRotation] = useState(0);
// Use requestAnimationFrame for smoother rotation than CSS only when we want to control pause state precisely
useEffect(() => {
let animationFrameId: number;
let lastTime = performance.now();
const animate = (time: number) => {
if (isPlaying) {
const delta = time - lastTime;
// 33 1/3 RPM = ~0.55 rotations per second = ~200 degrees per second
// Removed modulo 360 to prevent jumping when value wraps around
setRotation((prev) => (prev + (delta * 0.15)));
}
lastTime = time;
animationFrameId = requestAnimationFrame(animate);
};
animationFrameId = requestAnimationFrame(animate);
return () => cancelAnimationFrame(animationFrameId);
}, [isPlaying]);
return (
<div className="relative w-full max-w-[500px] aspect-square flex items-center justify-center">
{/* Plinth / Base */}
<div className="absolute inset-4 bg-[#1a1a1a] rounded-[40px] shadow-2xl border border-neutral-800">
{/* Texture grain */}
<div className="absolute inset-0 opacity-20 bg-[url('https://www.transparenttextures.com/patterns/wood-pattern.png')] rounded-[40px] pointer-events-none"></div>
</div>
{/* Turntable Platter (The metal part under the record) */}
<div className="absolute inset-12 rounded-full bg-neutral-800 shadow-inner border border-neutral-700 flex items-center justify-center">
<div className="w-[98%] h-[98%] rounded-full bg-neutral-900 shadow-lg"></div>
</div>
{/* The Vinyl Record */}
<div
// Removed transition-transform to allow smooth infinite rotation driven by JS without CSS conflicts
className="absolute w-[76%] h-[76%] rounded-full shadow-[0_10px_30px_rgba(0,0,0,0.5)] will-change-transform"
style={{ transform: `rotate(${rotation}deg)` }}
>
{/* Vinyl Grooves Texture */}
<div className="absolute inset-0 rounded-full vinyl-grooves opacity-90 border-[6px] border-[#0a0a0a]"></div>
{/* Shiny reflection on vinyl */}
<div className="absolute inset-0 rounded-full bg-gradient-to-tr from-transparent via-white/5 to-transparent rotate-45 pointer-events-none"></div>
<div className="absolute inset-0 rounded-full bg-gradient-to-bl from-transparent via-white/5 to-transparent rotate-45 pointer-events-none"></div>
{/* Label / Cover Art */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[42%] h-[42%] rounded-full overflow-hidden border-4 border-[#111]">
<img
src={coverUrl}
alt="Album Cover"
className="w-full h-full object-cover"
/>
</div>
{/* Spindle Hole */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-3 h-3 bg-[#e5e5e5] rounded-full border border-black z-10"></div>
</div>
{/* Tonearm Structure */}
<div className="absolute top-4 right-4 w-24 h-64 pointer-events-none z-20">
{/* Pivot Base */}
<div className="absolute top-4 right-4 w-16 h-16 rounded-full bg-gradient-to-br from-neutral-600 to-neutral-800 shadow-xl border border-neutral-600 flex items-center justify-center">
<div className="w-8 h-8 rounded-full bg-neutral-400 shadow-inner"></div>
</div>
{/* The Arm itself */}
<div
className="absolute top-12 right-12 w-8 h-64 origin-[top_center] transition-transform duration-1000 ease-in-out"
style={{
// Adjusted angles: -25deg moves it visibly off the record, 20deg moves it onto the record
transform: isPlaying ? 'rotate(20deg)' : 'rotate(-25deg)',
transformOrigin: '50% 20px'
}}
>
{/* Main rod */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-3 h-48 bg-gradient-to-r from-neutral-400 via-neutral-200 to-neutral-400 rounded-full shadow-lg"></div>
{/* Headstock */}
<div className="absolute bottom-12 left-1/2 -translate-x-1/2 w-8 h-12 bg-neutral-800 rounded-sm border border-neutral-600"></div>
{/* Needle */}
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 w-1 h-4 bg-amber-200"></div>
</div>
</div>
{/* Start/Stop Button Visual on Turntable */}
<div className="absolute bottom-10 left-10 w-12 h-12 rounded-full bg-gradient-to-br from-neutral-700 to-neutral-900 border border-neutral-600 shadow-lg flex items-center justify-center">
<div className={`w-2 h-2 rounded-full ${isPlaying ? 'bg-green-500 shadow-[0_0_10px_#22c55e]' : 'bg-red-500'} transition-colors duration-300`}></div>
</div>
</div>
);
};
export default Turntable;