import { motion, type Variants } from "framer-motion"; import { useTranslations } from "@/i18n/utils"; import { type SkillItem } from "@/types"; const allSkills: SkillItem[] = [ // Programming Languages { name: "TypeScript", icon: "typescript" }, { name: "JavaScript", icon: "javascript" }, { name: "React", icon: "react" }, { name: "Vue", icon: "vue" }, // Frontend Development { name: "Next.js", icon: "nextjs" }, { name: "Nuxt.js", icon: "nuxtjs" }, { name: "React Native", icon: "react" }, { name: "Tailwind CSS", icon: "tailwindcss" }, { name: "Shadcn UI", icon: "react" }, // Using react icon as fallback { name: "PrimeVue", icon: "vue" }, // Using vue icon as fallback { name: "Naive-UI", icon: "vue" }, // Using vue icon as fallback // Backend Development { name: "Node.js", icon: "nodejs" }, { name: "Express.js", icon: "express" }, { name: "Nest.js", icon: "nestjs" }, { name: "Hono.js", icon: "nodejs" }, // Using nodejs icon as fallback // Database & Storage { name: "PostgreSQL", icon: "postgresql" }, { name: "MongoDB", icon: "mongodb" }, { name: "Drizzle ORM", icon: "typescript" }, // Using typescript icon as fallback { name: "Mongoose", icon: "mongodb" }, // Using mongodb icon as fallback { name: "Prisma", icon: "prisma" }, // Cloud & DevOps { name: "AWS", icon: "aws" }, { name: "Cloudflare", icon: "cloudflare" }, { name: "Vercel", icon: "vercel" }, // Tools & Services { name: "Zod", icon: "typescript" }, // Using typescript icon as fallback { name: "BetterAuth", icon: "nodejs" }, // Using nodejs icon as fallback { name: "Clerk", icon: "react" }, // Using react icon as fallback { name: "GitLab", icon: "gitlab" }, { name: "CI/CD", icon: "github" }, // Using github icon as fallback { name: "Git", icon: "git" }, { name: "Docker", icon: "docker" }, ]; /** * Individual skill item component with icon and name */ function SkillItem({ skill }: { skill: SkillItem }) { const iconUrl = `https://skillicons.dev/icons?i=${skill.icon}`; return (
{skill.name} {skill.name}
); } /** * Marquee row component that scrolls skills horizontally * Optimized for performance with constant animation */ function MarqueeRow({ skills, direction = "left", speed = 50 }: { skills: SkillItem[]; direction?: "left" | "right"; speed?: number; }) { // Duplicate skills array to create seamless infinite scroll const duplicatedSkills = [...skills, ...skills]; // Define animation variants to ensure type safety with hardware acceleration const variants: Variants = { animate: { transform: direction === "left" ? ["translateX(0%)", "translateX(-50%)"] : ["translateX(-50%)", "translateX(0%)"], transition: { transform: { repeat: Infinity, repeatType: "loop" as const, duration: speed, ease: [0, 0, 1, 1] // 使用数组形式的 linear easing } } } }; return (
{duplicatedSkills.map((skill, index) => ( ))}
); } /** * Main skills marquee component with multiple scrolling rows */ export default function SkillsMarquee({ lang }: { lang: "en" | "zh" }) { const t = useTranslations(lang); // Split skills into multiple rows for varied scrolling effect const skillsRow1 = allSkills.slice(0, Math.ceil(allSkills.length / 3)); const skillsRow2 = allSkills.slice(Math.ceil(allSkills.length / 3), Math.ceil(allSkills.length * 2 / 3)); const skillsRow3 = allSkills.slice(Math.ceil(allSkills.length * 2 / 3)); return (
{/* First row - scrolling left */} {/* Second row - scrolling right */} {/* Third row - scrolling left */}
{/* Gradient overlays for smooth fade effect */}
); }