160 lines
6.0 KiB
TypeScript
160 lines
6.0 KiB
TypeScript
import { motion } from "framer-motion";
|
|
|
|
export const DeveloperSkillsVector = () => {
|
|
const skills = [
|
|
{ name: "Frontend", color: "from-blue-500 to-blue-600", icon: "🎨", position: { x: -60, y: -30 } },
|
|
{ name: "Backend", color: "from-green-500 to-green-600", icon: "⚙️", position: { x: 60, y: -30 } },
|
|
{ name: "Database", color: "from-purple-500 to-purple-600", icon: "🗄️", position: { x: 0, y: 50 } }
|
|
];
|
|
|
|
return (
|
|
<div className="relative w-full h-80 flex items-center justify-center font-manrope">
|
|
{/* Subtle Background Glow */}
|
|
<div className="absolute inset-0 opacity-10">
|
|
<svg className="w-full h-full" viewBox="0 0 400 400" fill="none">
|
|
<defs>
|
|
<radialGradient id="bgGlow" cx="50%" cy="50%" r="40%">
|
|
<stop offset="0%" stopColor="#E5195E" stopOpacity="0.08" />
|
|
<stop offset="100%" stopColor="#3B82F6" stopOpacity="0.02" />
|
|
</radialGradient>
|
|
</defs>
|
|
<circle cx="200" cy="200" r="120" fill="url(#bgGlow)" />
|
|
</svg>
|
|
</div>
|
|
|
|
{/* Central Developer Icon */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 0.8 }}
|
|
className="relative z-20"
|
|
>
|
|
<motion.div
|
|
animate={{ y: [0, -8, 0] }}
|
|
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
|
|
className="w-20 h-20 bg-gradient-to-br from-accent to-purple-600 rounded-2xl flex items-center justify-center shadow-xl border border-white/10"
|
|
>
|
|
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
</svg>
|
|
</motion.div>
|
|
</motion.div>
|
|
|
|
{/* Skill Badges */}
|
|
{skills.map((skill, index) => (
|
|
<motion.div
|
|
key={skill.name}
|
|
initial={{ opacity: 0, scale: 0, x: 0, y: 0 }}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: 1,
|
|
x: skill.position.x,
|
|
y: skill.position.y
|
|
}}
|
|
transition={{
|
|
duration: 0.6,
|
|
delay: 0.4 + index * 0.2,
|
|
type: "spring",
|
|
stiffness: 100
|
|
}}
|
|
className="absolute z-10"
|
|
>
|
|
<motion.div
|
|
animate={{
|
|
y: [0, -6, 0],
|
|
scale: [1, 1.05, 1]
|
|
}}
|
|
transition={{
|
|
duration: 3 + index * 0.5,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
delay: index * 0.8
|
|
}}
|
|
className={`w-16 h-16 bg-gradient-to-br ${skill.color} rounded-xl flex items-center justify-center shadow-lg border border-white/20`}
|
|
>
|
|
<span className="text-2xl">{skill.icon}</span>
|
|
</motion.div>
|
|
|
|
{/* Skill Label */}
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.4, delay: 0.8 + index * 0.2 }}
|
|
className="absolute -bottom-8 left-1/2 transform -translate-x-1/2 text-white text-xs font-manrope whitespace-nowrap"
|
|
>
|
|
{skill.name}
|
|
</motion.div>
|
|
</motion.div>
|
|
))}
|
|
|
|
{/* Subtle Connecting Elements */}
|
|
<svg className="absolute inset-0 w-full h-full pointer-events-none opacity-20 z-15">
|
|
<defs>
|
|
<linearGradient id="connectionGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" stopColor="#E5195E" stopOpacity="0.3" />
|
|
<stop offset="50%" stopColor="#3B82F6" stopOpacity="0.2" />
|
|
<stop offset="100%" stopColor="#8B5CF6" stopOpacity="0.3" />
|
|
</linearGradient>
|
|
</defs>
|
|
|
|
{skills.map((skill, index) => (
|
|
<motion.line
|
|
key={`connection-${index}`}
|
|
x1="50%"
|
|
y1="50%"
|
|
x2={`${50 + (skill.position.x / 4)}%`}
|
|
y2={`${50 + (skill.position.y / 4)}%`}
|
|
stroke="url(#connectionGradient)"
|
|
strokeWidth="1"
|
|
strokeDasharray="2,4"
|
|
initial={{ pathLength: 0, opacity: 0 }}
|
|
animate={{ pathLength: 1, opacity: 1 }}
|
|
transition={{ duration: 1.5, delay: 1 + index * 0.3 }}
|
|
/>
|
|
))}
|
|
</svg>
|
|
|
|
{/* Status Indicator */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 0.5, delay: 1.8 }}
|
|
className="absolute top-8 right-8 z-25"
|
|
>
|
|
<div className="bg-black/30 backdrop-blur-sm rounded-lg px-3 py-2 border border-white/10">
|
|
<div className="flex items-center space-x-2">
|
|
<motion.div
|
|
animate={{ scale: [1, 1.2, 1] }}
|
|
transition={{ duration: 2, repeat: Infinity }}
|
|
className="w-2 h-2 bg-green-400 rounded-full"
|
|
></motion.div>
|
|
<span className="text-green-400 text-xs font-manrope">Available</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Experience Level */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6, delay: 2 }}
|
|
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-25"
|
|
>
|
|
<div className="bg-black/30 backdrop-blur-sm rounded-lg px-4 py-2 border border-white/10">
|
|
<div className="flex items-center space-x-3">
|
|
{[1, 2, 3, 4, 5].map((star, index) => (
|
|
<motion.div
|
|
key={star}
|
|
initial={{ scale: 0 }}
|
|
animate={{ scale: 1 }}
|
|
transition={{ duration: 0.3, delay: 2.2 + index * 0.1 }}
|
|
className="w-3 h-3 bg-yellow-400 rounded-full"
|
|
></motion.div>
|
|
))}
|
|
<span className="text-white text-xs font-manrope ml-2">Expert Level</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
}; |