|
|
|
import React, { useEffect, useState } from 'react'; |
|
import { motion } from 'framer-motion'; |
|
|
|
const VoiceVisualizer: React.FC = () => { |
|
const [bars, setBars] = useState<number[]>([]); |
|
|
|
useEffect(() => { |
|
|
|
const barCount = 60; |
|
const initialBars = Array.from({ length: barCount }, () => Math.random() * 50 + 10); |
|
setBars(initialBars); |
|
|
|
|
|
const interval = setInterval(() => { |
|
setBars(prev => prev.map(() => { |
|
|
|
return Math.random() * 65 + 5; |
|
})); |
|
}, 75); |
|
|
|
return () => clearInterval(interval); |
|
}, []); |
|
|
|
return ( |
|
<div className="flex items-end justify-center gap-[1px] h-24 w-80 relative overflow-hidden rounded-lg"> |
|
{/* Add subtle glow effect in the background */} |
|
<div className="absolute inset-0 bg-black/80 z-0"></div> |
|
|
|
{/* Center light glow */} |
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-32 h-32 bg-white/5 rounded-full blur-xl z-0"></div> |
|
|
|
{bars.map((height, index) => { |
|
// Calculate dynamic opacity for better visual effect |
|
const opacity = 0.7 + (height / 100) * 0.3; |
|
|
|
return ( |
|
<motion.div |
|
key={index} |
|
className="w-0.5 rounded-full relative z-10" |
|
animate={{ |
|
height: `${height}%` |
|
}} |
|
transition={{ |
|
type: "spring", |
|
stiffness: 300, |
|
damping: 20, |
|
duration: 0.2 |
|
}} |
|
style={{ |
|
background: `linear-gradient(to bottom, rgba(255,255,255,${opacity}) 0%, rgba(255,255,255,${opacity * 0.8}) 50%, rgba(255,255,255,${opacity * 0.5}) 100%)`, |
|
boxShadow: `0 0 4px rgba(255,255,255,${opacity * 0.5})`, |
|
}} |
|
/> |
|
); |
|
})} |
|
</div> |
|
); |
|
}; |
|
|
|
export default VoiceVisualizer; |
|
|