- 所有18套前端模板 + 管理后台共20个HTML文件添加i18n引擎 - 前端JavaScript方案:I18N_LOCALES翻译字典 + data-i18n属性 + t()函数调用 - 语言切换器UI:下拉菜单选择,匹配各模板主题风格 - 语言持久化:localStorage存储 + URL参数覆盖 + 浏览器语言自动检测 - onLangChange回调:语言切换时动态重新渲染所有内容 - 支持语言:zh-CN(简体中文) / zh-TW(繁体中文) / en(English)
1396 lines
78 KiB
HTML
1396 lines
78 KiB
HTML
<!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>
|
||
: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-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}
|
||
.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)}
|
||
.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)}}
|
||
.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}
|
||
.lang-switcher{position:relative;margin-left:8px}
|
||
.lang-btn{display:flex;align-items:center;gap:6px;padding:8px 14px;background:var(--bg-3);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);font-weight:bold;color:var(--text-dim);font-size:12px;letter-spacing:1px;cursor:pointer;transition:.08s}
|
||
.lang-btn:hover{color:var(--yellow);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
|
||
.lang-btn svg{width:16px;height:16px}
|
||
.lang-dropdown{position:absolute;top:calc(100% + 8px);right:0;min-width:140px;z-index:200;background:var(--bg-2);border:4px solid var(--ink);box-shadow:6px 6px 0 0 var(--shadow);opacity:0;transform:translateY(-8px);pointer-events:none;transition:all .2s}
|
||
.lang-dropdown.open{opacity:1;transform:none;pointer-events:auto}
|
||
.lang-option{display:flex;align-items:center;gap:8px;padding:10px 14px;font-size:13px;color:var(--text-dim);cursor:pointer;transition:all .08s;border-bottom:2px solid var(--bg-4)}
|
||
.lang-option:last-child{border-bottom:none}
|
||
.lang-option:hover{background:var(--bg-3);color:var(--yellow)}
|
||
.lang-option.active{color:var(--green);font-weight:bold}
|
||
.lang-option .check{width:14px;height:14px;opacity:0;transition:opacity .2s}
|
||
.lang-option.active .check{opacity:1}
|
||
.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{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{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)}
|
||
.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-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-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}
|
||
.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:"\25BC";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-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:"\25B6";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{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" data-i18n="logo_title">PIXEL GAMES<small data-i18n="logo_sub">PIXEL GAMES</small></div>
|
||
</div>
|
||
<nav class="nav" id="nav">
|
||
<a href="#" class="active" data-nav="home" data-i18n="nav_home"></a>
|
||
<a href="#library" data-nav="lib" data-i18n="nav_library"></a>
|
||
<a href="#hot" data-nav="rank" data-i18n="nav_hot"></a>
|
||
<a href="#new" data-nav="new" data-i18n="nav_new"></a>
|
||
</nav>
|
||
<form class="search" id="navSearch" onsubmit="return false">
|
||
<input type="text" id="navSearchInput" data-i18n-placeholder="search_placeholder" aria-label="">
|
||
<button type="submit" aria-label="Q">Q</button>
|
||
</form>
|
||
<div class="coin-counter" title="">
|
||
<span class="pix pix-coin"></span>
|
||
<span id="coinCount">9999</span>
|
||
</div>
|
||
<div class="lang-switcher" id="langSwitcher">
|
||
<button class="lang-btn" id="langBtn" onclick="toggleLangDropdown()">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/></svg>
|
||
<span id="langLabel">中文</span>
|
||
</button>
|
||
<div class="lang-dropdown" id="langDropdown">
|
||
<div class="lang-option active" data-lang="zh-CN" onclick="switchLang('zh-CN')">
|
||
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
中文
|
||
</div>
|
||
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
|
||
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
繁體中文
|
||
</div>
|
||
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
|
||
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
English
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- HERO -->
|
||
<section class="hero" id="home">
|
||
<div class="hero-grid-bg"></div>
|
||
<div class="container hero-inner">
|
||
<div class="hero-text">
|
||
<div class="hero-sub" data-i18n="hero_sub"></div>
|
||
<h1 class="hero-title"><span class="y" data-i18n="hero_title_line1"></span> · <span class="r" data-i18n="hero_title_line2"></span></h1>
|
||
<div class="press-start blink" data-i18n="hero_press_start"></div>
|
||
<div class="hero-tags">
|
||
<span class="hero-tag" data-i18n="hero_tag_1"></span>
|
||
<span class="hero-tag" data-i18n="hero_tag_2"></span>
|
||
<span class="hero-tag" data-i18n="hero_tag_3"></span>
|
||
<span class="hero-tag" data-i18n="hero_tag_4"></span>
|
||
</div>
|
||
<div class="hero-cta">
|
||
<button class="pbtn pbtn-yellow" onclick="scrollTo2('library')" data-i18n="hero_cta_play"></button>
|
||
<button class="pbtn pbtn-ghost" onclick="scrollTo2('hot')" data-i18n="hero_cta_rank"></button>
|
||
<span class="hero-coin"><span class="pix pix-coin"></span> <span data-i18n="hero_daily_coin"></span></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>
|
||
|
||
<!-- TICKER -->
|
||
<div class="ticker">
|
||
<div class="ticker-track" id="tickerTrack">
|
||
<span class="r">★ HOT</span><span data-i18n="ticker_1"></span>
|
||
<span class="g">● NEW</span><span data-i18n="ticker_2"></span>
|
||
<span class="r">★ EVENT</span><span data-i18n="ticker_3"></span>
|
||
<span class="g">● TIP</span><span data-i18n="ticker_4"></span>
|
||
<span class="r">★ UPDATE</span><span data-i18n="ticker_5"></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" data-i18n="section_hot"></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 data-i18n="score_name"></span><span class="h-rate" data-i18n="score_rating"></span><span data-i18n="score_plays"></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" data-i18n="section_new"></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" data-i18n="section_library"></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" data-i18n-placeholder="lib_search_placeholder" aria-label="">
|
||
<button onclick="onLibSearch()">Q</button>
|
||
</div>
|
||
<div class="lib-select">
|
||
<select id="libSort" onchange="onLibSort()">
|
||
<option value="" data-i18n="sort_default"></option>
|
||
<option value="rating" data-i18n="sort_rating"></option>
|
||
<option value="plays" data-i18n="sort_plays"></option>
|
||
<option value="newest" data-i18n="sort_newest"></option>
|
||
</select>
|
||
</div>
|
||
<div class="lib-select">
|
||
<select id="libCat" onchange="onLibCat()">
|
||
<option value="" data-i18n="cat_all"></option>
|
||
</select>
|
||
</div>
|
||
<div class="lib-count"><span data-i18n="lib_total_prefix"></span> <b id="libTotal">0</b> <span data-i18n="lib_total_suffix"></span></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 data-i18n="footer_brand_desc"></p>
|
||
</div>
|
||
<div class="foot-col">
|
||
<h5 data-i18n="footer_game_center"></h5>
|
||
<a href="#library" data-i18n="footer_lib"></a>
|
||
<a href="#hot" data-i18n="footer_hot"></a>
|
||
<a href="#new" data-i18n="footer_new"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_browse_cat"></a>
|
||
</div>
|
||
<div class="foot-col">
|
||
<h5 data-i18n="footer_community"></h5>
|
||
<a href="#" onclick="return false" data-i18n="footer_forum"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_guide"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_rating"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_workshop"></a>
|
||
</div>
|
||
<div class="foot-col">
|
||
<h5 data-i18n="footer_about"></h5>
|
||
<a href="#" onclick="return false" data-i18n="footer_about_us"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_contact"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_privacy"></a>
|
||
<a href="#" onclick="return false" data-i18n="footer_terms"></a>
|
||
</div>
|
||
</div>
|
||
<div class="foot-bottom">
|
||
<p data-i18n="footer_copyright"></p>
|
||
<p class="foot-press blink" data-i18n="footer_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">LOADING...</span>
|
||
<button class="modal-close" onclick="closeModal()" aria-label="X">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>
|
||
|
||
<!-- TO TOP -->
|
||
<button class="to-top" id="toTop" onclick="scrollTop2()" aria-label="Top">▲</button>
|
||
|
||
<script>
|
||
/* ============================================================
|
||
PIXEL GAMES — i18n System
|
||
============================================================ */
|
||
const I18N_LOCALES = {
|
||
'zh-CN': {
|
||
logo_title: 'PIXEL GAMES',
|
||
logo_sub: '像素游戏厅',
|
||
nav_home: '首页',
|
||
nav_library: '游戏库',
|
||
nav_hot: '排行榜',
|
||
nav_new: '新游',
|
||
search_placeholder: '搜索游戏...',
|
||
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
|
||
hero_title_line1: '像素',
|
||
hero_title_line2: '世界',
|
||
hero_press_start: 'PRESS START',
|
||
hero_tag_1: '\u25B8 8-BIT 复古',
|
||
hero_tag_2: '\u25B8 海量游戏',
|
||
hero_tag_3: '\u25B8 极速下载',
|
||
hero_tag_4: '\u25B8 玩家社区',
|
||
hero_cta_play: '开始游戏 \u25B6',
|
||
hero_cta_rank: '查看排行',
|
||
hero_daily_coin: '每日登录 +100 币',
|
||
ticker_1: '《龙之传说》已登顶本周排行榜',
|
||
ticker_2: '《星际远征》正式上线,立即下载体验',
|
||
ticker_3: '周末双倍经验活动开启中',
|
||
ticker_4: '每日签到领取免费游戏币',
|
||
ticker_5: '《暗影刺客》v2.1.0 更新发布',
|
||
section_hot: '热门排行榜',
|
||
section_new: '新游速报',
|
||
section_library: '游戏仓库',
|
||
score_name: '游戏名称',
|
||
score_rating: '评分',
|
||
score_plays: '游玩次数',
|
||
lib_search_placeholder: '输入游戏名称或关键词...',
|
||
sort_default: '默认排序',
|
||
sort_rating: '评分最高',
|
||
sort_plays: '最多游玩',
|
||
sort_newest: '最新发布',
|
||
cat_all: '全部分类',
|
||
lib_total_prefix: '共',
|
||
lib_total_suffix: '款游戏',
|
||
empty_hot: '暂无热门游戏数据',
|
||
empty_new: '暂无新游数据',
|
||
empty_no_match: '未找到符合条件的游戏',
|
||
modal_category: '分类',
|
||
modal_version: '版本',
|
||
modal_size: '大小',
|
||
modal_platform: '平台',
|
||
modal_release_date: '发售日期',
|
||
modal_downloads: '下载量',
|
||
modal_desc_title: '\u25B8 游戏简介',
|
||
modal_desc_empty: '暂无简介',
|
||
modal_features_title: '\u2605 特色玩法',
|
||
modal_shots_title: '\u25A3 游戏截图',
|
||
modal_shots_empty: '暂无截图',
|
||
modal_download_btn: '立即下载 \u25B6',
|
||
modal_fav_btn: '加入收藏',
|
||
modal_close_btn: '关闭',
|
||
toast_downloading: '开始下载',
|
||
toast_fav_added: '已加入收藏',
|
||
footer_brand_desc: '复古像素风游戏平台 \u00B7 汇集海量经典与独立游戏 \u00B7 为玩家打造纯粹的 8-BIT 游戏体验。INSERT COIN, PRESS START, 享受游戏本来的乐趣。',
|
||
footer_game_center: '游戏中心',
|
||
footer_lib: '游戏仓库',
|
||
footer_hot: '热门排行',
|
||
footer_new: '新游速报',
|
||
footer_browse_cat: '分类浏览',
|
||
footer_community: '玩家社区',
|
||
footer_forum: '玩家论坛',
|
||
footer_guide: '攻略中心',
|
||
footer_rating: '评分评价',
|
||
footer_workshop: '创作工坊',
|
||
footer_about: '关于我们',
|
||
footer_about_us: '关于平台',
|
||
footer_contact: '联系我们',
|
||
footer_privacy: '隐私政策',
|
||
footer_terms: '服务条款',
|
||
footer_copyright: '\u00A9 2026 PIXEL GAMES \u00B7 像素游戏厅 \u00B7 保留所有权利',
|
||
footer_press_start: '\u25B2 PRESS START \u25B2',
|
||
cat_action: '动作',
|
||
cat_rpg: '角色扮演',
|
||
cat_shooter: '射击',
|
||
cat_adventure: '冒险',
|
||
cat_strategy: '策略',
|
||
cat_simulation: '模拟',
|
||
cat_sports: '体育',
|
||
cat_puzzle: '益智',
|
||
mock_desc_1: '这是一款充满复古魅力的像素风游戏,融合了经典玩法与现代设计。玩家将踏上史诗般的冒险旅程,探索广袤的世界,挑战强大的敌人,揭开隐藏的秘密。精美的像素画面与动感的8-bit音效带您重温黄金游戏时代。',
|
||
mock_desc_2: '在像素构筑的奇幻大陆上,你将扮演一位被命运选中的勇者,与忠诚的伙伴并肩作战,对抗邪恶势力。经典的回合制战斗、丰富的技能树与多样的装备系统,带你体验最纯粹的RPG乐趣。',
|
||
mock_desc_3: '激烈的像素射击对战,支持多人在线竞技。多种武器选择、多变的地图场景、快节奏的战斗体验,让你在复古画风中感受肾上腺素飙升的快感。',
|
||
mock_desc_4: '建造、探索、生存——在无限大的像素沙盒世界中,你的想象力是唯一的限制。收集资源、打造工具、建造庇护所,与好友一起开启无尽冒险。',
|
||
mock_feat_1: '无缝开放世界',
|
||
mock_feat_2: '实时战斗系统',
|
||
mock_feat_3: '动态天气系统',
|
||
mock_feat_4: '多结局剧情',
|
||
mock_feat_5: '合作多人模式',
|
||
mock_feat_6: '物理引擎破坏',
|
||
mock_feat_7: '技能树系统',
|
||
mock_feat_8: '昼夜循环',
|
||
mock_feat_9: '随机地下城',
|
||
mock_feat_10: '宠物养成'
|
||
},
|
||
'en': {
|
||
logo_title: 'PIXEL GAMES',
|
||
logo_sub: 'Pixel Arcade',
|
||
nav_home: 'Home',
|
||
nav_library: 'Library',
|
||
nav_hot: 'Rankings',
|
||
nav_new: 'New',
|
||
search_placeholder: 'Search games...',
|
||
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
|
||
hero_title_line1: 'Pixel',
|
||
hero_title_line2: 'World',
|
||
hero_press_start: 'PRESS START',
|
||
hero_tag_1: '\u25B8 8-BIT Retro',
|
||
hero_tag_2: '\u25B8 Huge Library',
|
||
hero_tag_3: '\u25B8 Fast Download',
|
||
hero_tag_4: '\u25B8 Community',
|
||
hero_cta_play: 'Play Now \u25B6',
|
||
hero_cta_rank: 'View Rankings',
|
||
hero_daily_coin: 'Daily login +100 coins',
|
||
ticker_1: 'Dragon Legend tops this week\'s chart',
|
||
ticker_2: 'Star Expedition officially launched, download now',
|
||
ticker_3: 'Weekend double XP event now active',
|
||
ticker_4: 'Daily sign-in for free game coins',
|
||
ticker_5: 'Shadow Assassin v2.1.0 update released',
|
||
section_hot: 'Top Rankings',
|
||
section_new: 'New Releases',
|
||
section_library: 'Game Library',
|
||
score_name: 'Game Title',
|
||
score_rating: 'Rating',
|
||
score_plays: 'Plays',
|
||
lib_search_placeholder: 'Enter game name or keyword...',
|
||
sort_default: 'Default Sort',
|
||
sort_rating: 'Top Rated',
|
||
sort_plays: 'Most Played',
|
||
sort_newest: 'Newest',
|
||
cat_all: 'All Categories',
|
||
lib_total_prefix: 'Total',
|
||
lib_total_suffix: 'games',
|
||
empty_hot: 'No ranking data yet',
|
||
empty_new: 'No new games yet',
|
||
empty_no_match: 'No matching games found',
|
||
modal_category: 'Category',
|
||
modal_version: 'Version',
|
||
modal_size: 'Size',
|
||
modal_platform: 'Platform',
|
||
modal_release_date: 'Release Date',
|
||
modal_downloads: 'Downloads',
|
||
modal_desc_title: '\u25B8 Description',
|
||
modal_desc_empty: 'No description available',
|
||
modal_features_title: '\u2605 Features',
|
||
modal_shots_title: '\u25A3 Screenshots',
|
||
modal_shots_empty: 'No screenshots',
|
||
modal_download_btn: 'Download \u25B6',
|
||
modal_fav_btn: 'Add to Favorites',
|
||
modal_close_btn: 'Close',
|
||
toast_downloading: 'Downloading',
|
||
toast_fav_added: 'Added to favorites',
|
||
footer_brand_desc: 'A retro pixel-style game platform featuring a massive collection of classic and indie games. Built for pure 8-BIT gaming experience. INSERT COIN, PRESS START, enjoy gaming at its finest.',
|
||
footer_game_center: 'Game Center',
|
||
footer_lib: 'Game Library',
|
||
footer_hot: 'Top Charts',
|
||
footer_new: 'New Releases',
|
||
footer_browse_cat: 'Browse Categories',
|
||
footer_community: 'Community',
|
||
footer_forum: 'Forum',
|
||
footer_guide: 'Guides',
|
||
footer_rating: 'Reviews',
|
||
footer_workshop: 'Workshop',
|
||
footer_about: 'About',
|
||
footer_about_us: 'About Us',
|
||
footer_contact: 'Contact',
|
||
footer_privacy: 'Privacy Policy',
|
||
footer_terms: 'Terms of Service',
|
||
footer_copyright: '\u00A9 2026 PIXEL GAMES. All rights reserved.',
|
||
footer_press_start: '\u25B2 PRESS START \u25B2',
|
||
cat_action: 'Action',
|
||
cat_rpg: 'RPG',
|
||
cat_shooter: 'Shooter',
|
||
cat_adventure: 'Adventure',
|
||
cat_strategy: 'Strategy',
|
||
cat_simulation: 'Simulation',
|
||
cat_sports: 'Sports',
|
||
cat_puzzle: 'Puzzle',
|
||
mock_desc_1: 'A charming pixel-art game blending classic gameplay with modern design. Embark on an epic adventure, explore vast worlds, defeat powerful enemies, and uncover hidden secrets. Beautiful pixel graphics and dynamic 8-bit sound transport you to the golden age of gaming.',
|
||
mock_desc_2: 'On a pixel-crafted fantasy continent, play as a chosen hero fighting alongside loyal companions against evil forces. Classic turn-based combat, rich skill trees, and diverse equipment systems deliver the purest RPG experience.',
|
||
mock_desc_3: 'Intense pixel shooting combat with multiplayer online battles. Multiple weapon choices, dynamic maps, and fast-paced action bring adrenaline-pumping thrills in retro style.',
|
||
mock_desc_4: 'Build, explore, survive — in an infinite pixel sandbox world where your imagination is the only limit. Collect resources, craft tools, build shelters, and embark on endless adventures with friends.',
|
||
mock_feat_1: 'Seamless Open World',
|
||
mock_feat_2: 'Real-Time Combat',
|
||
mock_feat_3: 'Dynamic Weather',
|
||
mock_feat_4: 'Multiple Endings',
|
||
mock_feat_5: 'Co-op Multiplayer',
|
||
mock_feat_6: 'Physics Destruction',
|
||
mock_feat_7: 'Skill Tree System',
|
||
mock_feat_8: 'Day/Night Cycle',
|
||
mock_feat_9: 'Random Dungeons',
|
||
mock_feat_10: 'Pet Companion'
|
||
},
|
||
'zh-TW': {
|
||
logo_title: 'PIXEL GAMES',
|
||
logo_sub: '像素遊戲廳',
|
||
nav_home: '首頁',
|
||
nav_library: '遊戲庫',
|
||
nav_hot: '排行榜',
|
||
nav_new: '新遊',
|
||
search_placeholder: '搜尋遊戲...',
|
||
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
|
||
hero_title_line1: '像素',
|
||
hero_title_line2: '世界',
|
||
hero_press_start: 'PRESS START',
|
||
hero_tag_1: '\u25B8 8-BIT 復古',
|
||
hero_tag_2: '\u25B8 海量遊戲',
|
||
hero_tag_3: '\u25B8 極速下載',
|
||
hero_tag_4: '\u25B8 玩家社群',
|
||
hero_cta_play: '開始遊戲 \u25B6',
|
||
hero_cta_rank: '查看排行',
|
||
hero_daily_coin: '每日登入 +100 幣',
|
||
ticker_1: '《龍之傳說》已登頂本週排行榜',
|
||
ticker_2: '《星際遠征》正式上線,立即下載體驗',
|
||
ticker_3: '週末雙倍經驗活動開啟中',
|
||
ticker_4: '每日簽到領取免費遊戲幣',
|
||
ticker_5: '《暗影刺客》v2.1.0 更新發布',
|
||
section_hot: '熱門排行榜',
|
||
section_new: '新遊速報',
|
||
section_library: '遊戲倉庫',
|
||
score_name: '遊戲名稱',
|
||
score_rating: '評分',
|
||
score_plays: '遊玩次數',
|
||
lib_search_placeholder: '輸入遊戲名稱或關鍵詞...',
|
||
sort_default: '預設排序',
|
||
sort_rating: '評分最高',
|
||
sort_plays: '最多遊玩',
|
||
sort_newest: '最新發布',
|
||
cat_all: '全部分類',
|
||
lib_total_prefix: '共',
|
||
lib_total_suffix: '款遊戲',
|
||
empty_hot: '暫無熱門遊戲資料',
|
||
empty_new: '暫無新遊資料',
|
||
empty_no_match: '未找到符合條件的遊戲',
|
||
modal_category: '分類',
|
||
modal_version: '版本',
|
||
modal_size: '大小',
|
||
modal_platform: '平台',
|
||
modal_release_date: '發售日期',
|
||
modal_downloads: '下載量',
|
||
modal_desc_title: '\u25B8 遊戲簡介',
|
||
modal_desc_empty: '暫無簡介',
|
||
modal_features_title: '\u2605 特色玩法',
|
||
modal_shots_title: '\u25A3 遊戲截圖',
|
||
modal_shots_empty: '暫無截圖',
|
||
modal_download_btn: '立即下載 \u25B6',
|
||
modal_fav_btn: '加入收藏',
|
||
modal_close_btn: '關閉',
|
||
toast_downloading: '開始下載',
|
||
toast_fav_added: '已加入收藏',
|
||
footer_brand_desc: '復古像素風遊戲平台 \u00B7 匯集海量經典與獨立遊戲 \u00B7 為玩家打造純粹的 8-BIT 遊戲體驗。INSERT COIN, PRESS START, 享受遊戲本來的樂趣。',
|
||
footer_game_center: '遊戲中心',
|
||
footer_lib: '遊戲倉庫',
|
||
footer_hot: '熱門排行',
|
||
footer_new: '新遊速報',
|
||
footer_browse_cat: '分類瀏覽',
|
||
footer_community: '玩家社群',
|
||
footer_forum: '玩家論壇',
|
||
footer_guide: '攻略中心',
|
||
footer_rating: '評分評價',
|
||
footer_workshop: '創作工坊',
|
||
footer_about: '關於我們',
|
||
footer_about_us: '關於平台',
|
||
footer_contact: '聯絡我們',
|
||
footer_privacy: '隱私政策',
|
||
footer_terms: '服務條款',
|
||
footer_copyright: '\u00A9 2026 PIXEL GAMES \u00B7 像素遊戲廳 \u00B7 保留所有權利',
|
||
footer_press_start: '\u25B2 PRESS START \u25B2',
|
||
cat_action: '動作',
|
||
cat_rpg: '角色扮演',
|
||
cat_shooter: '射擊',
|
||
cat_adventure: '冒險',
|
||
cat_strategy: '策略',
|
||
cat_simulation: '模擬',
|
||
cat_sports: '體育',
|
||
cat_puzzle: '益智',
|
||
mock_desc_1: '這是一款充滿復古魅力的像素風遊戲,融合了經典玩法與現代設計。玩家將踏上史詩般的冒險旅程,探索廣袤的世界,挑戰強大的敵人,揭開隱藏的秘密。精美的像素畫面與動感的8-bit音效帶您重溫黃金遊戲時代。',
|
||
mock_desc_2: '在像素構築的奇幻大陸上,你將扮演一位被命運選中的勇者,與忠誠的夥伴並肩作戰,對抗邪惡勢力。經典的回合制戰鬥、豐富的技能樹與多樣的裝備系統,帶你體驗最純粹的RPG樂趣。',
|
||
mock_desc_3: '激烈的像素射擊對戰,支援多人線上競技。多種武器選擇、多變的地圖場景、快節奏的戰鬥體驗,讓你在復古畫風中感受腎上腺素飆升的快感。',
|
||
mock_desc_4: '建造、探索、生存——在無限大的像素沙盒世界中,你的想像力是唯一的限制。收集資源、打造工具、建造庇護所,與好友一起開啟無盡冒險。',
|
||
mock_feat_1: '無縫開放世界',
|
||
mock_feat_2: '即時戰鬥系統',
|
||
mock_feat_3: '動態天氣系統',
|
||
mock_feat_4: '多結局劇情',
|
||
mock_feat_5: '合作多人模式',
|
||
mock_feat_6: '物理引擎破壞',
|
||
mock_feat_7: '技能樹系統',
|
||
mock_feat_8: '晝夜循環',
|
||
mock_feat_9: '隨機地下城',
|
||
mock_feat_10: '寵物養成'
|
||
}
|
||
};
|
||
const LANG_LABELS = {'zh-CN':'中文','en':'EN','zh-TW':'繁中'};
|
||
|
||
let currentLang = (function(){
|
||
try{
|
||
var u=new URLSearchParams(location.search).get('lang');
|
||
if(u&&I18N_LOCALES[u]) return u;
|
||
}catch(e){}
|
||
try{var s=localStorage.getItem('lang');if(s&&I18N_LOCALES[s]) return s;}catch(e){}
|
||
var b=navigator.language||navigator.userLanguage||'';
|
||
if(b.startsWith('zh-TW')||b.startsWith('zh-Hant')) return 'zh-TW';
|
||
if(b.startsWith('zh')) return 'zh-CN';
|
||
return 'en';
|
||
})();
|
||
|
||
function t(key){return (I18N_LOCALES[currentLang]&&I18N_LOCALES[currentLang][key])||(I18N_LOCALES['zh-CN'][key])||key;}
|
||
|
||
function applyI18n(){
|
||
document.querySelectorAll('[data-i18n]').forEach(function(el){
|
||
var key=el.getAttribute('data-i18n');
|
||
el.textContent=t(key);
|
||
});
|
||
document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){
|
||
var key=el.getAttribute('data-i18n-placeholder');
|
||
el.placeholder=t(key);
|
||
});
|
||
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
|
||
var lbl=document.getElementById('langLabel');
|
||
if(lbl) lbl.textContent=LANG_LABELS[currentLang]||currentLang;
|
||
document.querySelectorAll('.lang-option').forEach(function(opt){
|
||
opt.classList.toggle('active',opt.getAttribute('data-lang')===currentLang);
|
||
});
|
||
document.title='PIXEL GAMES \u00B7 '+t('logo_sub');
|
||
}
|
||
|
||
function switchLang(lang){
|
||
if(!I18N_LOCALES[lang]) return;
|
||
currentLang=lang;
|
||
try{localStorage.setItem('lang',lang);}catch(e){}
|
||
applyI18n();
|
||
if(typeof onLangChange==='function') onLangChange(lang);
|
||
document.getElementById('langDropdown').classList.remove('open');
|
||
}
|
||
window.switchLang = switchLang;
|
||
|
||
function toggleLangDropdown(){
|
||
document.getElementById('langDropdown').classList.toggle('open');
|
||
}
|
||
window.toggleLangDropdown = toggleLangDropdown;
|
||
document.addEventListener('click',function(e){
|
||
var sw=document.getElementById('langSwitcher');
|
||
if(sw&&!sw.contains(e.target)) document.getElementById('langDropdown').classList.remove('open');
|
||
});
|
||
|
||
let onLangChange = null;
|
||
|
||
/* ============================================================
|
||
PIXEL GAMES — 前端逻辑
|
||
============================================================ */
|
||
|
||
var API = '/api/v1/game';
|
||
|
||
/* ---------- 工具函数 ---------- */
|
||
function formatNum(n){
|
||
n = Number(n)||0;
|
||
if(n>=100000000) return (n/100000000).toFixed(1).replace(/\.0$/,'')+'\u4EBF';
|
||
if(n>=10000) return (n/10000).toFixed(1).replace(/\.0$/,'')+'\u4E07';
|
||
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 {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c];
|
||
});
|
||
}
|
||
function ratingBlocks(rating){
|
||
var r = Number(rating)||0;
|
||
var filled = Math.round(r*2);
|
||
var s = '';
|
||
for(var i=0;i<10;i++){ s += i<filled ? '\u2588' : '\u2591'; }
|
||
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 error');
|
||
return res.data;
|
||
}
|
||
return res;
|
||
});
|
||
}
|
||
|
||
/* ---------- 状态 ---------- */
|
||
var state = {
|
||
cats: [],
|
||
lib: { list:[], total:0, page:1, page_size:12, keyword:'', sort:'', category_id:'' },
|
||
homeLoaded:false
|
||
};
|
||
|
||
/* ============================================================
|
||
模拟数据
|
||
============================================================ */
|
||
var NAMES = {
|
||
'zh-CN': ['龙之传说','星际远征','暗影刺客','钢铁战线','幻境迷宫','魔法纪元','赛车狂飙','机甲风暴','荒野求生','深海探险','天空之城','末日堡垒','剑与魔法','像素英雄','霓虹狙击','炼金术士','勇者斗恶龙','银河漂流','地下王国','雷霆战机','忍者之道','王者棋盘','农场物语','太空殖民','蒸汽朋克','光明使者','暗夜猎手','极速漂移','神秘海域','传奇战场','永恒之歌','混沌之心','机械迷城','风暴英雄','烈焰之刃'],
|
||
'en': ['Dragon Legend','Star Expedition','Shadow Assassin','Steel Frontline','Fantasy Maze','Magic Era','Racing Fury','Mech Storm','Wilderness','Deep Sea','Sky Fortress','Doomsday Base','Swords & Magic','Pixel Hero','Neon Sniper','Alchemist','Dragon Quest','Galaxy Drift','Underground Kingdom','Thunder Fighter','Ninja Way','King\'s Board','Farm Story','Space Colony','Steam Punk','Light Bringer','Night Hunter','Speed Drift','Mysterious Sea','Legend Battlefield','Eternal Song','Heart of Chaos','Mech City','Storm Hero','Flame Blade'],
|
||
'zh-TW': ['龍之傳說','星際遠征','暗影刺客','鋼鐵戰線','幻境迷宮','魔法紀元','賽車狂飆','機甲風暴','荒野求生','深海探險','天空之城','末日堡壘','劍與魔法','像素英雄','霓虹狙擊','煉金術士','勇者鬥惡龍','銀河漂流','地下王國','雷霆戰機','忍者之道','王者棋盤','農場物語','太空殖民','蒸汽朋克','光明使者','暗夜獵手','極速漂移','神秘海域','傳奇戰場','永恆之歌','混沌之心','機械迷城','風暴英雄','烈焰之刃']
|
||
};
|
||
var CATS = {
|
||
'zh-CN': [['动作','⚔'],['角色扮演','★'],['射击','◉'],['冒险','▲'],['策略','♟'],['模拟','◆'],['体育','●'],['益智','♦']],
|
||
'en': [['Action','⚔'],['RPG','★'],['Shooter','◉'],['Adventure','▲'],['Strategy','♟'],['Simulation','◆'],['Sports','●'],['Puzzle','♦']],
|
||
'zh-TW': [['動作','⚔'],['角色扮演','★'],['射擊','◉'],['冒險','▲'],['策略','♟'],['模擬','◆'],['體育','●'],['益智','♦']]
|
||
};
|
||
var CAT_KEYS = ['cat_action','cat_rpg','cat_shooter','cat_adventure','cat_strategy','cat_simulation','cat_sports','cat_puzzle'];
|
||
var TAGS = {
|
||
'zh-CN': ['开放世界','巨龙','史诗','多人联机','硬核','剧情','生存','沙盒','潜行','竞技','合作','Roguelike','解谜','建造','魔法','科幻','末日','机甲','赛车','像素'],
|
||
'en': ['Open World','Dragons','Epic','Multiplayer','Hardcore','Story','Survival','Sandbox','Stealth','Competitive','Co-op','Roguelike','Puzzle','Building','Magic','Sci-Fi','Post-Apocalyptic','Mecha','Racing','Pixel'],
|
||
'zh-TW': ['開放世界','巨龍','史詩','多人連線','硬核','劇情','生存','沙盒','潛行','競技','合作','Roguelike','解謎','建造','魔法','科幻','末日','機甲','賽車','像素']
|
||
};
|
||
var FEATS_POOL = {
|
||
'zh-CN': ['无缝开放世界','实时战斗系统','动态天气系统','多结局剧情','合作多人模式','物理引擎破坏','技能树系统','宠物养成','昼夜循环','随机地下城'],
|
||
'en': ['Seamless Open World','Real-Time Combat','Dynamic Weather','Multiple Endings','Co-op Multiplayer','Physics Destruction','Skill Tree System','Pet Companion','Day/Night Cycle','Random Dungeons'],
|
||
'zh-TW': ['無縫開放世界','即時戰鬥系統','動態天氣系統','多結局劇情','合作多人模式','物理引擎破壞','技能樹系統','寵物養成','晝夜循環','隨機地下城']
|
||
};
|
||
var PUB = {
|
||
'zh-CN': ['像素工作室','银河游戏','雷霆互动','幻影科技','复古发行','独立工坊'],
|
||
'en': ['Pixel Studio','Galaxy Games','Thunder Interactive','Phantom Tech','Retro Publish','Indie Workshop'],
|
||
'zh-TW': ['像素工作室','銀河遊戲','雷霆互動','幻影科技','復古發行','獨立工坊']
|
||
};
|
||
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 lang = currentLang;
|
||
var names = NAMES[lang] || NAMES['zh-CN'];
|
||
var cats = CATS[lang] || CATS['zh-CN'];
|
||
var tags = TAGS[lang] || TAGS['zh-CN'];
|
||
var feats = FEATS_POOL[lang] || FEATS_POOL['zh-CN'];
|
||
var pubs = PUB[lang] || PUB['zh-CN'];
|
||
var descs = [
|
||
t('mock_desc_1'), t('mock_desc_2'), t('mock_desc_3'), t('mock_desc_4')
|
||
];
|
||
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), tagArr=[]; for(var i=0;i<tagN;i++){ var tg=rand(tags); if(tagArr.indexOf(tg)<0)tagArr.push(tg);}
|
||
var feN = randInt(2,3), featArr=[]; for(var j=0;j<feN;j++){ var f=rand(feats); if(featArr.indexOf(f)<0)featArr.push(f);}
|
||
return {
|
||
id:id, name:name, description: rand(descs),
|
||
category_id: ci+1, category_name: t(CAT_KEYS[ci]),
|
||
cover:'', developer:rand(pubs), publisher:rand(pubs),
|
||
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(tagArr), screenshots:'[]', features:JSON.stringify(featArr),
|
||
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 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(){
|
||
var cats = CATS[currentLang] || CATS['zh-CN'];
|
||
return cats.map(function(c,i){
|
||
return {id:i+1,name:c[0],icon:c[1],sort:i+1};
|
||
});
|
||
}
|
||
function renderCats(cats){
|
||
var html = '<button class="cat-btn active" data-cid="" onclick="onCatClick(this,\'\')"><span class="ci">\u2605</span>'+escapeHTML(t('cat_all'))+'</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||'\u25C6')+'</span>'+escapeHTML(c.name)+'</button>';
|
||
});
|
||
document.getElementById('catsWrap').innerHTML = html;
|
||
}
|
||
function renderLibCatSelect(cats){
|
||
var sel = document.getElementById('libCat');
|
||
sel.innerHTML = '<option value="">'+escapeHTML(t('cat_all'))+'</option>';
|
||
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');
|
||
}
|
||
|
||
/* ============================================================
|
||
首页数据
|
||
============================================================ */
|
||
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(t('empty_hot'));
|
||
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)+' \u6B21</span>'
|
||
+ '<span class="score-go">\u25B6 '+t('score_name')+'</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>\u2605 '+(Number(top.rating)||0).toFixed(1)+'</span><span>\u25B6 '+formatNum(top.plays)+'</span><span>\u25C6 '+(top.category_name||'')+'</span></div>'
|
||
+ '</div></div>';
|
||
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)+'\u2605</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(t('empty_new'));
|
||
return;
|
||
}
|
||
var html = '';
|
||
list.forEach(function(g){
|
||
var tags = parseJSON(g.tags,[]);
|
||
var tagHTML = tags.slice(0,3).map(function(tg){return '<span>'+escapeHTML(tg)+'</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||'')+' \u00B7 '+(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(t('empty_no_match'));
|
||
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(tg){return '<span>'+escapeHTML(tg)+'</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)+'\u2605</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">\u25B6 '+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)+')">\u25C0</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">\u00B7\u00B7\u00B7</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">\u00B7\u00B7\u00B7</span>'; html += '<button class="page-btn" onclick="goPage('+pages+')">'+pages+'</button>'; }
|
||
html += '<button class="page-btn" '+(cur>=pages?'disabled':'')+' onclick="goPage('+(cur+1)+')">\u25B6</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(t('empty_no_match')); 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(tg){return '<span>'+escapeHTML(tg)+'</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="screenshot" 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)">'+escapeHTML(t('modal_shots_empty'))+'</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(t('modal_category'), g.category_name||'')
|
||
+ metaItem(t('modal_version'), g.version||'-')
|
||
+ metaItem(t('modal_size'), g.size||'-')
|
||
+ metaItem(t('modal_platform'), g.platform||'-')
|
||
+ metaItem(t('modal_release_date'), g.release_date||'-')
|
||
+ metaItem(t('modal_downloads'), formatNum(g.downloads||g.plays||0))
|
||
+ '</div>'
|
||
+ '</div></div>'
|
||
+ '<div class="modal-desc"><span class="lbl">'+escapeHTML(t('modal_desc_title'))+'</span>'+escapeHTML(g.description||t('modal_desc_empty'))+'</div>'
|
||
+ (features.length?'<div class="modal-features"><div class="lbl">'+escapeHTML(t('modal_features_title'))+'</div><ul>'+featHTML+'</ul></div>':'')
|
||
+ '<div class="modal-shots"><div class="lbl">'+escapeHTML(t('modal_shots_title'))+'</div><div class="shots-row">'+shotHTML+'</div></div>'
|
||
+ '<div class="modal-actions">'
|
||
+ '<button class="pbtn pbtn-yellow" onclick="alert(\''+escapeHTML(t('toast_downloading'))+':'+escapeHTML(g.name||'')+'\')">'+escapeHTML(t('modal_download_btn'))+'</button>'
|
||
+ '<button class="pbtn pbtn-ghost" onclick="alert(\''+escapeHTML(t('toast_fav_added'))+'\')">'+escapeHTML(t('modal_fav_btn'))+'</button>'
|
||
+ '<button class="pbtn pbtn-red" onclick="closeModal()">'+escapeHTML(t('modal_close_btn'))+'</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">\u25A3</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]; }
|
||
});
|
||
|
||
(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);
|
||
})();
|
||
|
||
(function(){
|
||
var el = document.getElementById('heroChar');
|
||
if(!el) return;
|
||
var p = 'var(--ink)';
|
||
var g = 'var(--green)';
|
||
var r = 'var(--red)';
|
||
var s = 'var(--yellow)';
|
||
var br = 'var(--bg-4)';
|
||
var px = 8;
|
||
function row(y, str){
|
||
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';
|
||
})();
|
||
|
||
/* ============================================================
|
||
语言切换回调
|
||
============================================================ */
|
||
onLangChange = function(lang){
|
||
state.lib = { list:[], total:0, page:1, page_size:12, keyword:'', sort:'', category_id:'' };
|
||
document.getElementById('libSearch').value = '';
|
||
document.getElementById('navSearchInput').value = '';
|
||
loadCategories().then(function(){
|
||
loadHome();
|
||
loadLibrary();
|
||
});
|
||
};
|
||
|
||
/* ============================================================
|
||
初始化
|
||
============================================================ */
|
||
function init(){
|
||
applyI18n();
|
||
loadCategories().then(function(){
|
||
loadHome();
|
||
loadLibrary();
|
||
});
|
||
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>
|