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 "tailwindcss";
|
||||||
@import "tw-animate-css";
|
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@@ -124,3 +123,79 @@
|
|||||||
@apply bg-background text-foreground;
|
@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 type React from "react"
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { ExternalLink, Globe, Server, Mail, Zap, Shield, Code, FileText, Search } from "lucide-react"
|
import { ExternalLink, Server, Mail, Zap, Shield, Code, FileText, Search } from "lucide-react"
|
||||||
|
|
||||||
interface Site {
|
interface Site {
|
||||||
domain: string
|
domain: string
|
||||||
@@ -373,6 +373,37 @@ function getServerBadgeStyle(server: string) {
|
|||||||
|
|
||||||
export function SiteNavigation() {
|
export function SiteNavigation() {
|
||||||
const [searchQuery, setSearchQuery] = useState("")
|
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
|
const filteredCategories = categories
|
||||||
.map((category) => ({
|
.map((category) => ({
|
||||||
@@ -393,19 +424,22 @@ export function SiteNavigation() {
|
|||||||
<div className="mx-auto max-w-6xl">
|
<div className="mx-auto max-w-6xl">
|
||||||
{/* Header - 居中设计 */}
|
{/* Header - 居中设计 */}
|
||||||
<header className="mb-16 text-center">
|
<header className="mb-16 text-center">
|
||||||
<div className="inline-flex items-center justify-center gap-3 mb-6">
|
<h1
|
||||||
<div className="p-3 bg-primary/10 rounded-2xl">
|
className={`text-4xl md:text-6xl font-bold tracking-widest mb-4 font-mono transition-all duration-500 ${
|
||||||
<Globe className="size-8 text-primary" />
|
isAnimationDone ? "cyber-title" : "text-foreground"
|
||||||
</div>
|
}`}
|
||||||
</div>
|
>
|
||||||
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4">
|
{displayText || "SIMON站点导航"}
|
||||||
站点导航
|
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-muted-foreground text-lg md:text-xl">
|
<div className="flex items-center justify-center gap-2 mb-2">
|
||||||
<span className="font-mono text-primary/80">zhuzihan.com</span>
|
<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>
|
<span className="mx-2 text-border">|</span>
|
||||||
全部站点与服务管理
|
全部站点与服务管理
|
||||||
</p>
|
</p>
|
||||||
|
<span className="block h-px w-16 bg-primary/30" />
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Search - 居中设计 */}
|
{/* Search - 居中设计 */}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
"recharts": "2.15.4",
|
"recharts": "2.15.4",
|
||||||
"sonner": "^1.7.4",
|
"sonner": "^1.7.4",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "1.0.7",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"zod": "3.25.76"
|
"zod": "3.25.76"
|
||||||
},
|
},
|
||||||
@@ -67,7 +67,6 @@
|
|||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"postcss": "^8.5",
|
"postcss": "^8.5",
|
||||||
"tailwindcss": "^4.1.9",
|
"tailwindcss": "^4.1.9",
|
||||||
"tw-animate-css": "1.3.3",
|
|
||||||
"typescript": "^5"
|
"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 'tailwindcss';
|
||||||
@import 'tw-animate-css';
|
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user