update: 2026-03-28 20:59

This commit is contained in:
2026-03-28 20:59:52 +08:00
parent e21d58e603
commit 1c81d4e6ea
611 changed files with 27847 additions and 65061 deletions

View File

@@ -1,96 +0,0 @@
// 游戏结束排行榜展示
const gameStats = {
showStats({ score, playTime }) {
// 将毫秒转为 mm:ss
const formatDuration = (ms) => {
const totalSec = Math.max(0, Math.floor(ms / 1000));
const m = String(Math.floor(totalSec / 60)).padStart(2, '0');
const s = String(totalSec % 60).padStart(2, '0');
return `${m}:${s}`;
};
// 构造排行榜数据(模拟),将当前成绩与 gamedata.js 合并
const todayStr = (() => {
const d = new Date();
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
})();
// 当前玩家信息(可根据实际项目替换为真实用户)
const currentEntry = {
名称: localStorage.getItem('tetris_player_name') || '我',
账号: localStorage.getItem('tetris_player_account') || 'guest@local',
分数: score,
时间: formatDuration(playTime), // 排行榜展示“游戏时长”
isCurrent: true,
};
// 注意:在浏览器中,使用 const 声明的全局变量不会挂载到 window 上
// 因此这里直接使用 playerdata而不是 window.playerdata
const baseData = (typeof playerdata !== 'undefined' && Array.isArray(playerdata)) ? playerdata : [];
// 为基础数据模拟“游戏时长”mm:ss以满足展示需求
const simulateDuration = (scoreVal) => {
const sec = Math.max(30, Math.min(30 * 60, Math.round((Number(scoreVal) || 0) * 1.2)));
return formatDuration(sec * 1000);
};
const merged = [...baseData.map((d) => ({
...d,
// 使用已有分数推导一个模拟时长
时间: simulateDuration(d.分数),
isCurrent: false,
})), currentEntry]
.sort((a, b) => (b.分数 || 0) - (a.分数 || 0));
// 3) 渲染排行榜取前10
const tbody = document.getElementById('leaderboardBody');
tbody.innerHTML = '';
const topN = merged.slice(0, 10);
topN.forEach((item, idx) => {
const tr = document.createElement('tr');
if (item.isCurrent) {
tr.classList.add('current-row');
}
const rankCell = document.createElement('td');
const nameCell = document.createElement('td');
const scoreCell = document.createElement('td');
const timeCell = document.createElement('td');
const rankBadge = document.createElement('span');
rankBadge.className = 'rank-badge';
rankBadge.textContent = String(idx + 1);
rankCell.appendChild(rankBadge);
nameCell.textContent = item.名称 || '未知';
scoreCell.textContent = item.分数 || 0;
timeCell.textContent = item.时间 || formatDuration(playTime);
tr.appendChild(rankCell);
tr.appendChild(nameCell);
tr.appendChild(scoreCell);
tr.appendChild(timeCell);
tbody.appendChild(tr);
});
// 4) 展示排行榜界面
const statsEl = document.getElementById('gameStats');
statsEl.style.display = 'flex';
// 5) 再玩一次按钮
const playAgainBtn = document.getElementById('playAgainBtn');
if (playAgainBtn) {
playAgainBtn.onclick = () => {
statsEl.style.display = 'none';
if (window.game && typeof window.game.restart === 'function') {
window.game.restart();
}
};
}
},
};
// 暴露到全局
window.gameStats = gameStats;

View File

@@ -1,20 +0,0 @@
const playerdata = [
{
"名称":"树萌芽",
"账号":"3205788256@qq.com",
"分数":1232,
"时间":"2025-09-08"
},
{
"名称":"柚大青",
"账号":"2143323382@qq.com",
"分数":132,
"时间":"2025-09-21"
},
{
"名称":"牛马",
"账号":"2973419538@qq.com",
"分数":876,
"时间":"2025-09-25"
}
]

View File

@@ -43,7 +43,6 @@
</div>
</div>
<!-- 手机端触摸控制 -->
<div class="mobile-controls">
<div class="mobile-controls-left">
<button class="control-btn" id="rotateBtn"></button>
@@ -58,35 +57,15 @@
</div>
</div>
<!-- 游戏结束统计界面 -->
<div class="game-stats" id="gameStats">
<div class="stats-content">
<h2>游戏结束排行榜</h2>
<!-- 排行榜 -->
<div class="leaderboard" id="leaderboard">
<div class="leaderboard-title">本局排行榜</div>
<div class="leaderboard-wrap">
<table class="leaderboard-table">
<thead>
<tr>
<th>排名</th>
<th>名称</th>
<th>分数</th>
<th>游戏时长</th>
</tr>
</thead>
<tbody id="leaderboardBody"></tbody>
</table>
</div>
<div class="leaderboard-tip">仅显示前10名“游戏时长”为模拟数据已与您的成绩合并</div>
</div>
<h2>游戏结束</h2>
<div class="end-summary" id="endSummary"></div>
<button class="game-btn" id="playAgainBtn">再玩一次</button>
</div>
</div>
<script src="tetris.js"></script>
<script src="game-controls.js"></script>
<script src="gamedata.js"></script>
<script src="game-stats.js"></script>
</body>
</html>

View File

@@ -319,82 +319,13 @@ body {
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.3);
}
/* 排行榜样式 */
.leaderboard {
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 100%);
.end-summary {
margin: 16px 0 24px;
font-size: 1.05rem;
line-height: 1.8;
}
.end-summary strong {
color: #2e7d32;
border: 1px solid rgba(46, 125, 50, 0.3);
border-radius: 16px;
box-shadow: 0 6px 18px rgba(46, 125, 50, 0.25);
padding: 16px;
margin-bottom: 20px;
}
.leaderboard-title {
font-weight: 700;
font-size: 1.2rem;
margin-bottom: 12px;
background: linear-gradient(135deg, #4caf50 0%, #8bc34a 50%, #cddc39 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.leaderboard-wrap {
max-height: 260px;
overflow: auto;
border-radius: 12px;
}
.leaderboard-table {
width: 100%;
border-collapse: collapse;
}
.leaderboard-table thead tr {
background: linear-gradient(135deg, #66bb6a 0%, #8bc34a 100%);
color: #fff;
}
.leaderboard-table th,
.leaderboard-table td {
text-align: left;
padding: 10px 12px;
border-bottom: 1px solid rgba(46, 125, 50, 0.15);
font-size: 0.95rem;
}
.leaderboard-table tbody tr {
background: linear-gradient(135deg, rgba(46,125,50,0.08) 0%, rgba(46,125,50,0.03) 100%);
transition: background 0.2s ease, transform 0.2s ease;
}
.leaderboard-table tbody tr:hover {
background: linear-gradient(135deg, rgba(46,125,50,0.12) 0%, rgba(46,125,50,0.06) 100%);
transform: translateY(-1px);
}
.rank-badge {
display: inline-block;
min-width: 32px;
text-align: center;
padding: 4px 8px;
border-radius: 12px;
background: linear-gradient(45deg, #66bb6a, #8bc34a);
color: #fff;
font-weight: 700;
}
.current-row {
outline: 2px solid rgba(76, 175, 80, 0.7);
box-shadow: 0 0 0 4px rgba(76, 175, 80, 0.15) inset;
}
.leaderboard-tip {
margin-top: 10px;
font-size: 0.85rem;
color: #388e3c;
opacity: 0.85;
}
/* 响应式设计 */
@@ -457,14 +388,6 @@ body {
width: 95%;
}
.leaderboard-wrap {
max-height: 200px;
}
.leaderboard-table th,
.leaderboard-table td {
padding: 8px 10px;
font-size: 0.9rem;
}
}
@media (max-width: 480px) {
@@ -536,39 +459,3 @@ body {
.pulse {
animation: pulse 2s infinite;
}
/* 摘要卡片 */
.leaderboard-summary {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.summary-item {
background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%);
color: #fff;
padding: 12px;
border-radius: 10px;
border: 1px solid rgba(46, 125, 50, 0.3);
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.2);
}
.summary-label {
display: block;
font-size: 0.9rem;
opacity: 0.9;
}
.summary-value {
display: block;
font-size: 1.3rem;
font-weight: 700;
margin-top: 4px;
}
@media (max-width: 768px) {
.leaderboard-summary {
grid-template-columns: 1fr;
gap: 10px;
}
}

View File

@@ -402,15 +402,28 @@ class TetrisGame {
gameOver() {
this.gameRunning = false;
this.gamePaused = false;
// 显示游戏统计
gameStats.showStats({
score: this.score,
level: this.level,
lines: this.lines,
playTime: Date.now() - this.gameStartTime,
maxCombo: this.maxCombo
});
const playMs = Date.now() - this.gameStartTime;
const sec = Math.floor(playMs / 1000);
const m = String(Math.floor(sec / 60)).padStart(2, '0');
const s = String(sec % 60).padStart(2, '0');
const summary = document.getElementById('endSummary');
if (summary) {
summary.innerHTML =
`<p>得分 <strong>${this.score}</strong> · 等级 <strong>${this.level}</strong></p>` +
`<p>消除 <strong>${this.lines}</strong> 行 · 用时 <strong>${m}:${s}</strong></p>`;
}
const statsEl = document.getElementById('gameStats');
if (statsEl) statsEl.style.display = 'flex';
const btn = document.getElementById('playAgainBtn');
if (btn) {
btn.onclick = () => {
statsEl.style.display = 'none';
if (window.game && typeof window.game.restart === 'function') window.game.restart();
};
}
}
gameLoop(currentTime = 0) {