项目概述
使用原生JavaScript实现一个乘法答题游戏,随机生成乘法题目,判断答案正误并记录分数,通过localStorage实现分数持久化存储。
核心功能需求
- 随机题目生成:动态生成1-10之间的乘法题
- 答题交互:输入答案并提交,支持回车提交
- 正误判定:判断答案正确性并给出视觉反馈
- 积分系统:答对加分,记录当前分数和历史最高分
- 分数持久化:使用localStorage保存分数数据
- 游戏控制:支持重新开始游戏
技术栈
- HTML5:页面结构
- CSS3:样式设计,包括答题状态反馈
- JavaScript:题目生成、答案验证、分数管理、本地存储
- 本地存储:localStorage API
项目结构
multiplication-game/
├── index.html # 游戏主页面
├── css/
│ └── style.css # 样式文件
├── js/
└── game.js # 游戏核心逻辑
实现思路
1. 数据结构设计
javascript
// 游戏状态对象
const gameState = {
currentScore: 0, // 当前分数
highScore: 0, // 历史最高分
num1: 0, // 第一个乘数
num2: 0, // 第二个乘数
currentAnswer: 0, // 当前题目正确答案
streak: 0, // 连续答对次数
maxStreak: 0 // 最大连续答对次数
};
2. 核心功能模块
- 题目生成模块:使用Math.random()生成随机乘数
- 答案验证模块:比较用户输入与正确答案
- 分数管理模块:更新分数、记录最高分
- 本地存储模块:保存和读取分数数据
- UI交互模块:更新界面、反馈答题结果
3. 关键技术点实现
- 随机数生成 :
Math.floor(Math.random() * 10) + 1
生成1-10的随机数 - 本地存储 :
localStorage.setItem()
和localStorage.getItem()
实现数据持久化 - 事件处理:监听表单提交和键盘事件
- 状态反馈:通过CSS类切换实现正确/错误状态样式
代码:
- index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>乘法答题游戏 | Math Multiplication Game</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<div class="game-container">
<header>
<h1>乘法答题挑战 <i class="fas fa-calculator"></i></h1>
<p>测试你的乘法计算能力,挑战最高分!</p>
</header>
<div class="score-board">
<div class="score-item">
<span class="label">当前分数</span>
<span id="currentScore" class="score">0</span>
</div>
<div class="score-item">
<span class="label">历史最高分</span>
<span id="highScore" class="score">0</span>
</div>
</div>
<div class="game-card">
<div class="problem">
<span id="num1">?</span>
<span class="operator">×</span>
<span id="num2">?</span>
<span class="equal">=</span>
<form id="answerForm">
<input type="number" id="answerInput" placeholder="输入答案" autocomplete="off" required>
<button type="submit" id="submitBtn">提交 <i class="fas fa-paper-plane"></i></button>
</form>
</div>
<div id="feedback" class="feedback hidden"></div>
<div class="controls">
<button id="restartBtn"><i class="fas fa-sync-alt"></i> 重新开始</button>
</div>
</div>
<div class="game-stats">
<div class="stat-item">
<i class="fas fa-fire"></i>
<span>当前连击: <span id="currentStreak">0</span></span>
</div>
<div class="stat-item">
<i class="fas fa-trophy"></i>
<span>最大连击: <span id="maxStreak">0</span></span>
</div>
</div>
</div>
<script src="js/game.js"></script>
</body>
</html>
- style.css
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f0f4f8;
color: #333;
line-height: 1.6;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.game-container {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
}
header h1 {
font-size: 2.2rem;
color: #2c3e50;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
header p {
color: #7f8c8d;
font-size: 1.1rem;
}
/* 分数面板 */
.score-board {
display: flex;
justify-content: space-around;
margin-bottom: 25px;
background-color: white;
border-radius: 12px;
padding: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.score-item {
text-align: center;
}
.label {
display: block;
font-size: 0.9rem;
color: #7f8c8d;
margin-bottom: 5px;
}
.score {
font-size: 1.8rem;
font-weight: bold;
color: #2c3e50;
}
/* 游戏卡片 */
.game-card {
background-color: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
margin-bottom: 25px;
}
/* 题目区域 */
.problem {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin-bottom: 25px;
}
.problem span {
font-size: 2.5rem;
font-weight: bold;
color: #2c3e50;
}
.operator, .equal {
color: #7f8c8d;
}
#answerForm {
display: flex;
gap: 10px;
flex: 0 0 180px;
}
#answerInput {
width: 100%;
padding: 12px 15px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 1.2rem;
text-align: center;
transition: all 0.3s ease;
}
#answerInput:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
#submitBtn {
padding: 12px 20px;
background-color: #3498db;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
#submitBtn:hover {
background-color: #2980b9;
}
/* 反馈区域 */
.feedback {
padding: 15px;
border-radius: 8px;
text-align: center;
font-size: 1.1rem;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.correct {
background-color: rgba(46, 204, 113, 0.1);
color: #27ae60;
border: 1px solid #2ecc71;
}
.incorrect {
background-color: rgba(231, 76, 60, 0.1);
color: #e74c3c;
border: 1px solid #e74c3c;
}
.hidden {
display: none;
}
/* 控制按钮 */
.controls {
text-align: center;
}
#restartBtn {
padding: 10px 20px;
background-color: #95a5a6;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
}
#restartBtn:hover {
background-color: #7f8c8d;
}
/* 游戏统计 */
.game-stats {
display: flex;
justify-content: space-around;
background-color: white;
border-radius: 12px;
padding: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.stat-item {
display: flex;
align-items: center;
gap: 8px;
color: #7f8c8d;
font-size: 0.95rem;
}
.stat-item i {
color: #f39c12;
}
.stat-item span {
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 500px) {
.problem {
flex-direction: column;
gap: 10px;
}
.problem span {
font-size: 2rem;
}
#answerForm {
flex: 0 0 100%;
}
.score-board {
flex-direction: column;
gap: 15px;
}
.game-stats {
flex-direction: column;
gap: 10px;
text-align: center;
}
.stat-item {
justify-content: center;
}
}
- game.js
js
const gameState = {
currentScore: 0,
highScore: 0,
num1: 0,
num2: 0,
currentAnswer: 0,
streak: 0,
maxStreak: 0
};
// DOM元素
const num1El = document.getElementById('num1');
const num2El = document.getElementById('num2');
const answerInput = document.getElementById('answerInput');
const answerForm = document.getElementById('answerForm');
const feedbackEl = document.getElementById('feedback');
const currentScoreEl = document.getElementById('currentScore');
const highScoreEl = document.getElementById('highScore');
const restartBtn = document.getElementById('restartBtn');
const currentStreakEl = document.getElementById('currentStreak');
const maxStreakEl = document.getElementById('maxStreak');
// 初始化游戏
function initGame() {
// 从本地存储加载分数
loadScores();
// 生成第一题
generateProblem();
// 绑定事件监听器
bindEvents();
// 更新UI显示
updateStats();
}
// 从本地存储加载分数
function loadScores() {
const savedScores = localStorage.getItem('multiplicationGameScores');
if (savedScores) {
const scores = JSON.parse(savedScores);
gameState.highScore = scores.highScore || 0;
gameState.maxStreak = scores.maxStreak || 0;
}
}
// 保存分数到本地存储
function saveScores() {
const scoresToSave = {
highScore: gameState.highScore,
maxStreak: gameState.maxStreak
};
localStorage.setItem('multiplicationGameScores', JSON.stringify(scoresToSave));
}
// 生成新题目
function generateProblem() {
// 生成1-10之间的随机数
gameState.num1 = Math.floor(Math.random() * 10) + 1;
gameState.num2 = Math.floor(Math.random() * 10) + 1;
gameState.currentAnswer = gameState.num1 * gameState.num2;
// 更新UI显示题目
num1El.textContent = gameState.num1;
num2El.textContent = gameState.num2;
// 清空输入框并聚焦
answerInput.value = '';
answerInput.focus();
// 隐藏反馈
feedbackEl.classList.add('hidden');
}
// 检查答案
function checkAnswer(userAnswer) {
const isCorrect = userAnswer === gameState.currentAnswer;
// 显示反馈
showFeedback(isCorrect);
if (isCorrect) {
// 答对处理
handleCorrectAnswer();
} else {
// 答错处理
handleWrongAnswer();
}
// 短暂延迟后生成新题目
setTimeout(generateProblem, 1500);
}
// 显示答题反馈
function showFeedback(isCorrect) {
feedbackEl.classList.remove('hidden', 'correct', 'incorrect');
if (isCorrect) {
feedbackEl.classList.add('correct');
feedbackEl.innerHTML = `<i class="fas fa-check-circle"></i> 正确!答案是 ${gameState.currentAnswer}`;
} else {
feedbackEl.classList.add('incorrect');
feedbackEl.innerHTML = `<i class="fas fa-times-circle"></i> 错误,正确答案是 ${gameState.currentAnswer}`;
}
}
// 处理正确答案
function handleCorrectAnswer() {
// 增加分数
gameState.currentScore += 10;
// 增加连击数
gameState.streak++;
// 更新最大连击数
if (gameState.streak > gameState.maxStreak) {
gameState.maxStreak = gameState.streak;
}
// 检查是否打破最高分记录
if (gameState.currentScore > gameState.highScore) {
gameState.highScore = gameState.currentScore;
}
// 保存分数
saveScores();
// 更新UI
updateStats();
// 添加答题正确动画效果
document.querySelector('.game-card').classList.add('correct-animation');
setTimeout(() => {
document.querySelector('.game-card').classList.remove('correct-animation');
}, 500);
}
// 处理错误答案
function handleWrongAnswer() {
// 重置连击数
gameState.streak = 0;
// 更新UI
updateStats();
// 添加答题错误动画效果
document.querySelector('.game-card').classList.add('wrong-animation');
setTimeout(() => {
document.querySelector('.game-card').classList.remove('wrong-animation');
}, 500);
}
// 更新游戏统计信息UI
function updateStats() {
currentScoreEl.textContent = gameState.currentScore;
highScoreEl.textContent = gameState.highScore;
currentStreakEl.textContent = gameState.streak;
maxStreakEl.textContent = gameState.maxStreak;
}
// 重置游戏
function resetGame() {
// 重置当前分数和连击
gameState.currentScore = 0;
gameState.streak = 0;
// 更新UI
updateStats();
// 生成新题目
generateProblem();
// 显示重置反馈
feedbackEl.classList.remove('hidden', 'correct', 'incorrect');
feedbackEl.innerHTML = '<i class="fas fa-gamepad"></i> 游戏已重置,开始新挑战吧!';
}
// 绑定事件监听器
function bindEvents() {
// 答案提交表单
answerForm.addEventListener('submit', (e) => {
e.preventDefault();
const userAnswer = parseInt(answerInput.value, 10);
if (!isNaN(userAnswer)) {
checkAnswer(userAnswer);
}
});
// 重新开始按钮
restartBtn.addEventListener('click', resetGame);
// 添加键盘事件支持
answerInput.addEventListener('focus', () => {
answerInput.select();
});
}
// 添加CSS动画样式
const style = document.createElement('style');
style.textContent = `
.correct-animation {
animation: correctPulse 0.5s ease-in-out;
}
.wrong-animation {
animation: wrongShake 0.5s ease-in-out;
}
@keyframes correctPulse {
0% { box-shadow: 0 6px 20px rgba(0,0,0,0.08); }
50% { box-shadow: 0 6px 20px rgba(46, 204, 113, 0.3); }
100% { box-shadow: 0 6px 20px rgba(0,0,0,0.08); }
}
@keyframes wrongShake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px); }
50% { transform: translateX(10px); }
75% { transform: translateX(-10px); }
}
`;
document.head.appendChild(style);
// 初始化游戏
document.addEventListener('DOMContentLoaded', initGame);
效果展示: