chore: sync local changes (2026-03-12)
This commit is contained in:
@@ -1,191 +1,191 @@
|
||||
class GameControls {
|
||||
constructor(game) {
|
||||
this.game = game;
|
||||
this.initControls();
|
||||
this.initTouchControls();
|
||||
}
|
||||
|
||||
initControls() {
|
||||
// 重新开始按钮
|
||||
document.getElementById('restartBtn').addEventListener('click', () => {
|
||||
this.game.restart();
|
||||
});
|
||||
|
||||
// 键盘快捷键
|
||||
this.initKeyboardShortcuts();
|
||||
}
|
||||
|
||||
initKeyboardShortcuts() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
switch(e.key) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (this.game.gameOver) {
|
||||
this.game.restart();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTouchControls() {
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
let isDragging = false;
|
||||
let lastTouchX = 0;
|
||||
let lastTouchY = 0;
|
||||
let lastDirectionChange = 0;
|
||||
const directionChangeDelay = 200; // 防止方向变化过快
|
||||
|
||||
// 触摸开始
|
||||
canvas.addEventListener('touchstart', (e) => {
|
||||
isDragging = true;
|
||||
lastTouchX = e.touches[0].clientX;
|
||||
lastTouchY = e.touches[0].clientY;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 拖动过程中实时检测方向
|
||||
canvas.addEventListener('touchmove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - lastDirectionChange < directionChangeDelay) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTouchX = e.touches[0].clientX;
|
||||
const currentTouchY = e.touches[0].clientY;
|
||||
|
||||
const deltaX = currentTouchX - lastTouchX;
|
||||
const deltaY = currentTouchY - lastTouchY;
|
||||
const minDragDistance = 20; // 最小拖动距离
|
||||
|
||||
// 检查是否达到最小拖动距离
|
||||
if (Math.abs(deltaX) > minDragDistance || Math.abs(deltaY) > minDragDistance) {
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
// 水平拖动
|
||||
if (deltaX > 0) {
|
||||
this.game.changeDirection(1, 0); // 向右拖动
|
||||
} else {
|
||||
this.game.changeDirection(-1, 0); // 向左拖动
|
||||
}
|
||||
} else {
|
||||
// 垂直拖动
|
||||
if (deltaY > 0) {
|
||||
this.game.changeDirection(0, 1); // 向下拖动
|
||||
} else {
|
||||
this.game.changeDirection(0, -1); // 向上拖动
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最后触摸位置和方向变化时间
|
||||
lastTouchX = currentTouchX;
|
||||
lastTouchY = currentTouchY;
|
||||
lastDirectionChange = currentTime;
|
||||
|
||||
// 添加触觉反馈
|
||||
this.vibrate(30);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 触摸结束
|
||||
canvas.addEventListener('touchend', (e) => {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 触摸取消
|
||||
canvas.addEventListener('touchcancel', (e) => {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 防止移动端页面滚动
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
if (e.target.closest('.game-container')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// 添加鼠标拖动支持(用于桌面测试)
|
||||
this.initMouseDragControls(canvas);
|
||||
}
|
||||
|
||||
// 鼠标拖动控制(用于桌面测试)
|
||||
initMouseDragControls(canvas) {
|
||||
let isDragging = false;
|
||||
let lastMouseX = 0;
|
||||
let lastMouseY = 0;
|
||||
let lastDirectionChange = 0;
|
||||
const directionChangeDelay = 200;
|
||||
|
||||
canvas.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
lastMouseX = e.clientX;
|
||||
lastMouseY = e.clientY;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - lastDirectionChange < directionChangeDelay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMouseX = e.clientX;
|
||||
const currentMouseY = e.clientY;
|
||||
|
||||
const deltaX = currentMouseX - lastMouseX;
|
||||
const deltaY = currentMouseY - lastMouseY;
|
||||
const minDragDistance = 20;
|
||||
|
||||
if (Math.abs(deltaX) > minDragDistance || Math.abs(deltaY) > minDragDistance) {
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
if (deltaX > 0) {
|
||||
this.game.changeDirection(1, 0); // 向右拖动
|
||||
} else {
|
||||
this.game.changeDirection(-1, 0); // 向左拖动
|
||||
}
|
||||
} else {
|
||||
if (deltaY > 0) {
|
||||
this.game.changeDirection(0, 1); // 向下拖动
|
||||
} else {
|
||||
this.game.changeDirection(0, -1); // 向上拖动
|
||||
}
|
||||
}
|
||||
|
||||
lastMouseX = currentMouseX;
|
||||
lastMouseY = currentMouseY;
|
||||
lastDirectionChange = currentTime;
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseleave', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 震动反馈(移动端)
|
||||
vibrate(duration = 50) {
|
||||
if ('vibrate' in navigator) {
|
||||
navigator.vibrate(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化控制模块
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 等待游戏实例创建后初始化控制
|
||||
setTimeout(() => {
|
||||
new GameControls(game);
|
||||
}, 100);
|
||||
class GameControls {
|
||||
constructor(game) {
|
||||
this.game = game;
|
||||
this.initControls();
|
||||
this.initTouchControls();
|
||||
}
|
||||
|
||||
initControls() {
|
||||
// 重新开始按钮
|
||||
document.getElementById('restartBtn').addEventListener('click', () => {
|
||||
this.game.restart();
|
||||
});
|
||||
|
||||
// 键盘快捷键
|
||||
this.initKeyboardShortcuts();
|
||||
}
|
||||
|
||||
initKeyboardShortcuts() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
switch(e.key) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (this.game.gameOver) {
|
||||
this.game.restart();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTouchControls() {
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
let isDragging = false;
|
||||
let lastTouchX = 0;
|
||||
let lastTouchY = 0;
|
||||
let lastDirectionChange = 0;
|
||||
const directionChangeDelay = 200; // 防止方向变化过快
|
||||
|
||||
// 触摸开始
|
||||
canvas.addEventListener('touchstart', (e) => {
|
||||
isDragging = true;
|
||||
lastTouchX = e.touches[0].clientX;
|
||||
lastTouchY = e.touches[0].clientY;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 拖动过程中实时检测方向
|
||||
canvas.addEventListener('touchmove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - lastDirectionChange < directionChangeDelay) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTouchX = e.touches[0].clientX;
|
||||
const currentTouchY = e.touches[0].clientY;
|
||||
|
||||
const deltaX = currentTouchX - lastTouchX;
|
||||
const deltaY = currentTouchY - lastTouchY;
|
||||
const minDragDistance = 20; // 最小拖动距离
|
||||
|
||||
// 检查是否达到最小拖动距离
|
||||
if (Math.abs(deltaX) > minDragDistance || Math.abs(deltaY) > minDragDistance) {
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
// 水平拖动
|
||||
if (deltaX > 0) {
|
||||
this.game.changeDirection(1, 0); // 向右拖动
|
||||
} else {
|
||||
this.game.changeDirection(-1, 0); // 向左拖动
|
||||
}
|
||||
} else {
|
||||
// 垂直拖动
|
||||
if (deltaY > 0) {
|
||||
this.game.changeDirection(0, 1); // 向下拖动
|
||||
} else {
|
||||
this.game.changeDirection(0, -1); // 向上拖动
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最后触摸位置和方向变化时间
|
||||
lastTouchX = currentTouchX;
|
||||
lastTouchY = currentTouchY;
|
||||
lastDirectionChange = currentTime;
|
||||
|
||||
// 添加触觉反馈
|
||||
this.vibrate(30);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 触摸结束
|
||||
canvas.addEventListener('touchend', (e) => {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 触摸取消
|
||||
canvas.addEventListener('touchcancel', (e) => {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// 防止移动端页面滚动
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
if (e.target.closest('.game-container')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// 添加鼠标拖动支持(用于桌面测试)
|
||||
this.initMouseDragControls(canvas);
|
||||
}
|
||||
|
||||
// 鼠标拖动控制(用于桌面测试)
|
||||
initMouseDragControls(canvas) {
|
||||
let isDragging = false;
|
||||
let lastMouseX = 0;
|
||||
let lastMouseY = 0;
|
||||
let lastDirectionChange = 0;
|
||||
const directionChangeDelay = 200;
|
||||
|
||||
canvas.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
lastMouseX = e.clientX;
|
||||
lastMouseY = e.clientY;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - lastDirectionChange < directionChangeDelay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMouseX = e.clientX;
|
||||
const currentMouseY = e.clientY;
|
||||
|
||||
const deltaX = currentMouseX - lastMouseX;
|
||||
const deltaY = currentMouseY - lastMouseY;
|
||||
const minDragDistance = 20;
|
||||
|
||||
if (Math.abs(deltaX) > minDragDistance || Math.abs(deltaY) > minDragDistance) {
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
if (deltaX > 0) {
|
||||
this.game.changeDirection(1, 0); // 向右拖动
|
||||
} else {
|
||||
this.game.changeDirection(-1, 0); // 向左拖动
|
||||
}
|
||||
} else {
|
||||
if (deltaY > 0) {
|
||||
this.game.changeDirection(0, 1); // 向下拖动
|
||||
} else {
|
||||
this.game.changeDirection(0, -1); // 向上拖动
|
||||
}
|
||||
}
|
||||
|
||||
lastMouseX = currentMouseX;
|
||||
lastMouseY = currentMouseY;
|
||||
lastDirectionChange = currentTime;
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseleave', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 震动反馈(移动端)
|
||||
vibrate(duration = 50) {
|
||||
if ('vibrate' in navigator) {
|
||||
navigator.vibrate(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化控制模块
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 等待游戏实例创建后初始化控制
|
||||
setTimeout(() => {
|
||||
new GameControls(game);
|
||||
}, 100);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,268 +1,268 @@
|
||||
class GameStatistics {
|
||||
constructor() {
|
||||
this.highScores = JSON.parse(localStorage.getItem('snakeHighScores')) || [];
|
||||
this.sessionStats = {
|
||||
gamesPlayed: 0,
|
||||
totalScore: 0,
|
||||
maxLength: 0,
|
||||
maxLevel: 0,
|
||||
totalTime: 0
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// 恢复会话统计(如果存在)
|
||||
const savedSession = localStorage.getItem('snakeSessionStats');
|
||||
if (savedSession) {
|
||||
this.sessionStats = JSON.parse(savedSession);
|
||||
}
|
||||
|
||||
// 监听游戏事件
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// 监听自定义游戏事件
|
||||
document.addEventListener('gameOver', (e) => {
|
||||
this.handleGameOver(e.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('foodEaten', (e) => {
|
||||
this.handleFoodEaten(e.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('levelUp', (e) => {
|
||||
this.handleLevelUp(e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
handleGameOver(gameData) {
|
||||
this.sessionStats.gamesPlayed++;
|
||||
this.sessionStats.totalScore += gameData.score;
|
||||
this.sessionStats.maxLength = Math.max(this.sessionStats.maxLength, gameData.length);
|
||||
this.sessionStats.maxLevel = Math.max(this.sessionStats.maxLevel, gameData.level);
|
||||
this.sessionStats.totalTime += gameData.gameTime;
|
||||
|
||||
// 保存会话统计
|
||||
localStorage.setItem('snakeSessionStats', JSON.stringify(this.sessionStats));
|
||||
|
||||
// 检查是否进入高分榜
|
||||
this.checkHighScore(gameData);
|
||||
|
||||
// 显示统计信息
|
||||
this.displaySessionStats();
|
||||
}
|
||||
|
||||
handleFoodEaten(foodData) {
|
||||
// 可以记录特殊食物统计等
|
||||
console.log('食物被吃掉:', foodData);
|
||||
}
|
||||
|
||||
handleLevelUp(levelData) {
|
||||
// 等级提升统计
|
||||
console.log('等级提升到:', levelData.level);
|
||||
}
|
||||
|
||||
checkHighScore(gameData) {
|
||||
const highScoreEntry = {
|
||||
score: gameData.score,
|
||||
length: gameData.length,
|
||||
level: gameData.level,
|
||||
time: gameData.gameTime,
|
||||
date: new Date().toLocaleDateString(),
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
// 添加到高分榜
|
||||
this.highScores.push(highScoreEntry);
|
||||
|
||||
// 按分数排序(降序)
|
||||
this.highScores.sort((a, b) => b.score - a.score);
|
||||
|
||||
// 只保留前10名
|
||||
this.highScores = this.highScores.slice(0, 10);
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('snakeHighScores', JSON.stringify(this.highScores));
|
||||
}
|
||||
|
||||
displaySessionStats() {
|
||||
const statsElement = document.createElement('div');
|
||||
statsElement.className = 'session-stats';
|
||||
statsElement.innerHTML = `
|
||||
<h3>本次会话统计</h3>
|
||||
<p>游戏次数: ${this.sessionStats.gamesPlayed}</p>
|
||||
<p>总得分: ${this.sessionStats.totalScore}</p>
|
||||
<p>最高长度: ${this.sessionStats.maxLength}</p>
|
||||
<p>最高等级: ${this.sessionStats.maxLevel}</p>
|
||||
<p>总游戏时间: ${Math.floor(this.sessionStats.totalTime / 60)}分钟</p>
|
||||
<p>平均得分: ${Math.round(this.sessionStats.totalScore / this.sessionStats.gamesPlayed)}</p>
|
||||
`;
|
||||
|
||||
// 添加到游戏结束模态框
|
||||
const statsContainer = document.querySelector('.stats');
|
||||
if (statsContainer && !document.querySelector('.session-stats')) {
|
||||
statsContainer.appendChild(statsElement);
|
||||
}
|
||||
}
|
||||
|
||||
displayHighScores() {
|
||||
const highScoresElement = document.createElement('div');
|
||||
highScoresElement.className = 'high-scores';
|
||||
|
||||
if (this.highScores.length > 0) {
|
||||
highScoresElement.innerHTML = `
|
||||
<h3>🏆 高分榜</h3>
|
||||
${this.highScores.map((score, index) => `
|
||||
<div class="score-item ${index === 0 ? 'first-place' : ''}">
|
||||
<span class="rank">${index + 1}.</span>
|
||||
<span class="score">${score.score}分</span>
|
||||
<span class="length">长度:${score.length}</span>
|
||||
<span class="date">${score.date}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
`;
|
||||
} else {
|
||||
highScoresElement.innerHTML = '<p>暂无高分记录</p>';
|
||||
}
|
||||
|
||||
// 添加到游戏结束模态框
|
||||
const modalContent = document.querySelector('.modal-content');
|
||||
if (modalContent && !document.querySelector('.high-scores')) {
|
||||
modalContent.appendChild(highScoresElement);
|
||||
}
|
||||
}
|
||||
|
||||
getAchievements(gameData) {
|
||||
const achievements = [];
|
||||
|
||||
if (gameData.score >= 100) achievements.push('百分达人');
|
||||
if (gameData.length >= 20) achievements.push('长蛇之王');
|
||||
if (gameData.level >= 5) achievements.push('等级大师');
|
||||
if (gameData.gameTime >= 300) achievements.push('持久战将');
|
||||
if (gameData.score >= 50 && gameData.gameTime <= 60) achievements.push('速通高手');
|
||||
|
||||
return achievements;
|
||||
}
|
||||
|
||||
// 工具方法:格式化时间
|
||||
formatTime(seconds) {
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${mins}分${secs}秒`;
|
||||
}
|
||||
|
||||
// 清除统计
|
||||
clearStatistics() {
|
||||
this.highScores = [];
|
||||
this.sessionStats = {
|
||||
gamesPlayed: 0,
|
||||
totalScore: 0,
|
||||
maxLength: 0,
|
||||
maxLevel: 0,
|
||||
totalTime: 0
|
||||
};
|
||||
|
||||
localStorage.removeItem('snakeHighScores');
|
||||
localStorage.removeItem('snakeSessionStats');
|
||||
|
||||
console.log('统计信息已清除');
|
||||
}
|
||||
}
|
||||
|
||||
// 原游戏结束界面已移除,保留统计模块以便响应 'gameOver' 事件
|
||||
|
||||
// 初始化统计模块
|
||||
let gameStats;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
gameStats = new GameStatistics();
|
||||
});
|
||||
|
||||
// 添加CSS样式
|
||||
const statsStyles = `
|
||||
.session-stats {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #d4a76a;
|
||||
}
|
||||
|
||||
.session-stats h3 {
|
||||
color: #8b4513;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.session-stats p {
|
||||
margin: 5px 0;
|
||||
color: #654321;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.high-scores {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #fff1eb 0%, #ace0f9 100%);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #4682b4;
|
||||
}
|
||||
|
||||
.high-scores h3 {
|
||||
color: #2c5282;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.score-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #cbd5e0;
|
||||
}
|
||||
|
||||
.score-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.score-item.first-place {
|
||||
background: linear-gradient(135deg, #fceabb 0%, #f8b500 100%);
|
||||
border-radius: 5px;
|
||||
padding: 8px;
|
||||
margin: -8px -8px 8px -8px;
|
||||
}
|
||||
|
||||
.rank {
|
||||
font-weight: bold;
|
||||
color: #2d3748;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-weight: bold;
|
||||
color: #e53e3e;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.length {
|
||||
color: #4a5568;
|
||||
font-size: 0.8rem;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #718096;
|
||||
font-size: 0.7rem;
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
|
||||
// 注入样式
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = statsStyles;
|
||||
class GameStatistics {
|
||||
constructor() {
|
||||
this.highScores = JSON.parse(localStorage.getItem('snakeHighScores')) || [];
|
||||
this.sessionStats = {
|
||||
gamesPlayed: 0,
|
||||
totalScore: 0,
|
||||
maxLength: 0,
|
||||
maxLevel: 0,
|
||||
totalTime: 0
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// 恢复会话统计(如果存在)
|
||||
const savedSession = localStorage.getItem('snakeSessionStats');
|
||||
if (savedSession) {
|
||||
this.sessionStats = JSON.parse(savedSession);
|
||||
}
|
||||
|
||||
// 监听游戏事件
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// 监听自定义游戏事件
|
||||
document.addEventListener('gameOver', (e) => {
|
||||
this.handleGameOver(e.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('foodEaten', (e) => {
|
||||
this.handleFoodEaten(e.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('levelUp', (e) => {
|
||||
this.handleLevelUp(e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
handleGameOver(gameData) {
|
||||
this.sessionStats.gamesPlayed++;
|
||||
this.sessionStats.totalScore += gameData.score;
|
||||
this.sessionStats.maxLength = Math.max(this.sessionStats.maxLength, gameData.length);
|
||||
this.sessionStats.maxLevel = Math.max(this.sessionStats.maxLevel, gameData.level);
|
||||
this.sessionStats.totalTime += gameData.gameTime;
|
||||
|
||||
// 保存会话统计
|
||||
localStorage.setItem('snakeSessionStats', JSON.stringify(this.sessionStats));
|
||||
|
||||
// 检查是否进入高分榜
|
||||
this.checkHighScore(gameData);
|
||||
|
||||
// 显示统计信息
|
||||
this.displaySessionStats();
|
||||
}
|
||||
|
||||
handleFoodEaten(foodData) {
|
||||
// 可以记录特殊食物统计等
|
||||
console.log('食物被吃掉:', foodData);
|
||||
}
|
||||
|
||||
handleLevelUp(levelData) {
|
||||
// 等级提升统计
|
||||
console.log('等级提升到:', levelData.level);
|
||||
}
|
||||
|
||||
checkHighScore(gameData) {
|
||||
const highScoreEntry = {
|
||||
score: gameData.score,
|
||||
length: gameData.length,
|
||||
level: gameData.level,
|
||||
time: gameData.gameTime,
|
||||
date: new Date().toLocaleDateString(),
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
// 添加到高分榜
|
||||
this.highScores.push(highScoreEntry);
|
||||
|
||||
// 按分数排序(降序)
|
||||
this.highScores.sort((a, b) => b.score - a.score);
|
||||
|
||||
// 只保留前10名
|
||||
this.highScores = this.highScores.slice(0, 10);
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('snakeHighScores', JSON.stringify(this.highScores));
|
||||
}
|
||||
|
||||
displaySessionStats() {
|
||||
const statsElement = document.createElement('div');
|
||||
statsElement.className = 'session-stats';
|
||||
statsElement.innerHTML = `
|
||||
<h3>本次会话统计</h3>
|
||||
<p>游戏次数: ${this.sessionStats.gamesPlayed}</p>
|
||||
<p>总得分: ${this.sessionStats.totalScore}</p>
|
||||
<p>最高长度: ${this.sessionStats.maxLength}</p>
|
||||
<p>最高等级: ${this.sessionStats.maxLevel}</p>
|
||||
<p>总游戏时间: ${Math.floor(this.sessionStats.totalTime / 60)}分钟</p>
|
||||
<p>平均得分: ${Math.round(this.sessionStats.totalScore / this.sessionStats.gamesPlayed)}</p>
|
||||
`;
|
||||
|
||||
// 添加到游戏结束模态框
|
||||
const statsContainer = document.querySelector('.stats');
|
||||
if (statsContainer && !document.querySelector('.session-stats')) {
|
||||
statsContainer.appendChild(statsElement);
|
||||
}
|
||||
}
|
||||
|
||||
displayHighScores() {
|
||||
const highScoresElement = document.createElement('div');
|
||||
highScoresElement.className = 'high-scores';
|
||||
|
||||
if (this.highScores.length > 0) {
|
||||
highScoresElement.innerHTML = `
|
||||
<h3>🏆 高分榜</h3>
|
||||
${this.highScores.map((score, index) => `
|
||||
<div class="score-item ${index === 0 ? 'first-place' : ''}">
|
||||
<span class="rank">${index + 1}.</span>
|
||||
<span class="score">${score.score}分</span>
|
||||
<span class="length">长度:${score.length}</span>
|
||||
<span class="date">${score.date}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
`;
|
||||
} else {
|
||||
highScoresElement.innerHTML = '<p>暂无高分记录</p>';
|
||||
}
|
||||
|
||||
// 添加到游戏结束模态框
|
||||
const modalContent = document.querySelector('.modal-content');
|
||||
if (modalContent && !document.querySelector('.high-scores')) {
|
||||
modalContent.appendChild(highScoresElement);
|
||||
}
|
||||
}
|
||||
|
||||
getAchievements(gameData) {
|
||||
const achievements = [];
|
||||
|
||||
if (gameData.score >= 100) achievements.push('百分达人');
|
||||
if (gameData.length >= 20) achievements.push('长蛇之王');
|
||||
if (gameData.level >= 5) achievements.push('等级大师');
|
||||
if (gameData.gameTime >= 300) achievements.push('持久战将');
|
||||
if (gameData.score >= 50 && gameData.gameTime <= 60) achievements.push('速通高手');
|
||||
|
||||
return achievements;
|
||||
}
|
||||
|
||||
// 工具方法:格式化时间
|
||||
formatTime(seconds) {
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${mins}分${secs}秒`;
|
||||
}
|
||||
|
||||
// 清除统计
|
||||
clearStatistics() {
|
||||
this.highScores = [];
|
||||
this.sessionStats = {
|
||||
gamesPlayed: 0,
|
||||
totalScore: 0,
|
||||
maxLength: 0,
|
||||
maxLevel: 0,
|
||||
totalTime: 0
|
||||
};
|
||||
|
||||
localStorage.removeItem('snakeHighScores');
|
||||
localStorage.removeItem('snakeSessionStats');
|
||||
|
||||
console.log('统计信息已清除');
|
||||
}
|
||||
}
|
||||
|
||||
// 原游戏结束界面已移除,保留统计模块以便响应 'gameOver' 事件
|
||||
|
||||
// 初始化统计模块
|
||||
let gameStats;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
gameStats = new GameStatistics();
|
||||
});
|
||||
|
||||
// 添加CSS样式
|
||||
const statsStyles = `
|
||||
.session-stats {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #d4a76a;
|
||||
}
|
||||
|
||||
.session-stats h3 {
|
||||
color: #8b4513;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.session-stats p {
|
||||
margin: 5px 0;
|
||||
color: #654321;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.high-scores {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #fff1eb 0%, #ace0f9 100%);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #4682b4;
|
||||
}
|
||||
|
||||
.high-scores h3 {
|
||||
color: #2c5282;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.score-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #cbd5e0;
|
||||
}
|
||||
|
||||
.score-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.score-item.first-place {
|
||||
background: linear-gradient(135deg, #fceabb 0%, #f8b500 100%);
|
||||
border-radius: 5px;
|
||||
padding: 8px;
|
||||
margin: -8px -8px 8px -8px;
|
||||
}
|
||||
|
||||
.rank {
|
||||
font-weight: bold;
|
||||
color: #2d3748;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-weight: bold;
|
||||
color: #e53e3e;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.length {
|
||||
color: #4a5568;
|
||||
font-size: 0.8rem;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #718096;
|
||||
font-size: 0.7rem;
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
|
||||
// 注入样式
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = statsStyles;
|
||||
document.head.appendChild(styleSheet);
|
||||
@@ -1,50 +1,50 @@
|
||||
const playerdata = [
|
||||
{
|
||||
"名称":"树萌芽",
|
||||
"账号":"3205788256@qq.com",
|
||||
"分数":1568,
|
||||
"时间":"2025-09-08"
|
||||
},
|
||||
{
|
||||
"名称":"风行者",
|
||||
"账号":"4456723190@qq.com",
|
||||
"分数":1987,
|
||||
"时间":"2025-09-30"
|
||||
},
|
||||
{
|
||||
"名称":"月光骑士",
|
||||
"账号":"5832197462@qq.com",
|
||||
"分数":876,
|
||||
"时间":"2025-10-02"
|
||||
},
|
||||
{
|
||||
"名称":"星河",
|
||||
"账号":"6724981532@qq.com",
|
||||
"分数":1345,
|
||||
"时间":"2025-10-05"
|
||||
},
|
||||
{
|
||||
"名称":"雷霆",
|
||||
"账号":"7891234567@qq.com",
|
||||
"分数":2105,
|
||||
"时间":"2025-10-08"
|
||||
},
|
||||
{
|
||||
"名称":"火焰猫",
|
||||
"账号":"8912345678@qq.com",
|
||||
"分数":654,
|
||||
"时间":"2025-10-10"
|
||||
},
|
||||
{
|
||||
"名称":"冰雪女王",
|
||||
"账号":"9123456789@qq.com",
|
||||
"分数":1789,
|
||||
"时间":"2025-10-12"
|
||||
},
|
||||
{
|
||||
"名称":"😊",
|
||||
"账号":"1125234890@qq.com",
|
||||
"分数":1432,
|
||||
"时间":"2025-10-15"
|
||||
}
|
||||
const playerdata = [
|
||||
{
|
||||
"名称":"树萌芽",
|
||||
"账号":"3205788256@qq.com",
|
||||
"分数":1568,
|
||||
"时间":"2025-09-08"
|
||||
},
|
||||
{
|
||||
"名称":"风行者",
|
||||
"账号":"4456723190@qq.com",
|
||||
"分数":1987,
|
||||
"时间":"2025-09-30"
|
||||
},
|
||||
{
|
||||
"名称":"月光骑士",
|
||||
"账号":"5832197462@qq.com",
|
||||
"分数":876,
|
||||
"时间":"2025-10-02"
|
||||
},
|
||||
{
|
||||
"名称":"星河",
|
||||
"账号":"6724981532@qq.com",
|
||||
"分数":1345,
|
||||
"时间":"2025-10-05"
|
||||
},
|
||||
{
|
||||
"名称":"雷霆",
|
||||
"账号":"7891234567@qq.com",
|
||||
"分数":2105,
|
||||
"时间":"2025-10-08"
|
||||
},
|
||||
{
|
||||
"名称":"火焰猫",
|
||||
"账号":"8912345678@qq.com",
|
||||
"分数":654,
|
||||
"时间":"2025-10-10"
|
||||
},
|
||||
{
|
||||
"名称":"冰雪女王",
|
||||
"账号":"9123456789@qq.com",
|
||||
"分数":1789,
|
||||
"时间":"2025-10-12"
|
||||
},
|
||||
{
|
||||
"名称":"😊",
|
||||
"账号":"1125234890@qq.com",
|
||||
"分数":1432,
|
||||
"时间":"2025-10-15"
|
||||
}
|
||||
]
|
||||
@@ -1,332 +1,332 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc8 50%, #f1f8e9 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
touch-action: manipulation;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 100%);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 20px 40px rgba(46, 125, 50, 0.3);
|
||||
max-width: 400px;
|
||||
width: 95%;
|
||||
margin: 20px auto;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.game-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
color: #1b5e20;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.score-board {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%);
|
||||
padding: 12px;
|
||||
border-radius: 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.score-board span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-canvas-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
border: 3px solid #2e7d32;
|
||||
border-radius: 10px;
|
||||
background: #1b5e20;
|
||||
box-shadow: 0 8px 16px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
.game-controls {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin: 0 10px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
box-shadow: 0 6px 12px rgba(46, 125, 50, 0.4);
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
box-shadow: 0 2px 4px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
#pauseBtn {
|
||||
background: linear-gradient(135deg, #81c784 0%, #a5d6a7 100%);
|
||||
color: #1b5e20;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.game-instructions {
|
||||
text-align: center;
|
||||
color: #2e7d32;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(46, 125, 50, 0.6);
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 100%);
|
||||
padding: 30px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 20px 40px rgba(46, 125, 50, 0.4);
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.modal-content h2 {
|
||||
color: #1b5e20;
|
||||
margin-bottom: 20px;
|
||||
font-size: 2rem;
|
||||
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.stats p {
|
||||
margin: 10px 0;
|
||||
font-size: 1.1rem;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.stats span {
|
||||
font-weight: bold;
|
||||
color: #1b5e20;
|
||||
}
|
||||
|
||||
.restart-btn {
|
||||
background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: 25px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin-top: 20px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.restart-btn:hover {
|
||||
box-shadow: 0 8px 16px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
/* 排行榜样式 */
|
||||
.leaderboard {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.leaderboard-summary {
|
||||
margin: 10px 0 15px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 12px;
|
||||
color: #1b5e20;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.leaderboard-summary p {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.leaderboard h3 {
|
||||
color: #1b5e20;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leaderboard-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.leaderboard-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 10px;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.leaderboard-item.current-player {
|
||||
background: linear-gradient(135deg, #ffeb3b 0%, #fff176 100%);
|
||||
font-weight: bold;
|
||||
border: 2px solid #f57f17;
|
||||
}
|
||||
|
||||
.leaderboard-item .rank {
|
||||
font-weight: bold;
|
||||
min-width: 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-name {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-score {
|
||||
font-weight: bold;
|
||||
color: #1b5e20;
|
||||
min-width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-time {
|
||||
color: #4a5568;
|
||||
font-size: 0.8rem;
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 手机端优化 */
|
||||
@media (max-width: 768px) {
|
||||
.game-container {
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.score-board {
|
||||
font-size: 0.9rem;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 1.3rem;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
#pauseBtn {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
width: 280px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕优化 */
|
||||
@media (max-width: 480px) {
|
||||
.game-header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.score-board {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 1.1rem;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
width: 250px;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-content h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 防止文本选择 */
|
||||
.control-btn, .restart-btn {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc8 50%, #f1f8e9 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
touch-action: manipulation;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 100%);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 20px 40px rgba(46, 125, 50, 0.3);
|
||||
max-width: 400px;
|
||||
width: 95%;
|
||||
margin: 20px auto;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.game-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
color: #1b5e20;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.score-board {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%);
|
||||
padding: 12px;
|
||||
border-radius: 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.score-board span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-canvas-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
border: 3px solid #2e7d32;
|
||||
border-radius: 10px;
|
||||
background: #1b5e20;
|
||||
box-shadow: 0 8px 16px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
.game-controls {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin: 0 10px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
box-shadow: 0 6px 12px rgba(46, 125, 50, 0.4);
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
box-shadow: 0 2px 4px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
#pauseBtn {
|
||||
background: linear-gradient(135deg, #81c784 0%, #a5d6a7 100%);
|
||||
color: #1b5e20;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.game-instructions {
|
||||
text-align: center;
|
||||
color: #2e7d32;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(46, 125, 50, 0.6);
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 100%);
|
||||
padding: 30px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 20px 40px rgba(46, 125, 50, 0.4);
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.modal-content h2 {
|
||||
color: #1b5e20;
|
||||
margin-bottom: 20px;
|
||||
font-size: 2rem;
|
||||
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.stats p {
|
||||
margin: 10px 0;
|
||||
font-size: 1.1rem;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.stats span {
|
||||
font-weight: bold;
|
||||
color: #1b5e20;
|
||||
}
|
||||
|
||||
.restart-btn {
|
||||
background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: 25px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin-top: 20px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.restart-btn:hover {
|
||||
box-shadow: 0 8px 16px rgba(46, 125, 50, 0.3);
|
||||
}
|
||||
|
||||
/* 排行榜样式 */
|
||||
.leaderboard {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.leaderboard-summary {
|
||||
margin: 10px 0 15px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 12px;
|
||||
color: #1b5e20;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(46, 125, 50, 0.2);
|
||||
}
|
||||
|
||||
.leaderboard-summary p {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.leaderboard h3 {
|
||||
color: #1b5e20;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leaderboard-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.leaderboard-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 10px;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.leaderboard-item.current-player {
|
||||
background: linear-gradient(135deg, #ffeb3b 0%, #fff176 100%);
|
||||
font-weight: bold;
|
||||
border: 2px solid #f57f17;
|
||||
}
|
||||
|
||||
.leaderboard-item .rank {
|
||||
font-weight: bold;
|
||||
min-width: 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-name {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-score {
|
||||
font-weight: bold;
|
||||
color: #1b5e20;
|
||||
min-width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.leaderboard-item .player-time {
|
||||
color: #4a5568;
|
||||
font-size: 0.8rem;
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 手机端优化 */
|
||||
@media (max-width: 768px) {
|
||||
.game-container {
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.score-board {
|
||||
font-size: 0.9rem;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 1.3rem;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
#pauseBtn {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
width: 280px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕优化 */
|
||||
@media (max-width: 480px) {
|
||||
.game-header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.score-board {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 1.1rem;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
width: 250px;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-content h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 防止文本选择 */
|
||||
.control-btn, .restart-btn {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
/* 动画效果已删除 */
|
||||
Reference in New Issue
Block a user