Files
VFXdemo/index.html
Daniel afbcd99224 fix
2026-04-01 20:06:17 +08:00

338 lines
8.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VFX 快速预览工具台</title>
<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=Archivo:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<style>
:root {
color-scheme: dark;
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 100%;
min-height: 100%;
background: radial-gradient(circle at 20% 0%, #13182a 0%, #090a0f 35%, #07080c 100%);
color: #e8e8eb;
font-family: "Space Grotesk", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"PingFang SC", "Helvetica Neue", Arial, sans-serif;
}
#app {
min-height: 100vh;
}
.site-header {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 16px;
border-bottom: 1px solid rgba(130, 148, 255, 0.25);
background: rgba(8, 10, 17, 0.82);
backdrop-filter: blur(8px);
}
.brand {
font-family: "Archivo", sans-serif;
font-size: 26px;
font-weight: 700;
letter-spacing: 0.4px;
color: #f7f8ff;
text-shadow: 0 0 18px rgba(92, 140, 255, 0.45);
}
.header-right {
display: flex;
align-items: center;
gap: 10px;
}
.search {
width: 260px;
max-width: 42vw;
padding: 8px 10px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.04);
color: #d6d9e8;
}
button {
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.04);
color: #fff;
border-radius: 10px;
font-size: 13px;
}
button {
padding: 8px 12px;
cursor: pointer;
}
button:hover {
background: rgba(255, 255, 255, 0.12);
}
.stats {
margin-top: 10px;
padding: 10px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(0, 0, 0, 0.2);
font-size: 12px;
color: #c3c5cd;
}
.main {
padding: 16px;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
gap: 10px;
}
.topbar strong {
font-family: "Archivo", sans-serif;
letter-spacing: 0.2px;
}
.preview-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 12px;
}
.card {
border: 1px solid rgba(104, 129, 255, 0.35);
border-radius: 12px;
overflow: hidden;
background: linear-gradient(180deg, #0f1220 0%, #0b0d15 100%);
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
cursor: pointer;
}
.card:hover {
transform: translateY(-2px);
border-color: rgba(123, 178, 255, 0.7);
box-shadow: 0 14px 32px rgba(18, 46, 150, 0.25);
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 10px;
font-size: 12px;
color: #d6daea;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.card canvas {
width: 100%;
aspect-ratio: 16 / 10;
display: block;
background: #000;
}
.card-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 10px 10px;
font-size: 12px;
color: #9ea6c8;
}
.error {
color: #ff8b8b;
font-size: 11px;
white-space: pre-wrap;
}
.detail-view {
position: fixed;
inset: 0;
z-index: 20;
display: none;
grid-template-rows: 52px 1fr;
background: #05060a;
}
.detail-view.active {
display: grid;
}
.detail-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(14, 16, 23, 0.95);
}
.detail-stage {
width: 100%;
height: calc(100vh - 52px);
}
.detail-stage canvas {
width: 100%;
height: 100%;
display: block;
background: #000;
}
.modal {
position: fixed;
inset: 0;
z-index: 30;
display: none;
place-items: center;
background: rgba(4, 6, 10, 0.7);
backdrop-filter: blur(4px);
}
.modal.active {
display: grid;
}
.modal-card {
width: min(900px, 92vw);
max-height: 90vh;
overflow: auto;
border: 1px solid rgba(123, 178, 255, 0.5);
border-radius: 14px;
background: #0c0f1a;
padding: 14px;
}
.modal-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.modal-grid input,
.modal-grid textarea {
width: 100%;
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.04);
color: #fff;
border-radius: 8px;
padding: 8px;
}
.modal-grid textarea {
min-height: 220px;
resize: vertical;
grid-column: 1 / -1;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Courier New", monospace;
}
.table {
margin-top: 10px;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 10px;
overflow: hidden;
}
.row-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 8px 10px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.row-item:first-child {
border-top: 0;
}
</style>
</head>
<body>
<header class="site-header">
<div class="brand">Shadervault</div>
<div class="header-right">
<input id="search-input" class="search" placeholder="Search shaders..." />
<button id="manage-btn">管理内容</button>
</div>
</header>
<div id="app">
<main class="main">
<div class="topbar">
<strong>Featured Shader Playlist</strong>
<div style="display: flex; align-items: center; gap: 8px">
<span id="shader-count">0 shaders</span>
<button id="pause-btn">全局暂停</button>
<button id="reset-btn">全局重置</button>
<span id="global-fps">FPS: --</span>
</div>
</div>
<div class="stats" id="stats">准备就绪</div>
<section id="preview-grid" class="preview-grid"></section>
</main>
</div>
<section id="detail-view" class="detail-view" aria-hidden="true">
<div class="detail-topbar">
<div>
<strong id="detail-title">特效详情</strong>
<span id="detail-fps" style="margin-left: 8px; color: #c8c9cf; font-size: 12px">FPS: --</span>
</div>
<button id="back-btn">返回列表</button>
</div>
<div class="detail-stage">
<canvas id="detail-canvas"></canvas>
</div>
</section>
<section id="manage-modal" class="modal" aria-hidden="true">
<div class="modal-card">
<div class="topbar">
<strong>Shader 内容管理</strong>
<div style="display:flex; gap:8px">
<button id="close-manage-btn">关闭</button>
</div>
</div>
<div class="modal-grid">
<input id="shader-name-input" placeholder="名称" />
<input id="shader-author-input" placeholder="作者(可选)" />
<textarea id="shader-code-input" placeholder="粘贴 GLSL必须包含 mainImage"></textarea>
</div>
<div style="display:flex; gap:8px; margin-top:10px;">
<button id="save-shader-btn">保存到后端</button>
<button id="fill-example-btn">填入示例</button>
</div>
<div class="table" id="shader-admin-list"></div>
</div>
</section>
<script src="./frontend.js"></script>
</body>
</html>