mirror of
https://github.com/handsomezhuzhu/personal-navigation-site.git
synced 2026-04-18 22:32:52 +00:00
Merge pull request #6 from handsomezhuzhu/v0/kdaugh14-4907-7424b84c
Redesign navigation with centered layout and cyberpunk animations
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@@ -124,3 +123,79 @@
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cyber glow animation - 文字白色,光晕颜色流动 */
|
||||
@keyframes glow-flow {
|
||||
0% { text-shadow: 0 0 12px oklch(0.75 0.18 180 / 0.9), 0 0 30px oklch(0.75 0.18 180 / 0.5), 0 0 60px oklch(0.75 0.18 180 / 0.2); }
|
||||
25% { text-shadow: 0 0 12px oklch(0.70 0.20 220 / 0.9), 0 0 30px oklch(0.70 0.20 220 / 0.5), 0 0 60px oklch(0.70 0.20 220 / 0.2); }
|
||||
50% { text-shadow: 0 0 12px oklch(0.65 0.22 280 / 0.9), 0 0 30px oklch(0.65 0.22 280 / 0.5), 0 0 60px oklch(0.65 0.22 280 / 0.2); }
|
||||
75% { text-shadow: 0 0 12px oklch(0.72 0.20 240 / 0.9), 0 0 30px oklch(0.72 0.20 240 / 0.5), 0 0 60px oklch(0.72 0.20 240 / 0.2); }
|
||||
100% { text-shadow: 0 0 12px oklch(0.75 0.18 180 / 0.9), 0 0 30px oklch(0.75 0.18 180 / 0.5), 0 0 60px oklch(0.75 0.18 180 / 0.2); }
|
||||
}
|
||||
|
||||
.cyber-title {
|
||||
color: oklch(0.98 0 0);
|
||||
animation: glow-flow 6s ease infinite;
|
||||
}
|
||||
|
||||
/* Animation utilities */
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes zoom-in-95 {
|
||||
from { opacity: 0; transform: scale(0.95); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
@keyframes zoom-out-95 {
|
||||
from { opacity: 1; transform: scale(1); }
|
||||
to { opacity: 0; transform: scale(0.95); }
|
||||
}
|
||||
|
||||
@keyframes slide-in-from-top-2 {
|
||||
from { transform: translateY(-0.5rem); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-in-from-bottom-2 {
|
||||
from { transform: translateY(0.5rem); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-in-from-left-2 {
|
||||
from { transform: translateX(-0.5rem); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-in-from-right-2 {
|
||||
from { transform: translateX(0.5rem); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
.animate-in {
|
||||
animation-duration: 150ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animate-out {
|
||||
animation-duration: 150ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.fade-in-0 { animation-name: fade-in; }
|
||||
.fade-out-0 { animation-name: fade-out; }
|
||||
.zoom-in-95 { animation-name: zoom-in-95; }
|
||||
.zoom-out-95 { animation-name: zoom-out-95; }
|
||||
.slide-in-from-top-2 { animation-name: slide-in-from-top-2; }
|
||||
.slide-in-from-bottom-2 { animation-name: slide-in-from-bottom-2; }
|
||||
.slide-in-from-left-2 { animation-name: slide-in-from-left-2; }
|
||||
.slide-in-from-right-2 { animation-name: slide-in-from-right-2; }
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import type React from "react"
|
||||
|
||||
import { useState } from "react"
|
||||
import { ExternalLink, Globe, Server, Mail, Zap, Shield, Code, FileText, Search } from "lucide-react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { ExternalLink, Server, Mail, Zap, Shield, Code, FileText, Search } from "lucide-react"
|
||||
|
||||
interface Site {
|
||||
domain: string
|
||||
@@ -373,6 +373,37 @@ function getServerBadgeStyle(server: string) {
|
||||
|
||||
export function SiteNavigation() {
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [displayText, setDisplayText] = useState("")
|
||||
const [isAnimationDone, setIsAnimationDone] = useState(false)
|
||||
const fullText = "SIMON站点导航"
|
||||
|
||||
useEffect(() => {
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_-+=<>?/\\|~"
|
||||
let frame = 0
|
||||
const totalFrames = fullText.length * 5
|
||||
|
||||
const interval = setInterval(() => {
|
||||
let output = ""
|
||||
for (let i = 0; i < fullText.length; i++) {
|
||||
if (i < Math.floor(frame / 5)) {
|
||||
output += fullText[i]
|
||||
} else {
|
||||
output += chars[Math.floor(Math.random() * chars.length)]
|
||||
}
|
||||
}
|
||||
setDisplayText(output)
|
||||
frame++
|
||||
if (frame > totalFrames) {
|
||||
clearInterval(interval)
|
||||
setDisplayText(fullText)
|
||||
setIsAnimationDone(true)
|
||||
}
|
||||
}, 40)
|
||||
|
||||
return () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const filteredCategories = categories
|
||||
.map((category) => ({
|
||||
@@ -393,19 +424,22 @@ export function SiteNavigation() {
|
||||
<div className="mx-auto max-w-6xl">
|
||||
{/* Header - 居中设计 */}
|
||||
<header className="mb-16 text-center">
|
||||
<div className="inline-flex items-center justify-center gap-3 mb-6">
|
||||
<div className="p-3 bg-primary/10 rounded-2xl">
|
||||
<Globe className="size-8 text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4">
|
||||
站点导航
|
||||
<h1
|
||||
className={`text-4xl md:text-6xl font-bold tracking-widest mb-4 font-mono transition-all duration-500 ${
|
||||
isAnimationDone ? "cyber-title" : "text-foreground"
|
||||
}`}
|
||||
>
|
||||
{displayText || "SIMON站点导航"}
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-lg md:text-xl">
|
||||
<span className="font-mono text-primary/80">zhuzihan.com</span>
|
||||
<div className="flex items-center justify-center gap-2 mb-2">
|
||||
<span className="block h-px w-16 bg-primary/30" />
|
||||
<p className="text-muted-foreground text-sm md:text-base tracking-widest uppercase font-mono">
|
||||
<span className="text-primary/80">zhuzihan.com</span>
|
||||
<span className="mx-2 text-border">|</span>
|
||||
全部站点与服务管理
|
||||
</p>
|
||||
<span className="block h-px w-16 bg-primary/30" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Search - 居中设计 */}
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"recharts": "2.15.4",
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
@@ -67,7 +67,6 @@
|
||||
"@types/react-dom": "^19",
|
||||
"postcss": "^8.5",
|
||||
"tailwindcss": "^4.1.9",
|
||||
"tw-animate-css": "1.3.3",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
1474
pnpm-lock.yaml
generated
1474
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user