103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import gsap from "gsap";
|
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
import { SectionLabel } from "./section-label";
|
|
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
const groups: { name: string; items: string[] }[] = [
|
|
{
|
|
name: "语言",
|
|
items: ["TypeScript", "JavaScript", "Python"]
|
|
},
|
|
{
|
|
name: "前端",
|
|
items: ["Vue 3", "Nuxt", "Pinia", "Tailwind CSS", "Sass / Less"]
|
|
},
|
|
{
|
|
name: "UI 与可视化",
|
|
items: ["Naive UI", "Ant Design", "shadcn/ui", "ECharts"]
|
|
},
|
|
{
|
|
name: "后端",
|
|
items: ["Node.js", "Koa", "FastAPI"]
|
|
},
|
|
{
|
|
name: "工程化",
|
|
items: ["Vite", "pnpm", "Git"]
|
|
},
|
|
{
|
|
name: "设计",
|
|
items: ["Figma", "Pixso", "蓝湖"]
|
|
},
|
|
{
|
|
name: "AI 编程",
|
|
items: ["Cursor", "Copilot", "Claude Code", "Codex", "V0"]
|
|
},
|
|
{
|
|
name: "AI 模型",
|
|
items: ["GPT", "Claude", "Grok", "DeepSeek", "MiMo"]
|
|
}
|
|
];
|
|
|
|
export function TechStack() {
|
|
const rootRef = useRef<HTMLElement>(null);
|
|
|
|
useEffect(() => {
|
|
const ctx = gsap.context(() => {
|
|
gsap.fromTo(
|
|
".tech-item",
|
|
{ opacity: 0, y: 24 },
|
|
{
|
|
opacity: 1,
|
|
y: 0,
|
|
duration: 0.7,
|
|
ease: "power2.out",
|
|
stagger: 0.06,
|
|
scrollTrigger: {
|
|
trigger: rootRef.current,
|
|
start: "top 75%"
|
|
}
|
|
}
|
|
);
|
|
}, rootRef);
|
|
|
|
return () => ctx.revert();
|
|
}, []);
|
|
|
|
return (
|
|
<section
|
|
ref={rootRef}
|
|
aria-label="技术栈"
|
|
className="mx-auto max-w-6xl px-6 py-32 md:py-40"
|
|
>
|
|
<SectionLabel>Tech Stack</SectionLabel>
|
|
|
|
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 md:gap-x-16">
|
|
{groups.map((group) => (
|
|
<div key={group.name} className="tech-item">
|
|
<p className="mb-5 text-xs uppercase tracking-[0.2em] text-muted-foreground/60">
|
|
{group.name}
|
|
</p>
|
|
<ul className="flex flex-wrap gap-3">
|
|
{group.items.map((item) => (
|
|
<li key={item} className="tech-item">
|
|
<span className="inline-flex items-center gap-2 rounded-md border border-border bg-card px-4 py-2 text-sm text-secondary-foreground transition-all duration-300 hover:scale-[1.04] hover:border-foreground/20 hover:shadow-[0_0_20px_-6px_oklch(0.62_0.05_155_/_0.25)]">
|
|
<span
|
|
aria-hidden="true"
|
|
className="h-1 w-1 rounded-full bg-sage/70"
|
|
/>
|
|
{item}
|
|
</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|