Files
web_template/web/dist/game2/index.html
2026-07-10 10:07:15 +00:00

1289 lines
60 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>PIXEL GAMES · 像素游戏厅</title>
<style>
/* ============================================================
PIXEL GAMES — 复古像素风游戏平台 (单文件, 无外部依赖)
============================================================ */
:root{
--bg:#1a1a2e; /* 深蓝底 */
--bg-2:#16213e; /* 面板底 */
--bg-3:#0f3460; /* 深面板 */
--bg-4:#0a0a1a; /* 最深 */
--green:#0fbc6e; /* 复古绿 */
--green-d:#0a8a52;
--green-l:#3ce896;
--yellow:#f9d71c; /* 暖黄 */
--yellow-d:#c9a800;
--red:#e94560; /* 珊瑚红 */
--red-d:#b8334c;
--purple:#6c5ce7; /* 像素紫 */
--purple-d:#4e3fc4;
--cyan:#00e5ff;
--text:#e8e8f0;
--text-dim:#8a8aae;
--text-dimer:#5a5a78;
--ink:#000000;
--shadow:rgba(0,0,0,.55);
--font:'Courier New',Courier,monospace;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{scroll-behavior:smooth}
body{
font-family:var(--font);
background:var(--bg);
color:var(--text);
font-size:14px;
line-height:1.5;
letter-spacing:.5px;
overflow-x:hidden;
image-rendering:pixelated;
background-image:
radial-gradient(circle at 20% 10%, rgba(108,92,231,.08), transparent 40%),
radial-gradient(circle at 80% 80%, rgba(15,188,110,.07), transparent 40%);
background-attachment:fixed;
}
img{display:block;max-width:100%;image-rendering:pixelated}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;letter-spacing:inherit}
input,select{font-family:inherit;letter-spacing:inherit;color:inherit}
a{color:inherit;text-decoration:none}
ul{list-style:none}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:var(--bg-4)}
::-webkit-scrollbar-thumb{background:var(--green-d);border:2px solid var(--bg-4)}
::-webkit-scrollbar-thumb:hover{background:var(--green)}
/* ===== CRT 扫描线 & 屏幕特效 ===== */
.crt-overlay{
position:fixed;inset:0;z-index:9998;pointer-events:none;
background:repeating-linear-gradient(
to bottom,
rgba(0,0,0,0) 0,
rgba(0,0,0,0) 2px,
rgba(0,0,0,.22) 3px,
rgba(0,0,0,.22) 3px
);
mix-blend-mode:multiply;
opacity:.7;
}
.crt-vignette{
position:fixed;inset:0;z-index:9997;pointer-events:none;
box-shadow:inset 0 0 140px 30px rgba(0,0,0,.65), inset 0 0 40px 0 rgba(0,0,0,.4);
}
.crt-flicker{
position:fixed;inset:0;z-index:9999;pointer-events:none;
background:rgba(15,188,110,.012);
animation:flicker 6s steps(60) infinite;
}
@keyframes flicker{0%{opacity:.3}1%{opacity:.5}2%{opacity:.3}50%{opacity:.4}51%{opacity:.55}52%{opacity:.35}100%{opacity:.3}}
/* ===== 像素通用 ===== */
.container{max-width:1240px;margin:0 auto;padding:0 22px}
.pixel-font{font-family:var(--font);letter-spacing:2px;text-transform:uppercase}
/* 像素边框盒子 — 硬边 + 3D 阴影 */
.pbox{
background:var(--bg-2);
border:4px solid var(--ink);
box-shadow:
inset -4px -4px 0 0 rgba(0,0,0,.45),
inset 4px 4px 0 0 rgba(255,255,255,.06),
6px 6px 0 0 var(--shadow);
position:relative;
}
.pbox-green{border-color:var(--green)}
.pbox-yellow{border-color:var(--yellow)}
.pbox-red{border-color:var(--red)}
.pbox-purple{border-color:var(--purple)}
/* 像素按钮 — 按下 3D 效果 */
.pbtn{
display:inline-flex;align-items:center;justify-content:center;gap:8px;
font-family:var(--font);font-weight:bold;letter-spacing:2px;text-transform:uppercase;
background:var(--green);color:var(--ink);
border:4px solid var(--ink);
box-shadow:4px 4px 0 0 var(--ink), inset -3px -3px 0 0 rgba(0,0,0,.28), inset 3px 3px 0 0 rgba(255,255,255,.25);
padding:12px 22px;
transition:transform .04s linear, box-shadow .04s linear;
cursor:pointer;user-select:none;
image-rendering:pixelated;
}
.pbtn:hover{filter:brightness(1.1)}
.pbtn:active{transform:translate(4px,4px);box-shadow:0 0 0 0 var(--ink), inset -3px -3px 0 0 rgba(0,0,0,.28), inset 3px 3px 0 0 rgba(255,255,255,.18)}
.pbtn-yellow{background:var(--yellow)}
.pbtn-red{background:var(--red);color:#fff}
.pbtn-purple{background:var(--purple);color:#fff}
.pbtn-ghost{background:var(--bg-3);color:var(--green);box-shadow:4px 4px 0 0 var(--ink),inset -3px -3px 0 0 rgba(0,0,0,.4),inset 3px 3px 0 0 rgba(255,255,255,.06)}
.pbtn-sm{padding:7px 12px;font-size:12px;box-shadow:3px 3px 0 0 var(--ink),inset -2px -2px 0 0 rgba(0,0,0,.28),inset 2px 2px 0 0 rgba(255,255,255,.2)}
.pbtn-sm:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink),inset -2px -2px 0 0 rgba(0,0,0,.28),inset 2px 2px 0 0 rgba(255,255,255,.15)}
/* 像素斜切面板角 */
.clip-corner{clip-path:polygon(0 0,calc(100% - 14px) 0,100% 14px,100% 100%,14px 100%,0 calc(100% - 14px))}
/* 动画 */
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
.blink{animation:blink 1s steps(1) infinite}
@keyframes blink-soft{0%,49%{opacity:1}50%,100%{opacity:.25}}
.blink-soft{animation:blink-soft 1.1s steps(1) infinite}
@keyframes flash{0%,100%{background:var(--red)}50%{background:var(--yellow)}}
@keyframes float-up{0%{transform:translateY(0)}50%{transform:translateY(-8px)}100%{transform:translateY(0)}}
@keyframes scroll-bg{from{background-position:0 0}to{background-position:48px 48px}}
@keyframes pulse-border{0%,100%{box-shadow:0 0 0 0 rgba(249,215,28,.6)}50%{box-shadow:0 0 0 6px rgba(249,215,28,0)}}
@keyframes marquee{from{transform:translateX(100%)}to{transform:translateX(-100%)}}
@keyframes slidein{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}
@keyframes coin-spin{0%{transform:scaleX(1)}50%{transform:scaleX(.1)}100%{transform:scaleX(1)}}
/* ===== 像素小图标 (CSS box-shadow 网格) ===== */
.pix{position:relative;display:inline-block;width:16px;height:16px;vertical-align:middle}
.pix-coin{background:var(--yellow);box-shadow:0 -4px 0 var(--yellow),0 4px 0 var(--yellow),-4px 0 0 var(--yellow-d),4px 0 0 var(--yellow-d);width:8px;height:8px;margin:4px}
.pix-heart{width:12px;height:12px;background:var(--red);clip-path:polygon(50% 100%,0 50%,0 25%,25% 0,50% 25%,75% 0,100% 25%,100% 50%)}
.pix-star{width:12px;height:12px;background:var(--yellow);clip-path:polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%)}
.pix-bolt{width:8px;height:12px;background:var(--yellow);clip-path:polygon(60% 0,0 70%,40% 70%,30% 100%,100% 30%,55% 30%,70% 0)}
/* 像素装饰精灵 */
.sprite{image-rendering:pixelated;display:inline-block}
/* ============================================================
HEADER
============================================================ */
.header{
position:sticky;top:0;z-index:200;
background:var(--bg-4);
border-bottom:4px solid var(--green);
box-shadow:0 4px 0 0 rgba(15,188,110,.25);
}
.header-inner{display:flex;align-items:center;gap:18px;height:74px}
.logo{display:flex;align-items:center;gap:12px;flex-shrink:0;cursor:pointer}
.logo-mark{
width:44px;height:44px;background:var(--green);
border:4px solid var(--ink);
box-shadow:3px 3px 0 0 var(--ink);
display:grid;place-items:center;
position:relative;
}
.logo-mark::before{
content:"";position:absolute;width:10px;height:10px;background:var(--yellow);
box-shadow:14px 0 0 var(--red),0 14px 0 var(--purple),14px 14px 0 var(--yellow);
top:7px;left:7px;
}
.logo-txt{
font-size:20px;font-weight:bold;letter-spacing:3px;color:var(--green);
text-shadow:3px 3px 0 var(--ink), 3px 0 0 var(--ink), 0 3px 0 var(--ink);
line-height:1;
}
.logo-txt small{display:block;font-size:9px;color:var(--yellow);letter-spacing:2px;margin-top:4px;text-shadow:2px 2px 0 var(--ink)}
.nav{display:flex;gap:6px;margin-left:10px}
.nav a{
display:inline-block;padding:9px 14px;font-size:13px;font-weight:bold;letter-spacing:2px;
color:var(--text-dim);text-transform:uppercase;border:4px solid transparent;
transition:.08s;
}
.nav a:hover{color:var(--yellow)}
.nav a.active{color:var(--ink);background:var(--yellow);box-shadow:3px 3px 0 0 var(--ink)}
.search{flex:1;max-width:420px;margin-left:auto;display:flex}
.search input{
flex:1;height:46px;background:var(--bg-2);color:var(--text);
border:4px solid var(--ink);border-right:none;
padding:0 14px;font-size:13px;letter-spacing:1px;
box-shadow:inset 3px 3px 0 0 rgba(0,0,0,.5);
}
.search input::placeholder{color:var(--text-dimer)}
.search input:focus{outline:none;background:var(--bg-3)}
.search button{
height:46px;width:54px;background:var(--green);color:var(--ink);
border:4px solid var(--ink);font-size:18px;font-weight:bold;
box-shadow:3px 3px 0 0 var(--ink);
}
.search button:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.coin-counter{
display:flex;align-items:center;gap:8px;padding:8px 14px;
background:var(--bg-2);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);
font-weight:bold;color:var(--yellow);font-size:13px;letter-spacing:1px;
}
/* ============================================================
HERO
============================================================ */
.hero{
position:relative;overflow:hidden;
background:
linear-gradient(180deg,var(--bg-3) 0%,var(--bg) 100%);
border-bottom:4px solid var(--ink);
padding:70px 0 90px;
}
.hero-grid-bg{
position:absolute;inset:0;opacity:.25;pointer-events:none;
background-image:
linear-gradient(rgba(15,188,110,.18) 1px,transparent 1px),
linear-gradient(90deg,rgba(15,188,110,.18) 1px,transparent 1px);
background-size:32px 32px;
animation:scroll-bg 8s linear infinite;
}
.hero-inner{position:relative;display:grid;grid-template-columns:1.2fr .8fr;gap:40px;align-items:center}
.hero-title{
font-size:clamp(40px,7vw,84px);font-weight:bold;letter-spacing:6px;line-height:1;
color:var(--green);text-shadow:6px 6px 0 var(--ink),6px 0 0 var(--ink),0 6px 0 var(--ink);
margin-bottom:18px;
}
.hero-title .y{color:var(--yellow)}
.hero-title .r{color:var(--red)}
.hero-sub{
font-size:14px;letter-spacing:3px;color:var(--text-dim);margin-bottom:10px;
}
.press-start{
font-size:clamp(22px,4vw,38px);font-weight:bold;letter-spacing:5px;
color:var(--yellow);text-shadow:4px 4px 0 var(--ink);
margin:18px 0 26px;min-height:1.2em;
}
.hero-tags{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:28px}
.hero-tag{
padding:6px 12px;font-size:11px;letter-spacing:1px;
background:var(--bg-2);border:3px solid var(--ink);box-shadow:2px 2px 0 0 var(--ink);
color:var(--green);
}
.hero-cta{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.hero-coin{font-size:12px;color:var(--text-dim);letter-spacing:1px;display:flex;align-items:center;gap:8px}
/* 像素装饰场景 (右侧) */
.hero-scene{display:flex;justify-content:center;align-items:flex-end;gap:18px;min-height:260px;position:relative}
.scene-cloud{position:absolute;background:#fff;opacity:.5}
.scene-cloud.c1{width:40px;height:14px;top:10px;left:10%;box-shadow:-10px 0 0 #fff,10px -8px 0 #fff}
.scene-cloud.c2{width:30px;height:10px;top:40px;right:12%;box-shadow:-8px 0 0 #fff,8px -6px 0 #fff;opacity:.35}
.scene-ground{
position:absolute;bottom:0;left:0;right:0;height:34px;
background:repeating-linear-gradient(90deg,var(--green-d) 0 16px,var(--green) 16px 32px);
border-top:4px solid var(--ink);
}
.scene-char{
width:64px;height:84px;position:relative;animation:float-up 2.2s ease-in-out infinite;
}
.scene-brick{
width:36px;height:36px;background:var(--red);border:4px solid var(--ink);
box-shadow:inset 4px 4px 0 rgba(255,255,255,.25),3px 3px 0 var(--ink);
position:relative;
}
.scene-brick::after{content:"";position:absolute;inset:6px;border:3px solid var(--red-d)}
.scene-pipe{
width:44px;height:70px;background:var(--green);border:4px solid var(--ink);
box-shadow:inset 4px 4px 0 rgba(255,255,255,.2),3px 3px 0 var(--ink);
}
.scene-pipe::before{content:"";display:block;height:14px;background:var(--green-l);border-bottom:4px solid var(--ink)}
.scene-coin{width:18px;height:18px;background:var(--yellow);border:3px solid var(--ink);box-shadow:inset 3px 0 0 var(--yellow-d);animation:coin-spin 1.4s steps(2) infinite;margin-top:10px}
/* 滚动公告条 */
.ticker{
background:var(--ink);border-bottom:4px solid var(--ink);
overflow:hidden;white-space:nowrap;
}
.ticker-track{display:inline-block;padding:9px 0;font-size:12px;letter-spacing:2px;color:var(--yellow);animation:marquee 22s linear infinite}
.ticker-track span{margin:0 30px}
.ticker-track .r{color:var(--red)}
.ticker-track .g{color:var(--green-l)}
/* ============================================================
SECTION 通用
============================================================ */
section{padding:54px 0}
.sec-head{display:flex;align-items:center;gap:14px;margin-bottom:30px;flex-wrap:wrap}
.sec-title{
font-size:22px;font-weight:bold;letter-spacing:3px;text-transform:uppercase;
color:var(--yellow);text-shadow:3px 3px 0 var(--ink);
display:flex;align-items:center;gap:12px;
}
.sec-title::before{content:"";display:inline-block;width:14px;height:24px;background:var(--red);box-shadow:0 0 0 3px var(--ink),4px 4px 0 var(--ink)}
.sec-sub{font-size:11px;color:var(--text-dim);letter-spacing:2px}
.sec-line{flex:1;height:4px;background:repeating-linear-gradient(90deg,var(--green-d) 0 8px,transparent 8px 16px)}
/* ============================================================
CATEGORIES
============================================================ */
.cats{display:flex;gap:10px;flex-wrap:wrap;padding:26px}
.cat-btn{
padding:10px 18px;font-size:13px;font-weight:bold;letter-spacing:2px;
background:var(--bg-3);color:var(--text-dim);
border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink),inset -3px -3px 0 rgba(0,0,0,.4);
cursor:pointer;transition:.06s;display:flex;align-items:center;gap:8px;
}
.cat-btn:hover{color:var(--yellow);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
.cat-btn:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.cat-btn.active{background:var(--purple);color:#fff;box-shadow:3px 3px 0 0 var(--ink),inset -3px -3px 0 rgba(0,0,0,.3),inset 3px 3px 0 rgba(255,255,255,.15)}
.cat-btn .ci{font-size:16px;line-height:1}
/* ============================================================
HOT — 高分榜
============================================================ */
.hot-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:26px}
.scoreboard{
background:var(--bg-2);border:4px solid var(--ink);
box-shadow:6px 6px 0 0 var(--shadow),inset -4px -4px 0 rgba(0,0,0,.4),inset 4px 4px 0 rgba(255,255,255,.05);
}
.score-head{
display:grid;grid-template-columns:60px 1fr 130px 110px 80px;gap:10px;
padding:12px 16px;background:var(--bg-4);border-bottom:4px solid var(--ink);
font-size:11px;letter-spacing:2px;color:var(--green);font-weight:bold;
}
.score-row{
display:grid;grid-template-columns:60px 1fr 130px 110px 80px;gap:10px;
padding:13px 16px;border-bottom:3px solid var(--bg-4);
align-items:center;cursor:pointer;transition:.08s;
}
.score-row:hover{background:var(--bg-3);padding-left:20px}
.score-row:last-child{border-bottom:none}
.score-rank{font-size:22px;font-weight:bold;letter-spacing:1px}
.rank-1{color:var(--yellow);text-shadow:2px 2px 0 var(--ink)}
.rank-2{color:#c0c0d0;text-shadow:2px 2px 0 var(--ink)}
.rank-3{color:var(--red);text-shadow:2px 2px 0 var(--ink)}
.rank-oth{color:var(--text-dimer)}
.score-name{font-size:14px;font-weight:bold;color:var(--text);letter-spacing:1px;display:flex;align-items:center;gap:8px}
.score-name .tag-hot{font-size:9px;background:var(--red);color:#fff;padding:2px 5px;letter-spacing:1px}
.score-rate{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--yellow)}
.score-plays{font-size:12px;color:var(--cyan);letter-spacing:1px}
.score-go{font-size:11px;color:var(--green);letter-spacing:1px;text-align:right}
.score-row:hover .score-go{color:var(--yellow)}
/* 高分榜侧栏 */
.hot-side{display:flex;flex-direction:column;gap:18px}
.featured-card{
background:var(--bg-2);border:4px solid var(--ink);
box-shadow:6px 6px 0 0 var(--shadow);overflow:hidden;
}
.featured-cover{height:170px;background:var(--bg-3);position:relative;overflow:hidden}
.featured-cover img{width:100%;height:100%;object-fit:cover}
.featured-badge{position:absolute;top:10px;left:10px;background:var(--red);color:#fff;font-size:10px;padding:4px 8px;letter-spacing:1px;border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink);animation:blink-soft 1.4s steps(1) infinite}
.featured-body{padding:16px}
.featured-body h3{font-size:16px;letter-spacing:1px;color:var(--yellow);margin-bottom:8px;text-shadow:2px 2px 0 var(--ink)}
.featured-body p{font-size:12px;color:var(--text-dim);line-height:1.6;letter-spacing:.5px;margin-bottom:12px}
.featured-meta{display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:var(--green)}
/* ============================================================
NEW GAMES — 新游速报
============================================================ */
.new-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.new-card{
background:var(--bg-2);border:4px solid var(--ink);
box-shadow:5px 5px 0 0 var(--shadow);
cursor:pointer;transition:.08s;position:relative;overflow:hidden;
}
.new-card:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 0 var(--shadow)}
.new-cover{height:150px;background:var(--bg-3);position:relative;overflow:hidden;border-bottom:4px solid var(--ink)}
.new-cover img{width:100%;height:100%;object-fit:cover}
.new-cover .cover-px{position:absolute;inset:0}
.new-badge{
position:absolute;top:-2px;right:-2px;background:var(--yellow);color:var(--ink);
font-size:11px;font-weight:bold;padding:6px 10px;letter-spacing:1px;
border:4px solid var(--ink);box-shadow:0 4px 0 var(--ink);
animation:flash-bg 1.5s steps(1) infinite;
}
@keyframes flash-bg{0%,100%{background:var(--yellow);color:var(--ink)}50%{background:var(--red);color:#fff}}
.new-body{padding:14px}
.new-body h4{font-size:14px;color:var(--green);letter-spacing:1px;margin-bottom:6px;text-shadow:2px 2px 0 var(--ink)}
.new-body .dev{font-size:11px;color:var(--text-dimer);margin-bottom:8px}
.new-tags{display:flex;gap:6px;flex-wrap:wrap}
.new-tags span{font-size:10px;padding:3px 6px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan);letter-spacing:.5px}
/* ============================================================
LIBRARY — 游戏仓库
============================================================ */
.lib-bar{
display:flex;gap:14px;flex-wrap:wrap;align-items:center;margin-bottom:26px;
padding:18px;background:var(--bg-2);border:4px solid var(--ink);
box-shadow:5px 5px 0 0 var(--shadow);
}
.lib-search{display:flex;flex:1;min-width:240px}
.lib-search input{
flex:1;height:44px;background:var(--bg-4);border:4px solid var(--ink);border-right:none;
padding:0 14px;color:var(--text);font-size:13px;box-shadow:inset 3px 3px 0 rgba(0,0,0,.5);
}
.lib-search input:focus{outline:none;background:var(--bg-3)}
.lib-search button{height:44px;width:50px;background:var(--purple);color:#fff;border:4px solid var(--ink);box-shadow:3px 3px 0 var(--ink);font-size:16px}
.lib-search button:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.lib-select{position:relative}
.lib-select select{
height:44px;background:var(--bg-4);border:4px solid var(--ink);color:var(--text);
padding:0 34px 0 14px;font-size:12px;letter-spacing:1px;font-weight:bold;
box-shadow:3px 3px 0 var(--ink);appearance:none;cursor:pointer;
}
.lib-select::after{content:"▼";position:absolute;right:12px;top:50%;transform:translateY(-50%);color:var(--green);font-size:10px;pointer-events:none}
.lib-select select:focus{outline:none;background:var(--bg-3)}
.lib-count{font-size:12px;color:var(--text-dim);letter-spacing:1px}
.lib-count b{color:var(--yellow)}
.game-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.game-card{
background:var(--bg-2);border:4px solid var(--ink);box-shadow:5px 5px 0 0 var(--shadow);
cursor:pointer;transition:.08s;display:flex;flex-direction:column;
}
.game-card:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 0 var(--shadow);border-color:var(--green)}
.gcard-cover{height:170px;background:var(--bg-3);position:relative;overflow:hidden;border-bottom:4px solid var(--ink)}
.gcard-cover img{width:100%;height:100%;object-fit:cover}
.gcard-cover .ph{position:absolute;inset:0;display:grid;place-items:center;font-size:11px;color:var(--text-dimer);letter-spacing:1px}
.gcard-badges{position:absolute;top:8px;left:8px;display:flex;gap:6px;flex-direction:column}
.gbadge{font-size:9px;padding:3px 6px;letter-spacing:1px;border:2px solid var(--ink);font-weight:bold}
.gbadge-hot{background:var(--red);color:#fff}
.gbadge-new{background:var(--yellow);color:var(--ink)}
.gbadge-cat{background:var(--purple);color:#fff}
.gcard-rating{position:absolute;bottom:8px;right:8px;background:var(--ink);color:var(--yellow);font-size:11px;padding:3px 7px;border:2px solid var(--yellow);letter-spacing:1px;font-weight:bold}
.gcard-body{padding:13px;flex:1;display:flex;flex-direction:column}
.gcard-name{font-size:14px;color:var(--green);letter-spacing:1px;margin-bottom:6px;font-weight:bold;text-shadow:1px 1px 0 var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gcard-dev{font-size:11px;color:var(--text-dimer);margin-bottom:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gcard-tags{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:10px}
.gcard-tags span{font-size:9px;padding:2px 5px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan);letter-spacing:.5px}
.gcard-foot{display:flex;justify-content:space-between;align-items:center;margin-top:auto;padding-top:8px;border-top:3px solid var(--bg-4);font-size:11px}
.gcard-plays{color:var(--cyan)}
.gcard-size{color:var(--text-dimer)}
/* 空状态 / 加载 */
.empty-state{text-align:center;padding:60px 20px;color:var(--text-dim)}
.empty-state .es-icon{font-size:40px;margin-bottom:14px;color:var(--purple)}
.empty-state p{font-size:13px;letter-spacing:1px}
.loading-px{display:flex;justify-content:center;align-items:center;padding:50px;gap:10px}
.ld{width:14px;height:14px;background:var(--green);box-shadow:0 0 0 3px var(--ink);animation:ld 1s steps(1) infinite}
.ld:nth-child(2){animation-delay:.2s;background:var(--yellow)}
.ld:nth-child(3){animation-delay:.4s;background:var(--red)}
.ld:nth-child(4){animation-delay:.6s;background:var(--purple)}
@keyframes ld{0%,100%{opacity:.2;transform:translateY(0)}50%{opacity:1;transform:translateY(-8px)}}
/* 分页 */
.pagination{display:flex;justify-content:center;gap:8px;margin-top:34px;flex-wrap:wrap}
.page-btn{
min-width:42px;height:42px;padding:0 10px;
background:var(--bg-2);color:var(--text);font-weight:bold;font-size:13px;letter-spacing:1px;
border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);
cursor:pointer;display:grid;place-items:center;transition:.06s;
}
.page-btn:hover:not(:disabled){background:var(--bg-3);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
.page-btn:active:not(:disabled){transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.page-btn.active{background:var(--green);color:var(--ink)}
.page-btn:disabled{opacity:.3;cursor:not-allowed;box-shadow:3px 3px 0 0 rgba(0,0,0,.3)}
.page-dots{color:var(--text-dimer);align-self:center;padding:0 4px}
/* ============================================================
MODAL — 像素对话框
============================================================ */
.modal-mask{
position:fixed;inset:0;z-index:5000;display:none;
background:rgba(0,0,0,.82);align-items:center;justify-content:center;padding:20px;
}
.modal-mask.open{display:flex;animation:slidein .15s ease}
.modal{
width:min(880px,100%);max-height:90vh;overflow-y:auto;
background:var(--bg-2);border:6px solid var(--ink);
box-shadow:10px 10px 0 0 var(--shadow),0 0 0 6px var(--green),0 0 0 10px var(--ink);
position:relative;
}
.modal-titlebar{
display:flex;align-items:center;justify-content:space-between;
padding:12px 18px;background:var(--green);color:var(--ink);
border-bottom:6px solid var(--ink);font-weight:bold;letter-spacing:2px;font-size:14px;
}
.modal-titlebar .dots{display:flex;gap:6px}
.modal-titlebar .dots i{display:block;width:12px;height:12px;border:2px solid var(--ink)}
.modal-titlebar .dots i:nth-child(1){background:var(--red)}
.modal-titlebar .dots i:nth-child(2){background:var(--yellow)}
.modal-titlebar .dots i:nth-child(3){background:var(--green-l)}
.modal-close{
width:32px;height:32px;background:var(--red);color:#fff;font-weight:bold;font-size:16px;
border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink);display:grid;place-items:center;
}
.modal-close:active{transform:translate(2px,2px);box-shadow:0 0 0 0 var(--ink)}
.modal-body{padding:24px}
.modal-top{display:grid;grid-template-columns:280px 1fr;gap:24px;margin-bottom:22px}
.modal-cover{height:300px;background:var(--bg-3);border:4px solid var(--ink);overflow:hidden;position:relative;box-shadow:4px 4px 0 var(--ink)}
.modal-cover img{width:100%;height:100%;object-fit:cover}
.modal-info h2{font-size:24px;color:var(--yellow);letter-spacing:2px;margin-bottom:8px;text-shadow:3px 3px 0 var(--ink)}
.modal-info .m-dev{font-size:12px;color:var(--text-dim);margin-bottom:14px}
.modal-rate{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.modal-rate .rate-val{font-size:20px;color:var(--yellow);font-weight:bold}
.modal-info .m-tags{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px}
.modal-info .m-tags span{font-size:11px;padding:3px 8px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan)}
.m-meta{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:18px}
.m-meta-item{background:var(--bg-4);border:3px solid var(--ink);padding:8px 10px}
.m-meta-item label{display:block;font-size:10px;color:var(--text-dimer);letter-spacing:1px;margin-bottom:3px}
.m-meta-item span{font-size:12px;color:var(--green);font-weight:bold;letter-spacing:.5px}
.modal-desc{font-size:13px;color:var(--text);line-height:1.8;letter-spacing:.5px;margin-bottom:18px;background:var(--bg-4);border:3px solid var(--ink);padding:14px}
.modal-desc .lbl{color:var(--yellow);font-weight:bold;letter-spacing:2px;display:block;margin-bottom:6px}
.modal-features{margin-bottom:18px}
.modal-features .lbl{color:var(--purple);font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:8px}
.modal-features ul{display:flex;flex-direction:column;gap:6px}
.modal-features li{font-size:12px;color:var(--text);letter-spacing:.5px;padding-left:18px;position:relative}
.modal-features li::before{content:"▶";position:absolute;left:0;color:var(--green)}
.modal-shots{margin-bottom:20px}
.modal-shots .lbl{color:var(--cyan);font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:8px}
.shots-row{display:flex;gap:10px;overflow-x:auto;padding-bottom:6px}
.shots-row img{width:200px;height:120px;object-fit:cover;border:3px solid var(--ink);flex-shrink:0;box-shadow:3px 3px 0 var(--ink)}
.modal-actions{display:flex;gap:12px;flex-wrap:wrap;padding-top:16px;border-top:4px solid var(--bg-4)}
/* ============================================================
FOOTER
============================================================ */
.footer{
background:var(--bg-4);border-top:4px solid var(--green);padding:50px 0 24px;margin-top:30px;
}
.foot-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:34px;margin-bottom:34px}
.foot-brand .logo-txt{font-size:18px}
.foot-brand p{font-size:12px;color:var(--text-dim);line-height:1.7;margin-top:14px;letter-spacing:.5px}
.foot-col h5{font-size:12px;color:var(--yellow);letter-spacing:2px;margin-bottom:14px;text-shadow:2px 2px 0 var(--ink)}
.foot-col a{display:block;font-size:12px;color:var(--text-dim);padding:5px 0;letter-spacing:.5px;transition:.08s}
.foot-col a:hover{color:var(--green-l);transform:translateX(3px)}
.foot-bottom{
border-top:3px solid var(--bg-2);padding-top:20px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;
}
.foot-bottom p{font-size:11px;color:var(--text-dimer);letter-spacing:1px}
.foot-press{font-size:11px;color:var(--green);letter-spacing:2px}
/* 返回顶部 */
.to-top{
position:fixed;right:22px;bottom:22px;z-index:400;width:54px;height:54px;
background:var(--yellow);color:var(--ink);border:4px solid var(--ink);
box-shadow:4px 4px 0 0 var(--ink);display:none;place-items:center;font-weight:bold;font-size:18px;
}
.to-top.show{display:grid}
.to-top:active{transform:translate(4px,4px);box-shadow:0 0 0 0 var(--ink)}
/* ===== 响应式 ===== */
@media (max-width:1024px){
.hero-inner{grid-template-columns:1fr}
.hero-scene{min-height:200px;order:-1}
.hot-grid{grid-template-columns:1fr}
.new-grid,.game-grid{grid-template-columns:repeat(2,1fr)}
.modal-top{grid-template-columns:1fr}
.modal-cover{height:220px}
.foot-grid{grid-template-columns:1fr 1fr}
.nav{display:none}
}
@media (max-width:640px){
.header-inner{height:auto;padding:12px 0;flex-wrap:wrap}
.search{order:3;width:100%;max-width:none;flex-basis:100%}
.coin-counter{display:none}
.new-grid,.game-grid{grid-template-columns:1fr}
.score-head,.score-row{grid-template-columns:40px 1fr 70px;gap:6px}
.score-head .h-rate,.score-head .h-go,.score-rate,.score-go{display:none}
.score-plays{font-size:11px}
.m-meta{grid-template-columns:1fr}
.sec-title{font-size:18px}
.lib-bar{flex-direction:column;align-items:stretch}
.lib-select select{width:100%}
.hero{padding:40px 0 60px}
.foot-grid{grid-template-columns:1fr}
.foot-bottom{flex-direction:column;text-align:center}
}
</style>
</head>
<body>
<!-- CRT 屏幕特效 -->
<div class="crt-overlay"></div>
<div class="crt-vignette"></div>
<div class="crt-flicker"></div>
<!-- ===== HEADER ===== -->
<header class="header">
<div class="container header-inner">
<div class="logo" onclick="scrollTop2()">
<div class="logo-mark"></div>
<div class="logo-txt">PIXEL GAMES<small>像素游戏厅</small></div>
</div>
<nav class="nav" id="nav">
<a href="#" class="active" data-nav="home">首页</a>
<a href="#library" data-nav="lib">游戏库</a>
<a href="#hot" data-nav="rank">排行榜</a>
<a href="#new" data-nav="new">新游</a>
</nav>
<form class="search" id="navSearch" onsubmit="return false">
<input type="text" id="navSearchInput" placeholder="搜索游戏..." aria-label="搜索">
<button type="submit" aria-label="搜索">Q</button>
</form>
<div class="coin-counter" title="游戏币"><span class="pix pix-coin"></span><span id="coinCount">9999</span></div>
</div>
</header>
<!-- ===== HERO ===== -->
<section class="hero">
<div class="hero-grid-bg"></div>
<div class="container hero-inner">
<div class="hero-text">
<div class="hero-sub">▸ INSERT COIN TO PLAY ◂</div>
<h1 class="hero-title"><span class="y">像素</span> · <span class="r">世界</span></h1>
<div class="press-start blink">PRESS START</div>
<div class="hero-tags">
<span class="hero-tag">▸ 8-BIT 复古</span>
<span class="hero-tag">▸ 海量游戏</span>
<span class="hero-tag">▸ 极速下载</span>
<span class="hero-tag">▸ 玩家社区</span>
</div>
<div class="hero-cta">
<button class="pbtn pbtn-yellow" onclick="scrollTo2('library')">开始游戏 ▶</button>
<button class="pbtn pbtn-ghost" onclick="scrollTo2('hot')">查看排行</button>
<span class="hero-coin"><span class="pix pix-coin"></span> 每日登录 +100 币</span>
</div>
</div>
<div class="hero-scene" aria-hidden="true">
<div class="scene-cloud c1"></div>
<div class="scene-cloud c2"></div>
<div class="scene-brick"></div>
<div class="scene-char" id="heroChar"></div>
<div class="scene-pipe"></div>
<div class="scene-coin"></div>
<div class="scene-ground"></div>
</div>
</div>
</section>
<!-- 滚动公告 -->
<div class="ticker">
<div class="ticker-track" id="tickerTrack">
<span class="r">★ HOT</span><span>《龙之传说》已登顶本周排行榜</span>
<span class="g">● NEW</span><span>《星际远征》正式上线,立即下载体验</span>
<span class="r">★ EVENT</span><span>周末双倍经验活动开启中</span>
<span class="g">● TIP</span><span>每日签到领取免费游戏币</span>
<span class="r">★ UPDATE</span><span>《暗影刺客》v2.1.0 更新发布</span>
</div>
</div>
<!-- ===== CATEGORIES ===== -->
<section style="padding:0">
<div class="container">
<div class="pbox clip-corner" style="margin-top:30px">
<div class="cats" id="catsWrap">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
</section>
<!-- ===== HOT 高分榜 ===== -->
<section id="hot">
<div class="container">
<div class="sec-head">
<h2 class="sec-title">热门排行榜</h2>
<span class="sec-sub">HIGH SCORE TABLE</span>
<span class="sec-line"></span>
</div>
<div class="hot-grid">
<div class="scoreboard" id="scoreboard">
<div class="score-head">
<span>RANK</span><span>游戏名称</span><span class="h-rate">评分</span><span>游玩次数</span><span class="h-go"></span>
</div>
<div id="scoreRows">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
<div class="hot-side" id="hotSide">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
</section>
<!-- ===== NEW 新游速报 ===== -->
<section id="new" style="background:var(--bg-4);border-top:4px solid var(--ink);border-bottom:4px solid var(--ink)">
<div class="container">
<div class="sec-head">
<h2 class="sec-title">新游速报</h2>
<span class="sec-sub">NEW RELEASES</span>
<span class="sec-line"></span>
</div>
<div class="new-grid" id="newGrid">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</section>
<!-- ===== LIBRARY 游戏仓库 ===== -->
<section id="library">
<div class="container">
<div class="sec-head">
<h2 class="sec-title">游戏仓库</h2>
<span class="sec-sub">GAME LIBRARY</span>
<span class="sec-line"></span>
</div>
<div class="lib-bar">
<div class="lib-search">
<input type="text" id="libSearch" placeholder="输入游戏名称或关键词..." aria-label="搜索游戏">
<button onclick="onLibSearch()">Q</button>
</div>
<div class="lib-select">
<select id="libSort" onchange="onLibSort()">
<option value="">默认排序</option>
<option value="rating">评分最高</option>
<option value="plays">最多游玩</option>
<option value="newest">最新发布</option>
</select>
</div>
<div class="lib-select">
<select id="libCat" onchange="onLibCat()">
<option value="">全部分类</option>
</select>
</div>
<div class="lib-count">共 <b id="libTotal">0</b> 款游戏</div>
</div>
<div class="game-grid" id="gameGrid">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
<div class="pagination" id="pagination"></div>
</div>
</section>
<!-- ===== FOOTER ===== -->
<footer class="footer">
<div class="container">
<div class="foot-grid">
<div class="foot-brand">
<div class="logo-txt">PIXEL GAMES</div>
<p>复古像素风游戏平台 · 汇集海量经典与独立游戏 · 为玩家打造纯粹的 8-BIT 游戏体验。INSERT COIN, PRESS START, 享受游戏本来的乐趣。</p>
</div>
<div class="foot-col">
<h5>游戏中心</h5>
<a href="#library">游戏仓库</a>
<a href="#hot">热门排行</a>
<a href="#new">新游速报</a>
<a href="#" onclick="return false">分类浏览</a>
</div>
<div class="foot-col">
<h5>玩家社区</h5>
<a href="#" onclick="return false">玩家论坛</a>
<a href="#" onclick="return false">攻略中心</a>
<a href="#" onclick="return false">评分评价</a>
<a href="#" onclick="return false">创作工坊</a>
</div>
<div class="foot-col">
<h5>关于我们</h5>
<a href="#" onclick="return false">关于平台</a>
<a href="#" onclick="return false">联系我们</a>
<a href="#" onclick="return false">隐私政策</a>
<a href="#" onclick="return false">服务条款</a>
</div>
</div>
<div class="foot-bottom">
<p>© 2026 PIXEL GAMES · 像素游戏厅 · 保留所有权利</p>
<p class="foot-press blink">▲ PRESS START ▲</p>
</div>
</div>
</footer>
<!-- ===== MODAL ===== -->
<div class="modal-mask" id="modalMask" onclick="if(event.target===this)closeModal()">
<div class="modal" role="dialog" aria-modal="true">
<div class="modal-titlebar">
<span class="dots"><i></i><i></i><i></i></span>
<span id="modalTitle">游戏详情</span>
<button class="modal-close" onclick="closeModal()" aria-label="关闭">X</button>
</div>
<div class="modal-body" id="modalBody">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
<!-- 返回顶部 -->
<button class="to-top" id="toTop" onclick="scrollTop2()" aria-label="返回顶部">▲</button>
<script>
/* ============================================================
PIXEL GAMES — 前端逻辑
============================================================ */
var API = '/api/v1/game';
/* ---------- 工具函数 ---------- */
function formatNum(n){
n = Number(n)||0;
if(n>=100000000) return (n/100000000).toFixed(1).replace(/\.0$/,'')+'亿';
if(n>=10000) return (n/10000).toFixed(1).replace(/\.0$/,'')+'万';
return String(n);
}
function parseJSON(str, fallback){
try{ return JSON.parse(str); }catch(e){ return fallback; }
}
function escapeHTML(s){
if(s==null) return '';
return String(s).replace(/[&<>"']/g, function(c){
return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];
});
}
// 像素评分条:10 格 █/░
function ratingBlocks(rating){
var r = Number(rating)||0;
var filled = Math.round(r*2); // 0..10
var s = '';
for(var i=0;i<10;i++){ s += i<filled ? '█' : '░'; }
return '<span class="rb-fill">'+s.slice(0,filled)+'</span><span class="rb-empty">'+s.slice(filled)+'</span>';
}
function coverHTML(g, cls){
var c = g && g.cover ? g.cover : '';
if(c){
return '<img src="'+escapeHTML(c)+'" alt="'+escapeHTML(g.name||'')+'" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'grid\'"><div class="ph" style="display:none">'+escapeHTML((g.name||'PIXEL').slice(0,4))+'</div>';
}
return '<div class="ph">'+escapeHTML((g&&g.name||'PIXEL').slice(0,6))+'</div>';
}
/* ---------- API 封装 ---------- */
function apiGet(path){
return fetch(path, {headers:{'Accept':'application/json'}})
.then(function(r){ if(!r.ok) throw new Error('HTTP '+r.status); return r.json(); })
.then(function(res){
if(res && Object.prototype.hasOwnProperty.call(res,'code')){
if(res.code!==0) throw new Error(res.message||'API错误');
return res.data;
}
return res; // 直接返回数组/对象
});
}
/* ---------- 状态 ---------- */
var state = {
cats: [],
lib: { list:[], total:0, page:1, page_size:12, keyword:'', sort:'', category_id:'' },
homeLoaded:false
};
/* ============================================================
分类
============================================================ */
function loadCategories(){
return apiGet(API+'/categories').then(function(list){
state.cats = (list||[]).slice().sort(function(a,b){return (a.sort||0)-(b.sort||0);});
renderCats(state.cats);
renderLibCatSelect(state.cats);
}).catch(function(){
state.cats = MOCK_CATS();
renderCats(state.cats);
renderLibCatSelect(state.cats);
});
}
function MOCK_CATS(){
return [
{id:1,name:'动作',icon:'⚔',sort:1},
{id:2,name:'角色扮演',icon:'★',sort:2},
{id:3,name:'射击',icon:'◉',sort:3},
{id:4,name:'冒险',icon:'▲',sort:4},
{id:5,name:'策略',icon:'♟',sort:5},
{id:6,name:'模拟',icon:'◆',sort:6},
{id:7,name:'体育',icon:'●',sort:7},
{id:8,name:'益智',icon:'♦',sort:8}
];
}
function renderCats(cats){
var html = '<button class="cat-btn active" data-cid="" onclick="onCatClick(this,\'\')"><span class="ci">★</span>全部</button>';
cats.forEach(function(c){
html += '<button class="cat-btn" data-cid="'+c.id+'" onclick="onCatClick(this,'+c.id+')"><span class="ci">'+escapeHTML(c.icon||'◆')+'</span>'+escapeHTML(c.name)+'</button>';
});
document.getElementById('catsWrap').innerHTML = html;
}
function renderLibCatSelect(cats){
var sel = document.getElementById('libCat');
cats.forEach(function(c){
var o = document.createElement('option');
o.value = c.id; o.textContent = c.name;
sel.appendChild(o);
});
}
function onCatClick(btn, cid){
var btns = document.querySelectorAll('.cat-btn');
for(var i=0;i<btns.length;i++) btns[i].classList.remove('active');
btn.classList.add('active');
// 同步仓库分类
state.lib.category_id = cid;
var sel = document.getElementById('libCat');
sel.value = cid;
state.lib.page = 1;
loadLibrary();
scrollTo2('library');
}
/* ============================================================
首页数据 (home)
============================================================ */
function loadHome(){
return apiGet(API+'/home').then(function(d){
d = d||{};
renderCats((d.categories&&d.categories.length)?d.categories:state.cats);
renderHot(d.hot||[]);
renderNew(d.new||[]);
}).catch(function(){
// 回退:分别请求
Promise.all([
apiGet(API+'/hot?limit=8').catch(function(){return mockGames(8,true);}),
apiGet(API+'/new?limit=4').catch(function(){return mockGames(4,false,true);})
]).then(function(res){
renderHot(res[0]||[]);
renderNew(res[1]||[]);
});
});
}
/* ============================================================
热门排行榜 (高分榜)
============================================================ */
function renderHot(list){
list = (list||[]).slice(0,8);
if(!list.length){
document.getElementById('scoreRows').innerHTML = emptyHTML('暂无热门游戏数据');
document.getElementById('hotSide').innerHTML = '';
return;
}
var rows = '';
list.forEach(function(g,i){
var rk = i+1;
var rkCls = rk===1?'rank-1':rk===2?'rank-2':rk===3?'rank-3':'rank-oth';
var rkStr = rk<10?'0'+rk:''+rk;
rows += '<div class="score-row" onclick="openModal('+g.id+')">'
+ '<span class="score-rank '+rkCls+'">'+rkStr+'</span>'
+ '<span class="score-name">'+(rk<=3?'<span class="pix pix-star"></span> ':'')+escapeHTML(g.name||'')+(g.is_hot?'<span class="tag-hot">HOT</span>':'')+'</span>'
+ '<span class="score-rate"><span class="rate-val-mini">'+(Number(g.rating)||0).toFixed(1)+'</span> '+ratingBlocks(g.rating)+'</span>'
+ '<span class="score-plays">'+formatNum(g.plays)+' 次</span>'
+ '<span class="score-go">▶ 详情</span>'
+ '</div>';
});
document.getElementById('scoreRows').innerHTML = rows;
// 侧栏:第一名 + 其余前几的精选
var top = list[0];
var side = '<div class="featured-card" onclick="openModal('+top.id+')">'
+ '<div class="featured-cover">'+coverHTML(top)+'</div>'
+ '<div class="featured-badge">NO.1 王者</div>'
+ '<div class="featured-body">'
+ '<h3>'+escapeHTML(top.name||'')+'</h3>'
+ '<p>'+escapeHTML((top.description||'').slice(0,70))+'</p>'
+ '<div class="featured-meta"><span>★ '+(Number(top.rating)||0).toFixed(1)+'</span><span>▶ '+formatNum(top.plays)+'</span><span>◆ '+(top.category_name||'未知')+'</span></div>'
+ '</div></div>';
// 小榜 top3
side += '<div class="featured-card"><div class="featured-body"><div style="font-size:12px;color:var(--purple);letter-spacing:2px;margin-bottom:10px">TOP 3 速览</div>';
list.slice(0,3).forEach(function(g,i){
side += '<div onclick="openModal('+g.id+')" style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:2px solid var(--bg-4);cursor:pointer">'
+ '<span style="font-size:12px;color:var(--green)"><b style="color:'+(i===0?'var(--yellow)':i===1?'#c0c0d0':'var(--red)')+'">0'+(i+1)+'</b> '+escapeHTML(g.name||'')+'</span>'
+ '<span style="font-size:11px;color:var(--yellow)">'+(Number(g.rating)||0).toFixed(1)+'★</span></div>';
});
side += '</div></div>';
document.getElementById('hotSide').innerHTML = side;
}
/* ============================================================
新游速报
============================================================ */
function renderNew(list){
list = (list||[]).slice(0,4);
if(!list.length){
document.getElementById('newGrid').innerHTML = emptyHTML('暂无新游数据');
return;
}
var html = '';
list.forEach(function(g){
var tags = parseJSON(g.tags,[]);
var tagHTML = tags.slice(0,3).map(function(t){return '<span>'+escapeHTML(t)+'</span>';}).join('');
html += '<div class="new-card" onclick="openModal('+g.id+')">'
+ '<div class="new-cover">'+coverHTML(g)+'<div class="new-badge">NEW!</div></div>'
+ '<div class="new-body">'
+ '<h4>'+escapeHTML(g.name||'')+'</h4>'
+ '<div class="dev">'+escapeHTML(g.developer||'未知厂商')+' · '+(g.release_date||'')+'</div>'
+ '<div class="new-tags">'+tagHTML+'</div>'
+ '</div></div>';
});
document.getElementById('newGrid').innerHTML = html;
}
/* ============================================================
游戏仓库
============================================================ */
function loadLibrary(){
var q = state.lib;
var url = API+'/games?page='+q.page+'&page_size='+q.page_size;
if(q.category_id) url += '&category_id='+encodeURIComponent(q.category_id);
if(q.keyword) url += '&keyword='+encodeURIComponent(q.keyword);
if(q.sort) url += '&sort='+encodeURIComponent(q.sort);
showLoading('gameGrid');
apiGet(url).then(function(res){
res = res||{};
q.list = res.list||[];
q.total = res.total||0;
renderLibrary();
}).catch(function(){
// 模拟回退
var all = mockGames(36);
var f = all;
if(q.category_id) f = f.filter(function(g){return String(g.category_id)===String(q.category_id);});
if(q.keyword) f = f.filter(function(g){return (g.name||'').indexOf(q.keyword)>=0 || (g.description||'').indexOf(q.keyword)>=0;});
if(q.sort==='rating') f.sort(function(a,b){return (b.rating||0)-(a.rating||0);});
else if(q.sort==='plays') f.sort(function(a,b){return (b.plays||0)-(a.plays||0);});
else if(q.sort==='newest') f.sort(function(a,b){return (b.release_date||'').localeCompare(a.release_date||'');});
var start = (q.page-1)*q.page_size;
q.list = f.slice(start, start+q.page_size);
q.total = f.length;
renderLibrary();
});
}
function renderLibrary(){
document.getElementById('libTotal').textContent = state.lib.total;
var grid = document.getElementById('gameGrid');
if(!state.lib.list.length){
grid.innerHTML = emptyHTML('未找到符合条件的游戏');
document.getElementById('pagination').innerHTML = '';
return;
}
var html = '';
state.lib.list.forEach(function(g){
var tags = parseJSON(g.tags,[]);
var tagHTML = tags.slice(0,3).map(function(t){return '<span>'+escapeHTML(t)+'</span>';}).join('');
var badges = '';
if(g.is_hot) badges += '<span class="gbadge gbadge-hot">HOT</span>';
if(g.is_new) badges += '<span class="gbadge gbadge-new">NEW</span>';
if(g.category_name) badges += '<span class="gbadge gbadge-cat">'+escapeHTML(g.category_name)+'</span>';
html += '<div class="game-card" onclick="openModal('+g.id+')">'
+ '<div class="gcard-cover">'+coverHTML(g)+'<div class="gcard-badges">'+badges+'</div><div class="gcard-rating">'+(Number(g.rating)||0).toFixed(1)+'★</div></div>'
+ '<div class="gcard-body">'
+ '<div class="gcard-name">'+escapeHTML(g.name||'')+'</div>'
+ '<div class="gcard-dev">'+escapeHTML(g.developer||'')+'</div>'
+ '<div class="gcard-tags">'+tagHTML+'</div>'
+ '<div class="gcard-foot"><span class="gcard-plays">▶ '+formatNum(g.plays)+'</span><span class="gcard-size">'+escapeHTML(g.size||'')+'</span></div>'
+ '</div></div>';
});
grid.innerHTML = html;
renderPagination();
}
function renderPagination(){
var q = state.lib;
var pages = Math.ceil(q.total/q.page_size)||1;
var cur = q.page;
var el = document.getElementById('pagination');
if(pages<=1){ el.innerHTML=''; return; }
var html = '';
html += '<button class="page-btn" '+(cur<=1?'disabled':'')+' onclick="goPage('+(cur-1)+')">◀</button>';
var from = Math.max(1, cur-2), to = Math.min(pages, cur+2);
if(from>1){ html += '<button class="page-btn" onclick="goPage(1)">1</button>'; if(from>2) html+='<span class="page-dots">···</span>'; }
for(var p=from;p<=to;p++){
html += '<button class="page-btn '+(p===cur?'active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
}
if(to<pages){ if(to<pages-1) html+='<span class="page-dots">···</span>'; html += '<button class="page-btn" onclick="goPage('+pages+')">'+pages+'</button>'; }
html += '<button class="page-btn" '+(cur>=pages?'disabled':'')+' onclick="goPage('+(cur+1)+')">▶</button>';
el.innerHTML = html;
}
function goPage(p){
state.lib.page = p;
loadLibrary();
scrollTo2('library');
}
function onLibSearch(){ state.lib.keyword = document.getElementById('libSearch').value.trim(); state.lib.page=1; loadLibrary(); }
function onLibSort(){ state.lib.sort = document.getElementById('libSort').value; state.lib.page=1; loadLibrary(); }
function onLibCat(){ state.lib.category_id = document.getElementById('libCat').value; syncCatBtn(state.lib.category_id); state.lib.page=1; loadLibrary(); }
function syncCatBtn(cid){
var btns = document.querySelectorAll('.cat-btn');
for(var i=0;i<btns.length;i++){
if(btns[i].getAttribute('data-cid')===String(cid)) btns[i].classList.add('active');
else btns[i].classList.remove('active');
}
}
// 搜索防抖
var navDebounce, libDebounce;
document.getElementById('libSearch').addEventListener('input', function(){
clearTimeout(libDebounce);
var v = this.value;
libDebounce = setTimeout(function(){ state.lib.keyword=v.trim(); state.lib.page=1; loadLibrary(); }, 360);
});
document.getElementById('navSearchInput').addEventListener('input', function(){
clearTimeout(navDebounce);
var v = this.value;
navDebounce = setTimeout(function(){
document.getElementById('libSearch').value = v;
state.lib.keyword = v.trim(); state.lib.page=1; loadLibrary();
if(v.trim()) scrollTo2('library');
}, 360);
});
/* ============================================================
游戏详情 Modal
============================================================ */
function openModal(id){
var mask = document.getElementById('modalMask');
mask.classList.add('open');
document.body.style.overflow='hidden';
document.getElementById('modalTitle').textContent = 'LOADING...';
document.getElementById('modalBody').innerHTML = '<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>';
apiGet(API+'/games/'+id).then(function(g){
renderModal(g);
}).catch(function(){
var g = mockGame(id);
renderModal(g);
});
}
function renderModal(g){
if(!g){ document.getElementById('modalBody').innerHTML = emptyHTML('游戏数据加载失败'); return; }
document.getElementById('modalTitle').textContent = g.name||'游戏详情';
var tags = parseJSON(g.tags,[]);
var features = parseJSON(g.features,[]);
var shots = parseJSON(g.screenshots,[]);
var tagHTML = tags.map(function(t){return '<span>'+escapeHTML(t)+'</span>';}).join('');
var featHTML = features.map(function(f){return '<li>'+escapeHTML(f)+'</li>';}).join('');
var shotHTML = shots.length ? shots.map(function(s){return '<img src="'+escapeHTML(s)+'" alt="截图" onerror="this.style.display=\'none\'">';}).join('') : '<div class="ph" style="width:200px;height:120px;display:grid;place-items:center;color:var(--text-dimer);font-size:11px;border:3px solid var(--ink)">暂无截图</div>';
var html = '<div class="modal-top">'
+ '<div class="modal-cover">'+coverHTML(g)+'</div>'
+ '<div class="modal-info">'
+ '<h2>'+escapeHTML(g.name||'')+'</h2>'
+ '<div class="m-dev">'+escapeHTML(g.developer||'')+(g.publisher?(' / '+escapeHTML(g.publisher)):'')+'</div>'
+ '<div class="modal-rate"><span class="rate-val">'+(Number(g.rating)||0).toFixed(1)+'</span>'+ratingBlocks(g.rating)+'<span style="font-size:11px;color:var(--text-dim)">/ 5.0</span></div>'
+ '<div class="m-tags">'+tagHTML+'</div>'
+ '<div class="m-meta">'
+ metaItem('分类', g.category_name||'未知')
+ metaItem('版本', g.version||'-')
+ metaItem('大小', g.size||'-')
+ metaItem('平台', g.platform||'多平台')
+ metaItem('发售日期', g.release_date||'-')
+ metaItem('下载量', formatNum(g.downloads||g.plays||0))
+ '</div>'
+ '</div></div>'
+ '<div class="modal-desc"><span class="lbl">▸ 游戏简介</span>'+escapeHTML(g.description||'暂无简介')+'</div>'
+ (features.length?'<div class="modal-features"><div class="lbl">★ 特色玩法</div><ul>'+featHTML+'</ul></div>':'')
+ '<div class="modal-shots"><div class="lbl">▣ 游戏截图</div><div class="shots-row">'+shotHTML+'</div></div>'
+ '<div class="modal-actions">'
+ '<button class="pbtn pbtn-yellow" onclick="alert(\'开始下载:'+escapeHTML(g.name||'')+'\')">立即下载 ▶</button>'
+ '<button class="pbtn pbtn-ghost" onclick="alert(\'已加入收藏\')">加入收藏</button>'
+ '<button class="pbtn pbtn-red" onclick="closeModal()">关闭</button>'
+ '</div>';
document.getElementById('modalBody').innerHTML = html;
}
function metaItem(label, val){
return '<div class="m-meta-item"><label>'+escapeHTML(label)+'</label><span>'+escapeHTML(val)+'</span></div>';
}
function closeModal(){
document.getElementById('modalMask').classList.remove('open');
document.body.style.overflow='';
}
document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeModal(); });
/* ============================================================
辅助
============================================================ */
function showLoading(id){ document.getElementById(id).innerHTML = '<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>'; }
function emptyHTML(msg){ return '<div class="empty-state" style="grid-column:1/-1"><div class="es-icon">▣</div><p>'+escapeHTML(msg)+'</p></div>'; }
function scrollTo2(id){ var el=document.getElementById(id); if(el){ var y=el.getBoundingClientRect().top+window.pageYOffset-80; window.scrollTo({top:y,behavior:'smooth'}); } }
function scrollTop2(){ window.scrollTo({top:0,behavior:'smooth'}); }
// 滚动显示返回顶部 + 导航高亮
window.addEventListener('scroll', function(){
var t = document.getElementById('toTop');
if(window.pageYOffset>400) t.classList.add('show'); else t.classList.remove('show');
// 导航高亮
var secs = ['home','hot','new','library'];
var cur = 'home';
for(var i=0;i<secs.length;i++){ var el=document.getElementById(secs[i]); if(el && el.getBoundingClientRect().top<160) cur=secs[i]; }
// hero 无 id, 用顶部
});
// 像素评分样式注入
(function(){
var s = document.createElement('style');
s.textContent = '.rb-fill{color:var(--yellow);letter-spacing:0}.rb-empty{color:var(--bg-3);letter-spacing:0}.rate-val-mini{color:var(--yellow);font-weight:bold;margin-right:2px}';
document.head.appendChild(s);
})();
// Hero 像素角色 (CSS box-shadow 像素画)
(function(){
var el = document.getElementById('heroChar');
if(!el) return;
// 用多重 box-shadow 画一个像素小人 (绿帽红衣)
var p = 'var(--ink)';
var g = 'var(--green)';
var r = 'var(--red)';
var s = 'var(--yellow)'; // 肤色用黄
var br = 'var(--bg-4)';
var px = 8; // 每像素 8px
function row(y, str){ // str 形如 "GGGGGG"
var shadows = [];
for(var x=0;x<str.length;x++){
var ch = str[x];
var c;
if(ch==='G') c=g; else if(ch==='R') c=r; else if(ch==='S') c=s; else if(ch==='B') c=br; else if(ch==='K') c=p; else continue;
shadows.push((x*px)+'px '+(y*px)+'px 0 0 '+c);
}
return shadows.join(',');
}
var map = [
'..GGGG..',
'.GGGGGG.',
'SSKSSKSS',
'SSSSSSSS',
'SSKKKKSS',
'RRRRRRRR',
'RRRRRRRR',
'RRRRRRRR',
'.R.RR.R.',
'.S.SS.S.',
'.B.BB.B.'
];
var all=[];
for(var i=0;i<map.length;i++){ all.push(row(i, map[i])); }
el.style.cssText = 'width:'+px+'px;height:'+px+'px;position:relative;background:transparent;box-shadow:'+all.join(',')+';animation:float-up 2.2s ease-in-out infinite';
})();
/* ============================================================
模拟数据 (API 不可用时回退, 便于预览)
============================================================ */
var NAMES = ['龙之传说','星际远征','暗影刺客','钢铁战线','幻境迷宫','魔法纪元','赛车狂飙','机甲风暴','荒野求生','深海探险','天空之城','末日堡垒','剑与魔法','像素英雄','霓虹狙击','炼金术士','勇者斗恶龙','银河漂流','地下王国','雷霆战机','忍者之道','王者棋盘','农场物语','太空殖民','蒸汽朋克','光明使者','暗夜猎手','极速漂移','神秘海域','传奇战场','永恒之歌','混沌之心','机械迷城','风暴英雄','烈焰之刃'];
var CATS = [['动作','⚔'],['角色扮演','★'],['射击','◉'],['冒险','▲'],['策略','♟'],['模拟','◆'],['体育','●'],['益智','♦']];
var TAGS = ['开放世界','巨龙','史诗','多人联机','硬核','剧情','生存','沙盒','潜行','竞技','合作',' Roguelike','roguelike','解谜','建造','魔法','科幻','末日','机甲','赛车'];
var FEATS = ['无缝开放世界','实时战斗系统','动态天气系统','多结局剧情','合作多人模式','物理引擎破坏','技能树系统','宠物养成','昼夜循环','随机地下城'];
var PUB = ['像素工作室','银河游戏','雷霆互动','幻影科技','复古发行','独立工坊'];
function rand(a){ return a[Math.floor(Math.random()*a.length)]; }
function randInt(a,b){ return Math.floor(Math.random()*(b-a+1))+a; }
function mockGame(id){
var name = NAMES[(id-1)%NAMES.length] + (id>NAMES.length?(' '+Math.floor((id-1)/NAMES.length)):'');
var ci = (id-1)%CATS.length;
var tagN = randInt(2,4), tags=[]; for(var i=0;i<tagN;i++){ var t=rand(TAGS); if(tags.indexOf(t)<0)tags.push(t);}
var feN = randInt(2,3), feats=[]; for(var j=0;j<feN;j++){ var f=rand(FEATS); if(feats.indexOf(f)<0)feats.push(f);}
return {
id:id, name:name, description:'这是一款充满复古魅力的像素风游戏,融合了经典玩法与现代设计。玩家将踏上史诗般的冒险旅程,探索广袤的世界,挑战强大的敌人,揭开隐藏的秘密。精美的像素画面与动感的8-bit音效带您重温黄金游戏时代。',
category_id: ci+1, category_name: CATS[ci][0],
cover:'', developer:rand(PUB), publisher:rand(PUB),
rating: Math.round((3.5+Math.random()*1.5)*10)/10,
plays: randInt(1000,9800000), downloads: randInt(500,5000000),
size: randInt(20,120)+'.'+randInt(0,9)+' GB', version:'v'+randInt(1,3)+'.'+randInt(0,9)+'.'+randInt(0,9),
release_date:'2026-'+String(randInt(1,7)).padStart(2,'0')+'-'+String(randInt(1,28)).padStart(2,'0'),
platform:'PC / PS5 / Switch', tags:JSON.stringify(tags), screenshots:'[]', features:JSON.stringify(feats),
is_hot: Math.random()>0.5, is_new: Math.random()>0.5, sort:id, status:1
};
}
function mockGames(n, hotOnly, newOnly){
var arr=[];
for(var i=0;i<n;i++){
var g = mockGame(randInt(1,35));
if(hotOnly) g.is_hot=true;
if(newOnly){ g.is_new=true; }
arr.push(g);
}
return arr;
}
/* ============================================================
初始化
============================================================ */
function init(){
// 分类先加载
loadCategories().then(function(){
// 首页 + 仓库
loadHome();
loadLibrary();
});
// 每秒游戏币+1 小彩蛋
setInterval(function(){
var el = document.getElementById('coinCount');
var v = parseInt(el.textContent,10)||0;
if(Math.random()>0.7){ el.textContent = v+1; }
}, 2000);
}
init();
</script>
</body>
</html>