91148fe064
1. 新增物理弹球游戏全套代码,包含物理引擎、规则、渲染、游戏逻辑、存档、音效、UI 2. 新增物理弹球相关调试与单元测试脚本 3. 优化私有剪贴上传功能:增加200MB上传限制与错误提示 4. 修复复古弹球桌的光滑面防粘附与休眠逻辑
174 lines
9.5 KiB
HTML
174 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>物理弹球 · 思想实验室</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||
html, body { height: 100%; background: #000; overflow: hidden; font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||
#pb-wrap { position: relative; height: 100%; max-width: 100vw; aspect-ratio: 420 / 880; margin: 0 auto; touch-action: none; user-select: none; }
|
||
@media (min-aspect-ratio: 420/880) { #pb-wrap { height: 100%; } }
|
||
#pb-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
||
.hidden { display: none !important; }
|
||
button { font-family: inherit; cursor: pointer; border: none; }
|
||
|
||
/* ---------- HUD ---------- */
|
||
#pb-hud { position: absolute; top: 0; left: 0; right: 0; padding: 10px 12px; display: flex; align-items: flex-start; pointer-events: none; }
|
||
#pb-pause-btn { pointer-events: auto; background: none; color: #ddd; font-size: 20px; width: 34px; height: 34px; border-radius: 50%; border: 2px solid #888; line-height: 1; }
|
||
#pb-time { margin-left: 10px; margin-top: 6px; color: #999; font-size: 13px; }
|
||
#pb-balls { margin-left: auto; color: #c89458; font-size: 14px; margin-top: 8px; }
|
||
#pb-big { color: #ffd83d; font-size: 12px; border: 1px solid #ffd83d; border-radius: 10px; padding: 2px 8px; margin-left: 10px; margin-top: 6px; }
|
||
#pb-score { margin-left: 12px; color: #fff; font-size: 26px; font-weight: 600; }
|
||
#pb-score.pop { animation: pb-pop 0.18s ease; }
|
||
@keyframes pb-pop { 0% { transform: scale(1.3); color: #ffd83d; } 100% { transform: scale(1); } }
|
||
|
||
/* ---------- 主菜单(覆盖层不挡拖拽,仅按钮可点) ---------- */
|
||
#pb-menu { position: absolute; inset: 0; pointer-events: none; }
|
||
#pb-menu button, #pb-daily { pointer-events: auto; }
|
||
#pb-gear { position: absolute; top: 10px; left: 10px; width: 34px; height: 34px; border-radius: 50%; background: #f7c81a; color: #4a3600; font-size: 18px; }
|
||
#pb-help { position: absolute; top: 10px; right: 10px; width: 34px; height: 34px; border-radius: 50%; background: #333; color: #ddd; font-size: 17px; font-weight: 700; }
|
||
#pb-best { position: absolute; top: 52px; right: 14px; color: #f7c81a; font-size: 15px; }
|
||
#pb-logo { position: absolute; top: 16%; left: 50%; transform: translateX(-50%); }
|
||
#pb-title { position: absolute; top: calc(16% + 130px); left: 0; right: 0; text-align: center; color: #fff; font-size: 22px; letter-spacing: 14px; text-indent: 14px; }
|
||
#pb-side-balls { position: absolute; left: 0; top: 52%; background: #ff2f7b; color: #fff; border-radius: 0 22px 22px 0; padding: 8px 16px 8px 10px; font-size: 14px; }
|
||
#pb-side-records { position: absolute; left: 0; top: calc(52% + 52px); background: #7b2fff; color: #fff; border-radius: 0 22px 22px 0; padding: 8px 16px 8px 10px; font-size: 14px; }
|
||
#pb-daily { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 86px; height: 86px; border-radius: 50%; overflow: hidden; background: linear-gradient(#fff 0 50%, #f7c81a 50% 100%); text-align: center; font-size: 15px; font-weight: 700; color: #222; line-height: 1.5; padding-top: 14px; box-shadow: 0 0 0 3px rgba(255,255,255,0.15); }
|
||
#pb-hint-text { position: absolute; bottom: 7%; left: 0; right: 0; text-align: center; color: #ddd; font-size: 15px; }
|
||
#pb-hint-line { position: absolute; bottom: 12%; left: 18%; right: 18%; height: 2px; background: #fff; }
|
||
|
||
/* ---------- 覆盖层通用 ---------- */
|
||
.overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.88); display: flex; flex-direction: column; align-items: center; z-index: 5; }
|
||
.ov-btn-blue { background: #1478e6; color: #fff; border-radius: 26px; padding: 13px 44px; font-size: 16px; margin-top: 14px; display: flex; align-items: center; gap: 10px; }
|
||
.ov-btn-pink { background: #ff2f7b; color: #fff; border-radius: 26px; padding: 13px 40px; font-size: 16px; margin-top: 30px; }
|
||
|
||
/* ---------- 暂停 ---------- */
|
||
#pb-pause h2 { color: #fff; font-size: 26px; letter-spacing: 12px; margin-top: 24%; }
|
||
|
||
/* ---------- 结束 ---------- */
|
||
#pb-over h2 { color: #fff; font-size: 24px; margin-top: 22%; }
|
||
#pb-over-score { color: #f7c81a; font-size: 52px; font-weight: 700; margin-top: 12px; }
|
||
#pb-over-time { color: #999; margin-top: 6px; }
|
||
#pb-over-new { color: #ff2f7b; font-weight: 700; margin-top: 8px; }
|
||
#pb-over-best { color: #f7c81a; margin-top: 4px; font-size: 14px; }
|
||
|
||
/* ---------- 记录 ---------- */
|
||
#pb-records { padding-top: 8%; }
|
||
#pb-records h2 { color: #fff; font-size: 20px; margin-bottom: 14px; }
|
||
#pb-records-list { width: 100%; flex: 1; overflow-y: auto; }
|
||
.pb-rec-row { display: flex; align-items: center; padding: 12px 16px; background: #383838; }
|
||
.pb-rec-row.alt { background: #262626; }
|
||
.pb-rec-rank { width: 40px; font-size: 18px; font-weight: 700; }
|
||
.pb-rec-date { font-size: 13px; flex: 1; }
|
||
.pb-rec-right { text-align: right; }
|
||
.pb-rec-right b { display: block; font-size: 20px; }
|
||
.pb-rec-right i { font-style: normal; font-size: 12px; }
|
||
.pb-rec-empty { color: #888; text-align: center; margin-top: 40px; }
|
||
#pb-records-close { margin: 12px 0 20px; }
|
||
|
||
/* ---------- 指南 ---------- */
|
||
#pb-guide { padding-top: 16%; }
|
||
#pb-guide h2 { color: #fff; font-size: 22px; margin-bottom: 18px; }
|
||
#pb-guide-demo { background: #000; }
|
||
#pb-guide p { color: #eee; font-size: 14px; margin-top: 14px; }
|
||
#pb-guide-ok { margin-top: 26px; }
|
||
|
||
/* ---------- 设置 ---------- */
|
||
#pb-settings { padding-top: 30%; }
|
||
#pb-settings h2 { color: #fff; font-size: 20px; margin-bottom: 24px; }
|
||
.pb-set-row { color: #ddd; font-size: 16px; display: flex; align-items: center; gap: 12px; }
|
||
.pb-set-row input[type="checkbox"] { width: 20px; height: 20px; }
|
||
.pb-set-slider { margin-top: 22px; }
|
||
.pb-set-slider input[type="range"] { width: 170px; accent-color: #f7c81a; }
|
||
.pb-set-slider b { color: #f7c81a; min-width: 30px; text-align: right; }
|
||
.pb-set-hint { color: #888; font-size: 12px; margin-top: 10px; }
|
||
|
||
/* ---------- toast ---------- */
|
||
#pb-toast { position: absolute; left: 50%; bottom: 16%; transform: translateX(-50%); background: rgba(40,40,40,0.95); color: #eee; font-size: 13px; padding: 9px 18px; border-radius: 20px; opacity: 0; transition: opacity 0.25s; pointer-events: none; z-index: 9; white-space: nowrap; }
|
||
#pb-toast.show { opacity: 1; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="pb-wrap">
|
||
<canvas id="pb-canvas" width="420" height="880"></canvas>
|
||
|
||
<div id="pb-hud" class="hidden">
|
||
<button id="pb-pause-btn" title="暂停">❚</button>
|
||
<div id="pb-time">时间 00:00</div>
|
||
<div id="pb-balls">🥎 0</div>
|
||
<div id="pb-big" class="hidden">大球 ×2</div>
|
||
<div id="pb-score">0</div>
|
||
</div>
|
||
|
||
<div id="pb-menu">
|
||
<button id="pb-gear" title="设置">⚙</button>
|
||
<button id="pb-help" title="游戏指南">?</button>
|
||
<div id="pb-best">最佳: 0</div>
|
||
<canvas id="pb-logo" width="220" height="120"></canvas>
|
||
<div id="pb-title">物理弹球</div>
|
||
<button id="pb-side-balls">🥎 球 <b id="pb-ball-count">1</b></button>
|
||
<button id="pb-side-records">📋 记录</button>
|
||
<button id="pb-daily">今天<br>+3 🥎</button>
|
||
<div id="pb-hint-line"></div>
|
||
<div id="pb-hint-text">拖动瞄准</div>
|
||
</div>
|
||
|
||
<div id="pb-pause" class="overlay hidden">
|
||
<h2>暂 停</h2>
|
||
<button id="pb-share" class="ov-btn-blue">📋 复制成绩</button>
|
||
<button id="pb-restart" class="ov-btn-blue">⟳ 重新开始</button>
|
||
<button id="pb-resume" class="ov-btn-pink">▶ 继续游戏</button>
|
||
</div>
|
||
|
||
<div id="pb-over" class="overlay hidden">
|
||
<h2>本局结束</h2>
|
||
<div id="pb-over-score">0</div>
|
||
<div id="pb-over-time">用时 00:00</div>
|
||
<div id="pb-over-new">★ 新纪录 ★</div>
|
||
<div id="pb-over-best">最佳: 0</div>
|
||
<button id="pb-again" class="ov-btn-pink">再来一局</button>
|
||
<button id="pb-to-menu" class="ov-btn-blue">返回主菜单</button>
|
||
</div>
|
||
|
||
<div id="pb-records" class="overlay hidden">
|
||
<h2>记录</h2>
|
||
<div id="pb-records-list"></div>
|
||
<button id="pb-records-close" class="ov-btn-blue">返回</button>
|
||
</div>
|
||
|
||
<div id="pb-guide" class="overlay hidden">
|
||
<h2>游戏指南</h2>
|
||
<canvas id="pb-guide-demo" width="220" height="160"></canvas>
|
||
<p>1. 水平移动手指,瞄准数字块。</p>
|
||
<p>2. 击中数字块得分(小球+1,大球+2)。</p>
|
||
<p>3. 数字块到达顶端,游戏结束。</p>
|
||
<p>4. 留意出现的道具助你一臂之力。</p>
|
||
<p>5. 球陷入夹角时会全力研磨接触的数字块,快速开出逃逸空间。</p>
|
||
<button id="pb-guide-ok" class="ov-btn-blue">知道了!</button>
|
||
</div>
|
||
|
||
<div id="pb-settings" class="overlay hidden">
|
||
<h2>设置</h2>
|
||
<label class="pb-set-row"><input type="checkbox" id="pb-sound-toggle" checked> 音效</label>
|
||
<div class="pb-set-row pb-set-slider">
|
||
<span>逃逸强度</span>
|
||
<input type="range" id="pb-escape-slider" min="0" max="100" step="5">
|
||
<b id="pb-escape-val">50</b>
|
||
</div>
|
||
<div class="pb-set-hint">越高:卡住的球挣脱越猛,越简单</div>
|
||
<button id="pb-settings-close" class="ov-btn-blue">返回</button>
|
||
</div>
|
||
|
||
<div id="pb-toast"></div>
|
||
</div>
|
||
|
||
<script src="pb_physics.js"></script>
|
||
<script src="pb_rules.js"></script>
|
||
<script src="pb_storage.js"></script>
|
||
<script src="pb_audio.js"></script>
|
||
<script src="pb_render.js"></script>
|
||
<script src="pb_game.js"></script>
|
||
<script src="pb_ui.js"></script>
|
||
</body>
|
||
</html>
|