初始化
This commit is contained in:
875
view/UI.html
Normal file
875
view/UI.html
Normal file
@@ -0,0 +1,875 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>艺术与代码 | 年糕崽崽的数字花园</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Noto+Sans+SC:wght@300;400;500;700&family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Lucide Icons -->
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['"Inter"', '"Noto Sans SC"', 'sans-serif'],
|
||||
serif: ['"Playfair Display"', 'serif'],
|
||||
mono: ['"JetBrains Mono"', 'monospace'],
|
||||
},
|
||||
colors: {
|
||||
art: {
|
||||
bg: '#050505',
|
||||
surface: '#121214',
|
||||
border: 'rgba(255,255,255,0.08)',
|
||||
text: '#ececec',
|
||||
muted: '#888888',
|
||||
accent: '#d4b383',
|
||||
error: '#ef4444'
|
||||
}
|
||||
},
|
||||
backgroundImage: {
|
||||
'noise': "url('data:image/svg+xml,%3Csvg viewBox=%220 0 200 200%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cfilter id=%22noiseFilter%22%3E%3CfeTurbulence type=%22fractalNoise%22 baseFrequency=%220.65%22 numOctaves=%223%22 stitchTiles=%22stitch%22/%3E%3C/filter%3E%3Crect width=%22100%25%22 height=%22100%25%22 filter=%22url(%23noiseFilter)%22 opacity=%220.07%22/%3E%3C/svg%3E')",
|
||||
},
|
||||
animation: {
|
||||
'float': 'float 8s ease-in-out infinite',
|
||||
'slide-down': 'slideDown 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards',
|
||||
'reveal': 'reveal 1s cubic-bezier(0.16, 1, 0.3, 1) forwards',
|
||||
'spotlight': 'spotlight 2s ease .75s 1 forwards',
|
||||
'marquee': 'marquee 25s linear infinite',
|
||||
'marquee-reverse': 'marqueeReverse 30s linear infinite',
|
||||
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
'shake': 'shake 0.5s cubic-bezier(.36,.07,.19,.97) both',
|
||||
},
|
||||
keyframes: {
|
||||
float: {
|
||||
'0%, 100%': { transform: 'translateY(0)' },
|
||||
'50%': { transform: 'translateY(-20px)' },
|
||||
},
|
||||
slideDown: {
|
||||
'0%': { transform: 'translateY(-100%)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
reveal: {
|
||||
'0%': { opacity: '0', transform: 'translateY(50px)' },
|
||||
'100%': { opacity: '1', transform: 'translateY(0)' },
|
||||
},
|
||||
spotlight: {
|
||||
'0%': { opacity: 0, transform: 'translate(-50%, -50%) scale(0.5)' },
|
||||
'100%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' }
|
||||
},
|
||||
marquee: {
|
||||
'0%': { transform: 'translateX(0%)' },
|
||||
'100%': { transform: 'translateX(-100%)' }
|
||||
},
|
||||
marqueeReverse: {
|
||||
'0%': { transform: 'translateX(-50%)' },
|
||||
'100%': { transform: 'translateX(0%)' }
|
||||
},
|
||||
shake: {
|
||||
'10%, 90%': { transform: 'translate3d(-1px, 0, 0)' },
|
||||
'20%, 80%': { transform: 'translate3d(2px, 0, 0)' },
|
||||
'30%, 50%, 70%': { transform: 'translate3d(-4px, 0, 0)' },
|
||||
'40%, 60%': { transform: 'translate3d(4px, 0, 0)' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 3px; transition: all 0.3s ease; }
|
||||
::-webkit-scrollbar-thumb:hover { background: #d4b383; box-shadow: 0 0 10px rgba(212, 179, 131, 0.4); }
|
||||
::-webkit-scrollbar-corner { background: transparent; }
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar { width: 4px; }
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.05); }
|
||||
.custom-scrollbar:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); }
|
||||
|
||||
/* Glass & Utils */
|
||||
.glass-nav { background: rgba(5, 5, 5, 0.7); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-bottom: 1px solid rgba(255, 255, 255, 0.03); }
|
||||
.art-card { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(255, 255, 255, 0.03); transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1); position: relative; overflow: hidden; }
|
||||
.art-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.02) 100%); opacity: 0; transition: opacity 0.5s ease; }
|
||||
.art-card:hover { transform: translateY(-5px); border-color: rgba(212, 179, 131, 0.3); box-shadow: 0 20px 40px -10px rgba(0,0,0,0.5); }
|
||||
.art-card:hover::before { opacity: 1; }
|
||||
.text-glow { text-shadow: 0 0 30px rgba(255,255,255,0.1); }
|
||||
|
||||
.link-underline { position: relative; }
|
||||
.link-underline::after { content: ''; position: absolute; width: 0; height: 1px; bottom: -2px; left: 0; background-color: #d4b383; transition: width 0.3s ease; }
|
||||
.link-underline:hover::after { width: 100%; }
|
||||
|
||||
/* Modal & Toast */
|
||||
.modal-overlay { background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(10px); }
|
||||
.toast-container { position: fixed; bottom: 2rem; right: 2rem; z-index: 100; display: flex; flex-direction: column; gap: 0.5rem; pointer-events: none; }
|
||||
.toast { background: rgba(20, 20, 23, 0.9); border: 1px solid rgba(255,255,255,0.1); backdrop-filter: blur(12px); padding: 1rem 1.5rem; border-radius: 0.5rem; color: white; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); display: flex; align-items: center; gap: 0.75rem; transform: translateX(100%); transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); pointer-events: auto; min-width: 300px; }
|
||||
.toast.show { transform: translateX(0); }
|
||||
.toast-success { border-left: 3px solid #d4b383; }
|
||||
.toast-error { border-left: 3px solid #ef4444; }
|
||||
|
||||
/* Form Error */
|
||||
.input-error { border-bottom-color: #ef4444 !important; }
|
||||
.error-msg { color: #ef4444; font-family: 'JetBrains Mono', monospace; font-size: 0.65rem; margin-top: 0.4rem; display: block; position: absolute; bottom: -1.2rem; left: 0; opacity: 0; letter-spacing: 0.05em; animation: fadeIn 0.3s forwards; background: rgba(239, 68, 68, 0.1); padding: 0.1rem 0.3rem; border-radius: 0.2rem; }
|
||||
@keyframes fadeIn { to { opacity: 1; } }
|
||||
|
||||
/* Code Block */
|
||||
.mac-code-block { background: #1e1e1e; padding: 3rem 1.5rem 1.5rem 1.5rem; border-radius: 0.75rem; overflow-x: auto; margin-bottom: 0; border: 1px solid rgba(255,255,255,0.1); position: relative; box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5); }
|
||||
.mac-code-block::before { content: ''; position: absolute; top: 1rem; left: 1.5rem; width: 0.75rem; height: 0.75rem; border-radius: 50%; background: #ff5f56; box-shadow: 1.25rem 0 0 #ffbd2e, 2.5rem 0 0 #27c93f; }
|
||||
.code-keyword { color: #c678dd; } .code-func { color: #61afef; } .code-string { color: #98c379; } .code-comment { color: #5c6370; font-style: italic; }
|
||||
|
||||
/* Layout Specifics */
|
||||
.work-detail-page { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #050505; z-index: 100; overflow-y: auto; }
|
||||
.gallery-image { width: 100%; margin-bottom: 4rem; filter: grayscale(20%); transition: filter 0.5s; }
|
||||
.gallery-image:hover { filter: grayscale(0%); }
|
||||
|
||||
.menu-open { transform: translateY(0); opacity: 1; pointer-events: auto; }
|
||||
.menu-closed { transform: translateY(-20px); opacity: 0; pointer-events: none; }
|
||||
|
||||
/* Tilt & Service Styles */
|
||||
.tilt-card { position: relative; transform-style: preserve-3d; transform: perspective(1000px); transition: transform 0.1s ease; }
|
||||
.tilt-inner { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); border-radius: 1rem; padding: 2rem; height: 100%; position: relative; overflow: hidden; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.05); }
|
||||
.tilt-inner::before { content: ""; position: absolute; height: 100%; width: 100%; top: 0; left: 0; background: radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%), rgba(212, 179, 131, 0.15), transparent 40%); z-index: 0; opacity: 0; transition: opacity 0.3s; pointer-events: none; }
|
||||
.tilt-card:hover .tilt-inner::before { opacity: 1; }
|
||||
|
||||
.process-accordion { display: flex; gap: 1rem; height: 400px; width: 100%; }
|
||||
.process-step { position: relative; flex: 1; background: #121214; border: 1px solid rgba(255,255,255,0.1); border-radius: 1.5rem; overflow: hidden; transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1); cursor: default; display: flex; flex-direction: column; justify-content: flex-end; padding: 2rem; }
|
||||
.process-step:hover { flex: 3; background: #1a1a1c; border-color: rgba(212, 179, 131, 0.4); }
|
||||
.process-step::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.8) 100%); z-index: 1; }
|
||||
.step-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.3; transition: opacity 0.5s, transform 0.5s; z-index: 0; filter: grayscale(100%); }
|
||||
.process-step:hover .step-bg { opacity: 0.6; transform: scale(1.05); filter: grayscale(0%); }
|
||||
.step-content { position: relative; z-index: 10; transform: translateY(0); transition: transform 0.5s; }
|
||||
.step-number { font-family: 'Playfair Display', serif; font-size: 4rem; color: rgba(255,255,255,0.1); line-height: 1; margin-bottom: 1rem; transition: color 0.3s; }
|
||||
.process-step:hover .step-number { color: #d4b383; }
|
||||
.step-desc { max-height: 0; opacity: 0; overflow: hidden; transition: all 0.5s ease; color: #888888; font-size: 0.9rem; line-height: 1.6; }
|
||||
.process-step:hover .step-desc { max-height: 100px; opacity: 1; margin-top: 1rem; }
|
||||
@media (max-width: 768px) { .process-accordion { flex-direction: column; height: auto; } .process-step { height: 120px; } .process-step:hover { flex: none; height: 200px; } }
|
||||
|
||||
/* Tech Stack Grid */
|
||||
.tech-category-title { font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; letter-spacing: 0.1em; color: #d4b383; text-transform: uppercase; margin-bottom: 0.75rem; padding-bottom: 0.25rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
|
||||
.tech-grid { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem; }
|
||||
.tech-item { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); padding: 0.35rem 0.75rem; font-family: 'Inter', sans-serif; font-size: 0.8rem; color: #a1a1aa; transition: all 0.2s; cursor: default; }
|
||||
.tech-item:hover { border-color: #d4b383; color: white; background: rgba(212, 179, 131, 0.1); }
|
||||
|
||||
/* Danmaku Styles */
|
||||
.danmaku-item { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.1); padding: 0.75rem 1.25rem; border-radius: 9999px; display: inline-flex; align-items: center; gap: 0.75rem; white-space: nowrap; color: #d4d4d8; font-size: 0.875rem; transition: all 0.3s; }
|
||||
.danmaku-item:hover { background: rgba(255, 255, 255, 0.1); border-color: #d4b383; transform: scale(1.05); color: white; z-index: 10; }
|
||||
.danmaku-row { display: flex; gap: 2rem; width: max-content; }
|
||||
.partner-logo { filter: grayscale(100%) opacity(0.5); transition: all 0.4s ease; }
|
||||
.partner-logo:hover { filter: grayscale(0%) opacity(1); transform: scale(1.1); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-art-bg text-art-text font-sans antialiased min-h-screen selection:bg-art-accent selection:text-black relative">
|
||||
|
||||
<!-- Global Noise -->
|
||||
<div class="fixed inset-0 pointer-events-none z-[60] bg-noise opacity-30 mix-blend-overlay"></div>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<header class="fixed top-0 w-full z-50 glass-nav transition-all duration-300" id="main-header">
|
||||
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
||||
<div class="cursor-pointer group" onclick="router('home')">
|
||||
<span class="font-serif text-2xl italic tracking-wider text-white group-hover:text-art-accent transition-colors">年糕崽崽.Dev</span>
|
||||
</div>
|
||||
<nav class="hidden md:flex items-center gap-10">
|
||||
<button onclick="router('home')" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline active-nav" data-target="home">首页</button>
|
||||
<button onclick="router('blog')" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" data-target="blog">思考</button>
|
||||
<button onclick="router('works')" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" data-target="works">作品</button>
|
||||
<button onclick="router('snippets')" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" data-target="snippets">代码</button>
|
||||
<button onclick="router('about')" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" data-target="about">关于</button>
|
||||
</nav>
|
||||
<div class="hidden md:flex items-center gap-4">
|
||||
<a href="#" class="text-art-muted hover:text-white transition-colors"><i data-lucide="github" class="w-5 h-5"></i></a>
|
||||
<button onclick="router('services')" class="px-5 py-2 text-xs font-bold tracking-widest uppercase border border-white/20 hover:border-art-accent hover:text-art-accent transition-all rounded-full">
|
||||
合作
|
||||
</button>
|
||||
</div>
|
||||
<button class="md:hidden text-white" onclick="toggleMobileMenu()">
|
||||
<i data-lucide="menu" class="w-6 h-6"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div id="mobile-menu" class="fixed inset-0 z-40 bg-art-bg/95 backdrop-blur-xl flex flex-col items-center justify-center space-y-8 transition-all duration-300 menu-closed md:hidden">
|
||||
<button onclick="toggleMobileMenu()" class="absolute top-6 right-6 text-white"><i data-lucide="x" class="w-8 h-8"></i></button>
|
||||
<a onclick="mobileNav('home')" class="font-serif text-3xl italic text-white hover:text-art-accent cursor-pointer">首页</a>
|
||||
<a onclick="mobileNav('blog')" class="font-serif text-3xl italic text-white hover:text-art-accent cursor-pointer">思考</a>
|
||||
<a onclick="mobileNav('works')" class="font-serif text-3xl italic text-white hover:text-art-accent cursor-pointer">作品</a>
|
||||
<a onclick="mobileNav('snippets')" class="font-serif text-3xl italic text-white hover:text-art-accent cursor-pointer">代码</a>
|
||||
<a onclick="mobileNav('about')" class="font-serif text-3xl italic text-white hover:text-art-accent cursor-pointer">关于</a>
|
||||
<a onclick="window.open('admin.html', '_blank')" class="font-mono text-sm text-art-muted mt-8">管理入口</a>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="pt-32 pb-20 px-6 max-w-7xl mx-auto relative z-10">
|
||||
|
||||
<!-- HOME SECTION -->
|
||||
<section id="home" class="page-section block">
|
||||
<!-- Hero -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 mb-24 items-center animate-reveal">
|
||||
<div class="lg:col-span-7 space-y-8">
|
||||
<p class="text-art-accent font-mono text-sm tracking-widest uppercase mb-4">前端架构师 & 创意开发者</p>
|
||||
<h1 class="font-serif text-5xl md:text-7xl lg:text-8xl leading-[1.1] text-white text-glow">
|
||||
<span class="block italic opacity-80">设计</span>
|
||||
<span class="block font-bold">铸造数字</span>
|
||||
<span class="block font-bold pl-12 md:pl-24">灵魂</span>
|
||||
</h1>
|
||||
<p class="text-art-muted text-lg md:text-xl max-w-xl leading-relaxed mt-8 font-light">
|
||||
在代码的逻辑与设计的感性之间寻找平衡。我是<span class="text-white font-bold">年糕崽崽</span>,不仅仅构建页面,更在构建<span class="text-white border-b border-white/20 pb-0.5">沉浸式体验</span>。
|
||||
</p>
|
||||
<div class="pt-8 flex items-center gap-6">
|
||||
<button onclick="router('works')" class="group flex items-center gap-2 text-white border-b border-white pb-1 hover:text-art-accent hover:border-art-accent transition-all">
|
||||
<span>浏览作品集</span>
|
||||
<i data-lucide="arrow-right" class="w-4 h-4 transform group-hover:translate-x-1 transition-transform"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-5 relative h-[400px] lg:h-[600px] w-full flex items-center justify-center">
|
||||
<div class="relative w-full h-full">
|
||||
<div class="absolute inset-0 border border-white/10 rounded-full rotate-12 scale-90"></div>
|
||||
<div class="absolute inset-0 border border-white/5 rounded-full -rotate-6 scale-75"></div>
|
||||
<div id="home-hero-work" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-64 h-80 bg-gradient-to-br from-white/10 to-transparent backdrop-blur-md border border-white/10 rounded-sm shadow-2xl rotate-6 hover:rotate-0 transition-transform duration-700 z-10 flex flex-col p-6 justify-between cursor-pointer">
|
||||
<div class="text-white/50 text-xs">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bento Grid -->
|
||||
<div class="space-y-6 animate-reveal" style="animation-delay: 0.2s;">
|
||||
<div class="flex items-end justify-between border-b border-white/10 pb-4 mb-8">
|
||||
<h2 class="font-serif text-3xl italic text-white">精选内容</h2>
|
||||
<span class="font-mono text-xs text-art-muted">下滑探索更多</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-[300px]" id="bento-grid">
|
||||
<div class="md:col-span-3 h-[300px] border border-white/5 rounded-xl bg-white/5 animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Dynamic Content Sections -->
|
||||
<section id="blog" class="page-section hidden animate-slide-down">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="mb-16 text-center">
|
||||
<h2 class="font-serif text-5xl italic text-white mb-6">深度思考</h2>
|
||||
<p class="text-art-muted max-w-lg mx-auto">关于前端技术、交互设计以及数字艺术的深度思考。</p>
|
||||
</div>
|
||||
<div class="space-y-12" id="blog-list-container"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="works" class="page-section hidden animate-slide-down">
|
||||
<div class="mb-16">
|
||||
<h2 class="font-serif text-5xl italic text-white mb-6">精选作品</h2>
|
||||
<p class="text-art-muted">这里展示了我参与设计和开发的核心项目。</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-20" id="works-list-container"></div>
|
||||
</section>
|
||||
|
||||
<!-- Work Detail -->
|
||||
<section id="work-detail" class="work-detail-page page-section hidden">
|
||||
<button onclick="router('works')" class="fixed top-8 right-8 z-[60] mix-blend-difference text-white hover:scale-110 transition-transform">
|
||||
<div class="rounded-full border border-white/20 p-4 backdrop-blur-md bg-white/5"><i data-lucide="x" class="w-6 h-6"></i></div>
|
||||
</button>
|
||||
<div class="fixed top-0 left-0 h-1 bg-art-accent z-[60] w-0 transition-all duration-100" id="scroll-progress"></div>
|
||||
<div class="relative w-full" id="work-detail-content">
|
||||
<div class="relative h-[85vh] w-full overflow-hidden group">
|
||||
<img id="work-hero-img" src="" class="absolute inset-0 w-full h-full object-cover filter grayscale group-hover:grayscale-0 transition-all duration-1000 scale-105" alt="">
|
||||
<div class="absolute inset-0 bg-black/40"></div>
|
||||
<div class="absolute bottom-0 left-0 p-8 md:p-20 w-full">
|
||||
<div class="flex items-end justify-between border-t border-white/30 pt-8 animate-slide-down">
|
||||
<div>
|
||||
<span id="work-category" class="font-mono text-art-accent text-sm tracking-[0.2em] uppercase mb-2 block"></span>
|
||||
<h1 id="work-title" class="font-serif text-5xl md:text-8xl text-white leading-[0.9] mix-blend-overlay"></h1>
|
||||
</div>
|
||||
<span id="work-year" class="hidden md:block font-mono text-white/50 text-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row max-w-[1920px] mx-auto min-h-screen">
|
||||
<div class="md:w-1/3 p-8 md:p-16 md:sticky md:top-0 md:h-screen md:max-h-screen overflow-y-auto custom-scrollbar flex flex-col justify-between border-r border-white/5 bg-[#050505]">
|
||||
<div class="space-y-12">
|
||||
<div><h3 class="font-mono text-xs text-art-muted uppercase tracking-widest mb-4">关于项目</h3><div id="work-desc" class="text-white/80 font-light leading-relaxed text-lg font-serif"></div></div>
|
||||
<div><h3 class="font-mono text-xs text-art-muted uppercase tracking-widest mb-6">技术全景</h3><div id="work-tech-stack" class="space-y-6"></div></div>
|
||||
</div>
|
||||
<div class="mt-12"><a id="work-link-live" href="#" target="_blank" class="group flex items-center justify-between w-full py-6 border-t border-white/10 hover:bg-white/5 transition-colors"><span class="font-serif text-2xl italic text-white group-hover:text-art-accent transition-colors">访问线上项目</span><i data-lucide="arrow-up-right" class="w-6 h-6 text-white group-hover:rotate-45 transition-transform"></i></a></div>
|
||||
</div>
|
||||
<div class="md:w-2/3 bg-[#080808] flex flex-col">
|
||||
<div id="work-gallery" class="flex flex-col flex-grow"></div>
|
||||
<div class="h-[40vh] flex items-center justify-center border-t border-white/5 bg-[#050505] cursor-pointer group hover:bg-white/5 transition-colors" onclick="openNextWork()">
|
||||
<div class="text-center"><p class="font-mono text-xs text-art-muted mb-4 tracking-widest">下一个作品</p><h2 class="font-serif text-5xl text-white group-hover:italic transition-all">返回作品列表</h2></div>
|
||||
</div>
|
||||
<footer class="py-8 text-center border-t border-white/5 relative z-10 bg-[#050505]"><p class="text-art-muted text-xs font-mono tracking-widest opacity-50">© 2024 年糕崽崽. 保留所有权利.</p></footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Snippets -->
|
||||
<section id="snippets" class="page-section hidden animate-slide-down">
|
||||
<div class="mb-12 text-center">
|
||||
<h2 class="font-serif text-5xl italic text-white mb-6">代码实验室</h2>
|
||||
<p class="text-art-muted">点击下方卡片查看代码与实时效果。</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8" id="snippets-grid">
|
||||
<!-- JS Injected -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Services (About/Contact) -->
|
||||
<section id="services" class="page-section hidden animate-slide-down">
|
||||
<div class="py-16 text-center">
|
||||
<h2 class="font-serif text-5xl italic text-white mb-6">共创数字未来</h2>
|
||||
<p class="text-art-muted text-lg max-w-2xl mx-auto">用代码构建骨架,用设计触动灵魂。</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-32 px-4" id="services-grid">
|
||||
<div class="tilt-card group h-96"><div class="tilt-inner flex flex-col justify-between"><div class="w-16 h-16 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-6 border border-blue-500/20 group-hover:scale-110 transition-transform duration-500"><i data-lucide="layers" class="w-8 h-8"></i></div><div><h3 class="text-2xl font-bold text-white mb-4">前端架构设计</h3><p class="text-art-muted text-sm leading-relaxed">为复杂的大型应用提供可扩展的架构方案。</p></div></div></div>
|
||||
<div class="tilt-card group h-96"><div class="tilt-inner flex flex-col justify-between border-art-accent/20 bg-white/5"><div class="w-16 h-16 rounded-2xl bg-art-accent/10 flex items-center justify-center text-art-accent mb-6 border border-art-accent/20 group-hover:scale-110 transition-transform duration-500 shadow-[0_0_30px_-10px_rgba(212,179,131,0.3)]"><i data-lucide="wand-2" class="w-8 h-8"></i></div><div><h3 class="text-2xl font-bold text-white mb-4">创意交互开发</h3><p class="text-art-muted text-sm leading-relaxed">利用 WebGL (Three.js) 和 GSAP 打造令人过目难忘的着陆页。</p></div></div></div>
|
||||
<div class="tilt-card group h-96"><div class="tilt-inner flex flex-col justify-between"><div class="w-16 h-16 rounded-2xl bg-purple-500/10 flex items-center justify-center text-purple-400 mb-6 border border-purple-500/20 group-hover:scale-110 transition-transform duration-500"><i data-lucide="smartphone" class="w-8 h-8"></i></div><div><h3 class="text-2xl font-bold text-white mb-4">跨平台应用</h3><p class="text-art-muted text-sm leading-relaxed">使用 UniApp 或 React Native 开发高质量的移动端应用。</p></div></div></div>
|
||||
</div>
|
||||
<!-- Danmaku & Partners -->
|
||||
<div class="py-20 mb-32 overflow-hidden relative">
|
||||
<div class="absolute left-0 top-0 w-20 h-full bg-gradient-to-r from-art-bg to-transparent z-10"></div>
|
||||
<div class="absolute right-0 top-0 w-20 h-full bg-gradient-to-l from-art-bg to-transparent z-10"></div>
|
||||
<h3 class="text-3xl font-serif text-white text-center mb-12 italic">客户原声</h3>
|
||||
<div class="flex flex-col gap-6" id="danmaku-container">
|
||||
<!-- JS Injected -->
|
||||
<div class="danmaku-row animate-marquee hover:[animation-play-state:paused]">
|
||||
<div class="danmaku-item"><div class="w-6 h-6 rounded-full bg-blue-500/20"></div><span>"从未见过如此丝滑的 WebGL 体验!"</span></div>
|
||||
<div class="danmaku-item"><div class="w-6 h-6 rounded-full bg-purple-500/20"></div><span>"代码质量非常高,易于维护。"</span></div>
|
||||
<div class="danmaku-item"><div class="w-6 h-6 rounded-full bg-green-500/20"></div><span>"细节把控令人惊叹,强烈推荐!"</span></div>
|
||||
<div class="danmaku-item"><div class="w-6 h-6 rounded-full bg-blue-500/20"></div><span>"从未见过如此丝滑的 WebGL 体验!"</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- CTA -->
|
||||
<div class="max-w-4xl mx-auto text-center px-6 mb-20">
|
||||
<div class="p-12 rounded-3xl bg-gradient-to-b from-white/10 to-transparent border border-white/10 relative overflow-hidden">
|
||||
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-64 h-64 bg-art-accent/20 blur-[100px] -z-10"></div>
|
||||
<h2 class="text-4xl md:text-5xl font-serif text-white mb-6">准备好开始了吗?</h2>
|
||||
<p class="text-art-muted mb-8 max-w-xl mx-auto">无论是一个疯狂的想法,还是一个具体的业务需求,我都乐意倾听。</p>
|
||||
<button onclick="openInquiry()" class="inline-flex items-center gap-2 px-8 py-4 bg-white text-black rounded-full font-bold hover:scale-105 transition-transform"><span>发起合作咨询</span><i data-lucide="arrow-right" class="w-5 h-5"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Partners -->
|
||||
<div class="max-w-4xl mx-auto px-6 mb-32 border-t border-white/5 pt-12">
|
||||
<p class="text-center text-xs font-mono text-art-muted tracking-widest uppercase mb-8">Trusted By</p>
|
||||
<div class="flex flex-wrap justify-center gap-8 md:gap-16 opacity-50">
|
||||
<span class="partner-logo text-xl font-bold font-serif text-white cursor-pointer">VOGUE</span>
|
||||
<span class="partner-logo text-xl font-bold font-mono text-white cursor-pointer">WIRED</span>
|
||||
<span class="partner-logo text-xl font-bold font-sans text-white cursor-pointer">stripe</span>
|
||||
<span class="partner-logo text-xl font-bold font-serif italic text-white cursor-pointer">Monocle</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section id="about" class="page-section hidden animate-slide-down">
|
||||
<div class="max-w-4xl mx-auto py-10">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
<div class="space-y-8">
|
||||
<div><span class="text-art-accent font-mono text-sm tracking-widest uppercase">个人档案</span><h2 class="font-serif text-5xl italic text-white mt-2 mb-6">关于我</h2></div>
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="w-24 h-24 rounded-full overflow-hidden border-2 border-art-accent/50 shadow-2xl"><img src="https://api.dicebear.com/7.x/avataaars/svg?seed=NianGao&backgroundColor=b6e3f4" alt="Avatar" class="w-full h-full object-cover"></div>
|
||||
<div class="space-y-2"><h3 class="text-2xl font-bold text-white">年糕崽崽</h3><div class="flex items-center gap-2 text-sm text-art-muted"><i data-lucide="map-pin" class="w-4 h-4 text-art-accent"></i><span>中国 · 浙江杭州</span></div></div>
|
||||
</div>
|
||||
<div class="prose prose-invert text-art-muted font-light leading-relaxed">
|
||||
<p>嗨!我是<strong>年糕崽崽</strong>,一名热衷于探索数字边界的前端架构师。我的职业生涯始于对像素级完美的执念。目前,我专注于高性能 B 端应用的体验升级。</p>
|
||||
</div>
|
||||
<div class="bg-white/5 border border-white/5 p-6 rounded-xl backdrop-blur-sm mt-6">
|
||||
<h4 class="text-white font-bold mb-4 flex items-center gap-2"><i data-lucide="contact" class="w-4 h-4 text-art-accent"></i> 联系方式</h4>
|
||||
<div class="space-y-3 text-sm">
|
||||
<a href="mailto:niangao@example.com" class="flex items-center gap-3 text-art-muted hover:text-white transition-colors"><i data-lucide="mail" class="w-4 h-4"></i><span>hello@niangao.dev</span></a>
|
||||
<div class="flex items-center gap-3 text-art-muted"><i data-lucide="message-circle" class="w-4 h-4"></i><span>WeChat: Niangao_Dev</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-8">
|
||||
<div class="bg-white/5 border border-white/5 p-8 rounded-2xl backdrop-blur-sm">
|
||||
<h3 class="text-xl font-bold text-white mb-6 flex items-center gap-2"><i data-lucide="cpu" class="w-5 h-5 text-art-accent"></i> 技术栈</h3>
|
||||
<div class="flex flex-wrap gap-2 mb-10"><span class="px-3 py-1 bg-white/5 rounded-full text-xs text-white/80 border border-white/10">Vue 3</span><span class="px-3 py-1 bg-white/5 rounded-full text-xs text-white/80 border border-white/10">React</span><span class="px-3 py-1 bg-white/5 rounded-full text-xs text-white/80 border border-white/10">TypeScript</span><span class="px-3 py-1 bg-white/5 rounded-full text-xs text-white/80 border border-white/10">Three.js</span><span class="px-3 py-1 bg-white/5 rounded-full text-xs text-white/80 border border-white/10">Golang</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="py-8 text-center border-t border-white/5 relative z-10">
|
||||
<div class="flex justify-center items-center gap-4 mb-4"><a onclick="window.open('admin.html', '_blank')" class="text-xs text-art-muted/30 hover:text-art-accent cursor-pointer">管理入口</a></div>
|
||||
<p class="text-art-muted text-xs font-mono tracking-widest opacity-50">© 2024 年糕崽崽. 保留所有权利.</p>
|
||||
</footer>
|
||||
|
||||
<!-- Snippet Modal -->
|
||||
<div id="snippet-modal" class="fixed inset-0 z-[100] modal-overlay hidden flex items-center justify-center p-4 md:p-10 opacity-0 transition-opacity duration-300">
|
||||
<div class="bg-[#0a0a0c] border border-white/10 w-full max-w-6xl h-full md:h-[80vh] rounded-2xl flex flex-col overflow-hidden shadow-2xl transform scale-95 transition-transform duration-300" id="modal-content">
|
||||
<div class="h-16 border-b border-white/10 flex items-center justify-between px-6 bg-white/5">
|
||||
<div class="flex items-center gap-3"><i data-lucide="code-2" class="text-art-accent"></i><span class="font-bold text-white" id="modal-title">Code</span></div>
|
||||
<button onclick="closeSnippet()" class="p-2 hover:bg-white/10 rounded-full text-white/50 hover:text-white transition-colors"><i data-lucide="x" class="w-5 h-5"></i></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col md:flex-row h-full overflow-hidden">
|
||||
<div class="w-full md:w-1/2 bg-[#0d0d0d] overflow-auto p-6 border-r border-white/10 relative group">
|
||||
<button class="absolute top-4 right-4 text-xs bg-white/10 px-2 py-1 rounded text-white/50 hover:text-white" onclick="showToast('代码已复制')">复制</button>
|
||||
<pre class="font-mono text-sm leading-relaxed text-gray-300" id="modal-code"></pre>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 preview-area relative flex items-center justify-center overflow-hidden" id="modal-preview-container"><div class="text-white/30 font-mono">Loading Preview...</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inquiry Modal -->
|
||||
<div id="inquiry-modal" class="fixed inset-0 z-[100] bg-[#050505]/95 backdrop-blur-xl hidden items-center justify-center p-4 transition-opacity duration-300">
|
||||
<div class="relative w-full max-w-4xl bg-[#080808] border border-white/10 overflow-hidden flex flex-col md:flex-row shadow-2xl">
|
||||
<div class="hidden md:flex w-1/3 bg-[#0c0c0c] border-r border-white/5 p-12 flex-col justify-between relative overflow-hidden">
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-noise opacity-10"></div>
|
||||
<div class="absolute -top-20 -left-20 w-64 h-64 bg-art-accent/10 rounded-full blur-[80px]"></div>
|
||||
<div class="relative z-10"><h3 class="font-serif text-3xl text-white italic mb-6 leading-tight">Let's build<br>something<br>unreal.</h3><p class="font-mono text-xs text-white/40 leading-relaxed">告诉我你的构想。<br>我将提供架构与代码,<br>将想象变为现实。</p></div>
|
||||
<div class="relative z-10 font-mono text-[10px] text-white/20 tracking-widest">LAT: 30.2741° N<br>LONG: 120.1551° E<br>HANGZHOU, CN</div>
|
||||
</div>
|
||||
<div class="w-full md:w-2/3 p-10 md:p-16 relative bg-[#080808]">
|
||||
<button onclick="document.getElementById('inquiry-modal').classList.add('hidden')" class="absolute top-6 right-6 text-white/30 hover:text-white transition-colors z-20"><i data-lucide="x" class="w-6 h-6"></i></button>
|
||||
<form id="inquiry-form" class="space-y-10 relative z-10">
|
||||
<div class="space-y-8">
|
||||
<p class="font-mono text-xs text-art-accent uppercase tracking-widest border-b border-white/10 pb-2 mb-6">01 // 身份识别</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div class="relative group/input">
|
||||
<input name="name" type="text" class="peer w-full bg-transparent border-b border-white/20 py-2 text-white font-serif text-lg focus:border-art-accent outline-none transition-colors placeholder-transparent" placeholder="Name">
|
||||
<label class="absolute left-0 top-2 text-white/30 text-xs font-mono transition-all peer-focus:-top-4 peer-focus:text-art-accent peer-not-placeholder-shown:-top-4 peer-not-placeholder-shown:text-white/50 pointer-events-none">您的称呼</label>
|
||||
</div>
|
||||
<div class="relative group/input">
|
||||
<input name="company" type="text" class="peer w-full bg-transparent border-b border-white/20 py-2 text-white font-serif text-lg focus:border-art-accent outline-none transition-colors placeholder-transparent" placeholder="Company">
|
||||
<label class="absolute left-0 top-2 text-white/30 text-xs font-mono transition-all peer-focus:-top-4 peer-focus:text-art-accent peer-not-placeholder-shown:-top-4 peer-not-placeholder-shown:text-white/50 pointer-events-none">公司 / 组织</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative group/input">
|
||||
<input name="contact" type="text" class="peer w-full bg-transparent border-b border-white/20 py-2 text-white font-serif text-lg focus:border-art-accent outline-none transition-colors placeholder-transparent" placeholder="Contact">
|
||||
<label class="absolute left-0 top-2 text-white/30 text-xs font-mono transition-all peer-focus:-top-4 peer-focus:text-art-accent peer-not-placeholder-shown:-top-4 peer-not-placeholder-shown:text-white/50 pointer-events-none">联系方式 (Email / WeChat)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
<p class="font-mono text-xs text-art-accent uppercase tracking-widest border-b border-white/10 pb-2 mb-6">02 // 项目细节</p>
|
||||
<div class="space-y-3">
|
||||
<label class="text-xs font-mono text-white/30 block mb-2">预估预算范围</label>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<label class="cursor-pointer group"><input type="radio" name="budget" value="10k-50k" class="peer hidden"><span class="px-4 py-2 border border-white/10 rounded-none text-xs font-mono text-white/50 hover:border-art-accent hover:text-white transition-all peer-checked:bg-art-accent peer-checked:text-black peer-checked:border-art-accent">10k - 50k</span></label>
|
||||
<label class="cursor-pointer group"><input type="radio" name="budget" value="50k-200k" class="peer hidden"><span class="px-4 py-2 border border-white/10 rounded-none text-xs font-mono text-white/50 hover:border-art-accent hover:text-white transition-all peer-checked:bg-art-accent peer-checked:text-black peer-checked:border-art-accent">50k - 200k</span></label>
|
||||
<label class="cursor-pointer group"><input type="radio" name="budget" value="200k+" class="peer hidden"><span class="px-4 py-2 border border-white/10 rounded-none text-xs font-mono text-white/50 hover:border-art-accent hover:text-white transition-all peer-checked:bg-art-accent peer-checked:text-black peer-checked:border-art-accent">200k +</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative group/input mt-8">
|
||||
<textarea name="description" rows="1" class="peer w-full bg-transparent border-b border-white/20 py-2 text-white font-serif text-lg focus:border-art-accent outline-none transition-colors placeholder-transparent resize-none min-h-[40px]" placeholder="Brief" oninput="this.style.height = ''; this.style.height = this.scrollHeight + 'px'"></textarea>
|
||||
<label class="absolute left-0 top-2 text-white/30 text-xs font-mono transition-all peer-focus:-top-4 peer-focus:text-art-accent peer-not-placeholder-shown:-top-4 peer-not-placeholder-shown:text-white/50 pointer-events-none">一句话描述需求</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-6">
|
||||
<button type="submit" class="group relative w-full overflow-hidden bg-white text-black font-bold py-4 px-8 transition-all hover:bg-art-accent">
|
||||
<div class="absolute inset-0 w-0 bg-art-accent transition-all duration-[250ms] ease-out group-hover:w-full"></div>
|
||||
<span class="relative flex items-center justify-between z-10"><span class="font-mono uppercase tracking-widest text-sm">INITIATE_PROTOCOL // 发送</span><i data-lucide="arrow-right" class="w-4 h-4 transition-transform group-hover:translate-x-1"></i></span>
|
||||
</button>
|
||||
<p class="mt-4 text-[10px] text-white/20 font-mono text-center tracking-widest">SECURED BY DIGITAL FINGERPRINTING</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
const API_BASE = "http://localhost:8080/api";
|
||||
|
||||
// Global State for Data
|
||||
const store = {
|
||||
works: [],
|
||||
posts: [],
|
||||
snippets: [],
|
||||
testimonials: []
|
||||
};
|
||||
|
||||
let currentWorkId = null;
|
||||
|
||||
// Static fallback data for reliability
|
||||
const workProjects = {
|
||||
'nova': {
|
||||
id: 'nova',
|
||||
title: "Nova 交易平台",
|
||||
category: "金融科技",
|
||||
year: "2023",
|
||||
heroImg: "https://images.unsplash.com/photo-1611974765270-ca12586343bb?q=80&w=2070",
|
||||
desc: `<p>Nova 是一个专为机构交易员设计的高频交易终端。我们面临的最大挑战是如何在处理毫秒级市场数据的同时,保持界面的流畅响应。我们采用 <strong>Web Worker</strong> 来处理繁重的数据计算,避免阻塞主线程。</p>`,
|
||||
techStack: [{ category: '前端层', items: ['React 18', 'TypeScript', 'D3.js'] }, { category: '后端服务', items: ['Golang', 'gRPC'] }],
|
||||
gallery: ["https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070", "https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070"],
|
||||
links: {live: "#"}, next: 'archdaily'
|
||||
},
|
||||
'archdaily': {
|
||||
id: 'archdaily',
|
||||
title: "ArchDaily 网站重构",
|
||||
category: "建筑设计",
|
||||
year: "2022",
|
||||
heroImg: "https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=2070",
|
||||
desc: `<p>ArchDaily 是全球最受欢迎的建筑网站之一。这次重构的目标是提升移动端体验。我们使用了 <strong>Nuxt 3</strong> 进行服务端渲染(SSR)。</p>`,
|
||||
techStack: [{ category: '核心前端', items: ['Vue 3', 'Nuxt.js', 'GSAP'] }, { category: 'CMS', items: ['Strapi'] }],
|
||||
gallery: ["https://images.unsplash.com/photo-1503387762-592deb58ef4e?q=80&w=2089", "https://images.unsplash.com/photo-1518005020951-ecc859466abc?q=80&w=1920"],
|
||||
links: {live: "#"}, next: 'nova'
|
||||
}
|
||||
};
|
||||
|
||||
const snippetsData = {
|
||||
'mouse': { title: 'React 鼠标追踪 Hook', code: `import { useState, useEffect } from 'react';\n\nexport const useMousePosition = () => {\n const [pos, setPos] = useState({ x: 0, y: 0 });\n useEffect(() => {\n const update = (e) => setPos({ x: e.clientX, y: e.clientY });\n window.addEventListener('mousemove', update);\n return () => window.removeEventListener('mousemove', update);\n }, []);\n return pos;\n};`, type: 'mouse' },
|
||||
'glass': { title: 'CSS 极致毛玻璃效果', code: `.glass-panel {\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(16px);\n box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);\n}`, type: 'glass' },
|
||||
'noise': { title: 'SVG 噪点纹理滤镜', code: `<filter id="noise">\n <feTurbulence type="fractalNoise" baseFrequency="0.8" />\n</filter>`, type: 'noise' }
|
||||
};
|
||||
|
||||
// --- Core UI Functions ---
|
||||
function showToast(message, type = 'success') {
|
||||
const container = document.getElementById('toast-container');
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type === 'success' ? 'toast-success' : 'toast-error'}`;
|
||||
toast.innerHTML = `<i data-lucide="${type === 'success' ? 'check-circle' : 'alert-circle'}" class="w-5 h-5 ${type === 'success' ? 'text-[#d4b383]' : 'text-red-500'}"></i><span class="text-sm font-medium">${message}</span>`;
|
||||
container.appendChild(toast);
|
||||
lucide.createIcons();
|
||||
requestAnimationFrame(() => toast.classList.add('show'));
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 400);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function router(targetId) {
|
||||
const effectiveTarget = (targetId === 'blog-detail' || targetId === 'work-detail') ? 'works' : (targetId === 'blog-detail' ? 'blog' : targetId);
|
||||
const navTarget = targetId === 'blog-detail' ? 'blog' : (targetId === 'work-detail' ? 'works' : targetId);
|
||||
|
||||
document.querySelectorAll('.nav-item').forEach(btn => {
|
||||
if (btn.dataset.target === navTarget) {
|
||||
btn.classList.add('text-white', 'after:w-full');
|
||||
btn.classList.remove('text-art-muted');
|
||||
} else {
|
||||
btn.classList.remove('text-white', 'after:w-full');
|
||||
btn.classList.add('text-art-muted');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.page-section').forEach(section => {
|
||||
if (section.id === targetId) {
|
||||
section.classList.remove('hidden');
|
||||
section.classList.remove('animate-slide-down');
|
||||
void section.offsetWidth;
|
||||
if(targetId === 'work-detail') {
|
||||
const detailPage = document.querySelector('.work-detail-page');
|
||||
if(detailPage) detailPage.scrollTop = 0;
|
||||
} else {
|
||||
section.classList.add('animate-slide-down');
|
||||
}
|
||||
} else {
|
||||
section.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
if(targetId !== 'work-detail') window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function toggleMobileMenu() {
|
||||
const menu = document.getElementById('mobile-menu');
|
||||
if (menu.classList.contains('menu-closed')) {
|
||||
menu.classList.remove('menu-closed');
|
||||
menu.classList.add('menu-open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
menu.classList.remove('menu-open');
|
||||
menu.classList.add('menu-closed');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
function mobileNav(target) { router(target); toggleMobileMenu(); }
|
||||
|
||||
// --- Data & Rendering ---
|
||||
async function init() {
|
||||
try {
|
||||
const [worksRes, postsRes, snippetsRes] = await Promise.all([
|
||||
fetch(`${API_BASE}/works`).catch(()=>({ok:false})),
|
||||
fetch(`${API_BASE}/posts`).catch(()=>({ok:false})),
|
||||
fetch(`${API_BASE}/snippets`).catch(()=>({ok:false}))
|
||||
]);
|
||||
|
||||
// Use static fallback if API fails or returns empty
|
||||
store.works = (worksRes.ok && (await worksRes.json()).length > 0) ? await worksRes.json() : Object.values(workProjects);
|
||||
store.posts = (postsRes.ok && (await postsRes.json()).length > 0) ? await postsRes.json() : [
|
||||
{id:'refactor', title:"重构的艺术:如何优雅地处理遗留代码", category:"工程化", date:"2026-01-12"},
|
||||
{id:'shader', title:"着色器魔法:从零开始写一个噪声生成器", category:"图形渲染", date:"2025-12-08"}
|
||||
];
|
||||
store.snippets = snippetsRes.ok ? await snippetsRes.json() : [];
|
||||
|
||||
renderBento();
|
||||
renderWorksList();
|
||||
renderBlogList();
|
||||
} catch (e) {
|
||||
console.warn("Init fallback");
|
||||
store.works = Object.values(workProjects);
|
||||
renderBento();
|
||||
renderWorksList();
|
||||
}
|
||||
}
|
||||
|
||||
function renderBento() {
|
||||
const heroWork = store.works[0];
|
||||
if(heroWork) {
|
||||
const heroEl = document.getElementById('home-hero-work');
|
||||
heroEl.onclick = () => openWork(heroWork.id);
|
||||
heroEl.innerHTML = `
|
||||
<div class="text-xs font-mono text-white/50">最新作品</div>
|
||||
<div class="font-serif text-3xl italic text-white">${heroWork.title.replace(' ', '<br>')}</div>
|
||||
<div class="flex justify-end"><i data-lucide="arrow-up-right" class="text-white"></i></div>
|
||||
`;
|
||||
}
|
||||
|
||||
const post = store.posts[0] || {title: "当极简主义遇见复杂数据", category: "设计思维", date: "2025-10-24"};
|
||||
const gridHTML = `
|
||||
<div class="md:col-span-2 art-card rounded-2xl p-8 flex flex-col justify-end group cursor-pointer" onclick="openBlog('${post.id || 'dashboard'}')">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent z-10"></div>
|
||||
<div class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564')] bg-cover bg-center transition-transform duration-700 group-hover:scale-105 opacity-60 mix-blend-overlay"></div>
|
||||
<div class="relative z-20 space-y-2">
|
||||
<div class="flex items-center gap-3"><span class="px-2 py-1 text-[10px] font-mono border border-white/20 rounded-full text-white backdrop-blur-sm">${post.category}</span></div>
|
||||
<h3 class="font-serif text-3xl text-white group-hover:text-art-accent transition-colors">${post.title}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="art-card rounded-2xl p-8 flex flex-col justify-between group">
|
||||
<div class="flex justify-between items-start"><i data-lucide="code-2" class="w-8 h-8 text-white/40 group-hover:text-art-accent transition-colors"></i><i data-lucide="arrow-up-right" class="w-5 h-5 text-white/20"></i></div>
|
||||
<div><div class="text-4xl font-mono font-bold text-white mb-2">120+</div><div class="text-sm text-art-muted">开源提交</div></div>
|
||||
</div>
|
||||
<div class="art-card rounded-2xl p-6 md:p-8 flex flex-col justify-between group cursor-pointer bg-[#050505]" onclick="router('snippets')">
|
||||
<div class="font-mono text-xs text-art-muted mb-4">// React Hook</div>
|
||||
<div class="font-mono text-sm text-gray-400 overflow-hidden opacity-60 group-hover:opacity-100 transition-opacity">useMousePosition...</div>
|
||||
<div class="mt-4 flex items-center gap-2 text-sm font-medium text-white"><span>访问代码实验室</span><i data-lucide="chevron-right" class="w-4 h-4 text-art-accent"></i></div>
|
||||
</div>
|
||||
<div class="md:col-span-2 art-card rounded-2xl p-8 md:p-12 flex flex-col md:flex-row items-center justify-between gap-8 group cursor-pointer bg-gradient-to-r from-art-surface to-transparent" onclick="router('services')"><div class="space-y-4"><h3 class="font-serif text-3xl text-white">需要独特的前端架构?</h3><p class="text-art-muted max-w-sm">从 WebGL 3D 到高性能 SaaS。</p></div><div class="h-16 w-16 rounded-full border border-white/20 flex items-center justify-center group-hover:bg-white group-hover:text-black transition-all duration-300"><i data-lucide="arrow-right" class="w-6 h-6"></i></div></div>
|
||||
`;
|
||||
document.getElementById('bento-grid').innerHTML = gridHTML;
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
function renderWorksList() {
|
||||
const container = document.getElementById('works-list-container');
|
||||
container.innerHTML = store.works.map((work, index) => {
|
||||
const isEven = index % 2 === 0;
|
||||
const imgOrder = isEven ? '' : 'md:order-2';
|
||||
const textOrder = isEven ? '' : 'md:order-1 md:text-right';
|
||||
const btnAlign = isEven ? '' : 'md:justify-end';
|
||||
|
||||
return `
|
||||
<div class="group grid grid-cols-1 md:grid-cols-2 gap-10 items-center cursor-pointer" onclick="openWork('${work.id}')">
|
||||
<div class="${imgOrder} relative aspect-[4/3] overflow-hidden rounded-sm bg-gray-900 border border-white/5">
|
||||
<div class="absolute inset-0 bg-gradient-to-tr from-purple-900/40 to-blue-900/40 mix-blend-color-burn z-10"></div>
|
||||
<div class="absolute inset-0 bg-[url('${work.heroImg}')] bg-cover bg-center group-hover:scale-105 transition-transform duration-700 opacity-80"></div>
|
||||
</div>
|
||||
<div class="space-y-6 ${textOrder}">
|
||||
<div class="font-mono text-xs text-art-accent">${work.year} • ${work.category}</div>
|
||||
<h3 class="font-serif text-4xl text-white group-hover:text-art-accent transition-colors">${work.title}</h3>
|
||||
<p class="text-art-muted font-light leading-relaxed line-clamp-3">${work.desc ? work.desc.replace(/<[^>]*>?/gm, '') : ''}</p>
|
||||
<div class="pt-4 flex ${btnAlign}"><span class="inline-flex items-center gap-2 text-sm font-medium text-white border-b border-transparent hover:border-art-accent hover:text-art-accent transition-all">查看详情 <i data-lucide="arrow-right" class="w-4 h-4"></i></span></div>
|
||||
</div>
|
||||
</div>
|
||||
`}).join('');
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
function renderBlogList() {
|
||||
const container = document.getElementById('blog-list-container');
|
||||
// Using store.posts which is populated in init
|
||||
container.innerHTML = store.posts.map(post => `
|
||||
<article class="group cursor-pointer border-b border-white/5 pb-12" onclick="openBlog('${post.id}')">
|
||||
<div class="flex flex-col md:flex-row gap-8 items-start">
|
||||
<div class="md:w-1/4 pt-2">
|
||||
<span class="font-mono text-xs text-art-accent block mb-1">${post.category}</span>
|
||||
<span class="text-sm text-art-muted">${post.date || '2025-01-01'}</span>
|
||||
</div>
|
||||
<div class="md:w-3/4 space-y-4">
|
||||
<h3 class="font-serif text-3xl text-white group-hover:text-art-accent transition-colors leading-tight">${post.title}</h3>
|
||||
<div class="text-xs text-art-accent font-medium mt-4 group-hover:text-white transition-colors">阅读全文 -></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`).join('');
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
function renderSnippetsList() {
|
||||
const container = document.getElementById('snippets-grid');
|
||||
const data = Object.keys(snippetsData).map(k => snippetsData[k]);
|
||||
container.innerHTML = data.map(s => `
|
||||
<div class="art-card rounded-xl p-0 overflow-hidden bg-[#0d0d0d] border border-white/10 cursor-pointer group hover:border-art-accent/50 transition-colors" onclick="openSnippet('${s.type}')">
|
||||
<div class="flex items-center justify-between px-4 py-3 bg-white/5 border-b border-white/5">
|
||||
<div class="flex gap-2"><div class="w-2.5 h-2.5 rounded-full bg-red-500/50"></div><div class="w-2.5 h-2.5 rounded-full bg-yellow-500/50"></div><div class="w-2.5 h-2.5 rounded-full bg-green-500/50"></div></div>
|
||||
<span class="text-xs font-mono text-art-muted">${s.title}</span>
|
||||
<div class="text-xs font-mono text-art-accent opacity-0 group-hover:opacity-100 transition-opacity">点击运行预览 -></div>
|
||||
</div>
|
||||
<div class="p-6 overflow-x-auto font-mono text-sm leading-relaxed pointer-events-none text-gray-500 line-clamp-3">${s.code}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
// --- Detail Actions ---
|
||||
function openWork(id) {
|
||||
let work = store.works.find(w => w.id == id);
|
||||
if(!work) return; // Should not happen with valid ID
|
||||
|
||||
currentWorkId = id;
|
||||
document.getElementById('work-title').innerText = work.title;
|
||||
document.getElementById('work-category').innerText = work.category;
|
||||
document.getElementById('work-year').innerText = work.year;
|
||||
document.getElementById('work-desc').innerHTML = work.desc;
|
||||
document.getElementById('work-hero-img').src = work.heroImg;
|
||||
document.getElementById('work-link-live').href = work.links.live;
|
||||
|
||||
// Tech Stack
|
||||
const techContainer = document.getElementById('work-tech-stack');
|
||||
techContainer.innerHTML = work.techStack.map(group => `
|
||||
<div class="mb-6"><div class="tech-category-title">${group.category}</div><div class="tech-grid">${group.items.map(item => `<div class="tech-item">${item}</div>`).join('')}</div></div>
|
||||
`).join('');
|
||||
|
||||
// Gallery
|
||||
const galleryContainer = document.getElementById('work-gallery');
|
||||
galleryContainer.innerHTML = work.gallery.map((img, index) => {
|
||||
if (index % 2 === 0) return `<img src="${img}" class="gallery-image w-full object-cover aspect-video mb-8 md:mb-16 grayscale hover:grayscale-0 transition-all duration-700">`;
|
||||
else return `<div class="flex justify-end mb-8 md:mb-16"><img src="${img}" class="gallery-image w-4/5 object-cover aspect-[4/3] grayscale hover:grayscale-0 transition-all duration-700"></div>`;
|
||||
}).join('');
|
||||
|
||||
router('work-detail');
|
||||
}
|
||||
|
||||
function openBlog(id) {
|
||||
router('blog-detail');
|
||||
document.getElementById('post-content').innerHTML = `<h2>文章内容加载中...</h2><p class="text-art-muted">(这是一个演示版面,真实内容需连接数据库)</p>`;
|
||||
}
|
||||
|
||||
function openNextWork() {
|
||||
// Simple next logic based on array index
|
||||
const idx = store.works.findIndex(w => w.id === currentWorkId);
|
||||
const nextIdx = (idx + 1) % store.works.length;
|
||||
openWork(store.works[nextIdx].id);
|
||||
}
|
||||
|
||||
function openSnippet(id) {
|
||||
const modal = document.getElementById('snippet-modal');
|
||||
const data = snippetsData[id];
|
||||
if(!data) return;
|
||||
document.getElementById('modal-title').innerText = data.title;
|
||||
document.getElementById('modal-code').innerText = data.code;
|
||||
renderPreview(data.type);
|
||||
modal.classList.remove('hidden');
|
||||
setTimeout(() => { modal.classList.remove('opacity-0'); document.getElementById('modal-content').classList.remove('scale-95'); document.getElementById('modal-content').classList.add('scale-100'); }, 10);
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
function closeSnippet() { const modal = document.getElementById('snippet-modal'); modal.classList.add('opacity-0'); setTimeout(() => { modal.classList.add('hidden'); }, 300); document.body.style.overflow = ''; }
|
||||
|
||||
function renderPreview(type) {
|
||||
const container = document.getElementById('modal-preview-container');
|
||||
container.innerHTML = '';
|
||||
// (Reusing visual logic from prev step)
|
||||
const box = document.createElement('div');
|
||||
if(type === 'mouse') {
|
||||
box.className = "flex flex-col items-center justify-center p-8 border border-white/20 rounded-xl bg-black/50 backdrop-blur text-white";
|
||||
box.innerHTML = `<div class="text-4xl font-mono mb-2" id="demo-xy">0, 0</div>`;
|
||||
container.onmousemove = (e) => {
|
||||
const rect = container.getBoundingClientRect();
|
||||
document.getElementById('demo-xy').innerText = `${Math.floor(e.clientX - rect.left)}, ${Math.floor(e.clientY - rect.top)}`;
|
||||
};
|
||||
} else {
|
||||
box.className = "w-40 h-40 bg-white/10 rounded-full flex items-center justify-center border border-white/20 backdrop-blur-md";
|
||||
box.innerHTML = "Preview";
|
||||
container.onmousemove = null;
|
||||
}
|
||||
container.appendChild(box);
|
||||
}
|
||||
|
||||
function openInquiry() { document.getElementById('inquiry-modal').classList.remove('hidden'); document.getElementById('inquiry-modal').classList.add('flex'); }
|
||||
function getFingerprint() { return "FP-" + Math.random().toString(36).substr(2, 9).toUpperCase(); }
|
||||
|
||||
document.getElementById('inquiry-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
let isValid = true;
|
||||
document.querySelectorAll('.error-msg').forEach(el => el.remove());
|
||||
document.querySelectorAll('.input-error').forEach(el => el.classList.remove('input-error'));
|
||||
|
||||
const fieldNames = { 'name': '称呼', 'contact': '联系方式' };
|
||||
['name', 'contact'].forEach(field => {
|
||||
const input = e.target.querySelector(`[name="${field}"]`);
|
||||
if(!input.value.trim()) {
|
||||
isValid = false;
|
||||
input.classList.add('input-error');
|
||||
const errorMsg = document.createElement('span');
|
||||
errorMsg.className = 'error-msg';
|
||||
errorMsg.innerText = `// 错误: 请输入您的${fieldNames[field]}`;
|
||||
input.closest('.group\\/input').appendChild(errorMsg);
|
||||
}
|
||||
});
|
||||
if(!isValid) return;
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
data.fingerprint = getFingerprint();
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/inquiry`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
if(res.ok) {
|
||||
showToast('咨询已提交,我会尽快联系您');
|
||||
document.getElementById('inquiry-modal').classList.add('hidden');
|
||||
e.target.reset();
|
||||
} else {
|
||||
showToast('提交失败 (后端未连接)', 'error');
|
||||
}
|
||||
} catch(err) {
|
||||
showToast('网络错误 (演示模式)', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// Spotlight
|
||||
const servicesGrid = document.getElementById('services-grid');
|
||||
if(servicesGrid) {
|
||||
servicesGrid.onmousemove = (e) => {
|
||||
for(const card of document.getElementsByClassName('service-card')) {
|
||||
// Logic from CSS variable binding if needed, but tilt-card handles itself
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// New FAQ Toggle Logic
|
||||
function toggleFaq(btn) {
|
||||
const container = btn.parentElement;
|
||||
const content = btn.nextElementSibling;
|
||||
|
||||
document.querySelectorAll('.group.is-open').forEach(el => {
|
||||
if(el !== container) {
|
||||
el.classList.remove('is-open');
|
||||
el.querySelector('div.h-0').style.height = '0px';
|
||||
}
|
||||
});
|
||||
|
||||
if (container.classList.contains('is-open')) {
|
||||
container.classList.remove('is-open');
|
||||
content.style.height = '0px';
|
||||
} else {
|
||||
container.classList.add('is-open');
|
||||
content.style.height = content.scrollHeight + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1812
view/UI2.html
Normal file
1812
view/UI2.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user