<!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>
:root {
--primary: #FF9F1C;
--secondary: #2EC4B6;
--accent: #E71D36;
--dark: #011627;
--light: #FDFFFC;
--bg: #F0F4F8;
--card-bg: #FFFFFF;
--success: #69C954;
--purple: #9D4EDD;
}
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg);
color: var(--dark);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
header {
background-color: var(--primary);
color: white;
padding: 15px;
text-align: center;
font-size: 1.2rem;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
}
main {
flex: 1;
overflow-y: auto;
padding: 20px;
padding-bottom: 120px;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
/* 通用卡片 */
.card {
background: var(--card-bg);
border-radius: 20px;
padding: 20px;
width: 100%;
max-width: 600px;
box-shadow: 0 8px 20px rgba(0,0,0,0.05);
margin-bottom: 20px;
transition: all 0.3s ease;
position: relative;
}
.text-lg { font-size: 1.1rem; line-height: 1.6; margin-bottom: 15px; }
.text-xl { font-size: 1.4rem; font-weight: bold; color: var(--primary); text-align: center; margin: 10px 0; }
.note { background: #FFF3CD; padding: 15px; border-radius: 10px; border-left: 5px solid #FFC107; font-size: 1rem; margin: 10px 0; line-height: 1.5; }
/* 动画演示区 */
.stage-area {
min-height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
padding-top: 20px;
}
/* 树形分解节点 */
.tree-node {
width: 60px;
height: 60px;
background: var(--dark);
color: white;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.2rem;
position: absolute;
transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
z-index: 2;
}
.tree-node.prime { background: var(--secondary); border: 2px solid white; }
.tree-node.highlight-2 { background: #4CC9F0; }
.tree-node.highlight-3 { background: #F72585; }
.tree-node.highlight-5 { background: #4361EE; }
.tree-line {
position: absolute;
background: #ccc;
transform-origin: top center;
z-index: 1;
transition: height 0.5s ease;
}
/* 桶 */
.bucket-container { display: flex; justify-content: center; gap: 15px; margin-top: 20px; width: 100%; }
.bucket {
border: 3px dashed #ddd; border-radius: 15px; padding: 10px; min-width: 80px; min-height: 100px;
display: flex; flex-direction: column; align-items: center; position: relative; background: #fafafa;
}
.bucket-label {
position: absolute; top: -15px; background: white; padding: 2px 8px; font-weight: bold;
border-radius: 10px; border: 1px solid #ddd; font-size: 0.9rem;
}
/* 选项列表 (学习模式) */
.option-list { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.option-item {
background: white; border: 2px solid #eee; padding: 10px; border-radius: 8px; text-align: center;
opacity: 0; transform: translateX(-10px); transition: all 0.3s ease;
}
.option-item.show { opacity: 1; transform: translateX(0); }
/* 练习模式样式 */
.quiz-header { display: flex; justify-content: space-between; margin-bottom: 15px; color: #888; font-size: 0.9rem; }
.quiz-progress { height: 6px; background: #eee; border-radius: 3px; margin-bottom: 20px; overflow: hidden; }
.quiz-progress-bar { height: 100%; background: var(--primary); width: 0%; transition: width 0.3s ease; }
.quiz-option {
display: block; width: 100%; padding: 15px; margin-bottom: 12px;
border: 2px solid #E0E0E0; border-radius: 12px; background: white;
font-size: 1.1rem; color: var(--dark); font-weight: 500;
cursor: pointer; transition: all 0.2s; text-align: left;
}
.quiz-option:active { transform: scale(0.98); }
.quiz-option.selected-correct { border-color: var(--success); background: #E8F5E9; color: #2E7D32; }
.quiz-option.selected-wrong { border-color: var(--accent); background: #FFEBEE; color: #C62828; animation: shake 0.4s; }
.feedback-box {
margin-top: 20px; padding: 15px; border-radius: 10px; font-size: 1rem; line-height: 1.5;
animation: slideUp 0.3s ease;
}
.feedback-correct { background: #E8F5E9; color: #2E7D32; border-left: 5px solid var(--success); }
.feedback-wrong { background: #FFEBEE; color: #C62828; border-left: 5px solid var(--accent); }
/* 底部导航 */
.bottom-nav {
position: fixed; bottom: 0; left: 0; width: 100%; background: white;
padding: 15px; box-shadow: 0 -5px 20px rgba(0,0,0,0.05);
display: flex; gap: 10px; z-index: 20;
}
.btn {
border: none; padding: 15px; border-radius: 12px; font-size: 1.1rem; font-weight: bold;
cursor: pointer; flex: 1; transition: all 0.2s; text-align: center;
}
.btn-primary { background: var(--primary); color: white; box-shadow: 0 4px 0 #e08e1a; }
.btn-primary:active { transform: translateY(4px); box-shadow: none; }
.btn-secondary { background: #E0E0E0; color: #555; box-shadow: 0 4px 0 #bdbdbd; }
.btn-action { background: var(--secondary); color: white; box-shadow: 0 4px 0 #249d91; }
/* 实用类 */
.hidden { display: none !important; }
.fade-in { animation: fadeIn 0.5s forwards; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
@keyframes slideUp { from { transform: translateY(10px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
</style>
</head>
<body>
<header>
<span id="appTitle">🕵️ 因子大侦探</span>
<span id="modeBadge" style="font-size: 0.8rem; background: rgba(255,255,255,0.2); padding: 4px 8px; border-radius: 4px;">学习模式</span>
</header>
<main id="mainContainer">
<!-- 学习卡片 -->
<div id="lessonCard" class="card">
<h2 id="lessonTitle" style="text-align:center; margin-bottom:15px;"></h2>
<div id="lessonContent"></div>
<div id="stage" class="stage-area"></div>
<div id="explanation" class="note hidden"></div>
</div>
<!-- 练习卡片 (初始隐藏) -->
<div id="quizCard" class="card hidden">
<div class="quiz-header">
<span id="quizIndexDisplay">第 1/10 题</span>
<span id="scoreDisplay">得分: 0</span>
</div>
<div class="quiz-progress">
<div id="progressBar" class="quiz-progress-bar"></div>
</div>
<h3 id="questionText" class="text-lg" style="font-weight: bold; font-size: 1.2rem;"></h3>
<div id="optionsContainer"></div>
<div id="feedbackContainer" class="hidden"></div>
</div>
<!-- 结算卡片 (初始隐藏) -->
<div id="resultCard" class="card hidden" style="text-align: center;">
<div style="font-size: 5rem; margin: 20px 0;">🏆</div>
<h2 id="finalScoreTitle">挑战完成!</h2>
<div id="finalScore" style="font-size: 3rem; color: var(--primary); font-weight: bold; margin: 10px 0;">90分</div>
<p id="finalComment" class="text-lg">太棒了!</p>
<div style="margin-top: 30px;">
<button class="btn btn-primary" onclick="restartQuiz()" style="width: 100%; margin-bottom: 10px;">再练一次 ⚔️</button>
<button class="btn btn-secondary" onclick="backToLesson()" style="width: 100%;">回顾知识点 📖</button>
</div>
</div>
</main>
<div class="bottom-nav" id="lessonNav">
<button id="prevBtn" class="btn btn-secondary" onclick="prevStep()">⬅️ 上一步</button>
<button id="actionBtn" class="btn btn-action hidden" onclick="triggerAction()">开始演示 ▶️</button>
<button id="nextBtn" class="btn btn-primary" onclick="nextStep()">下一步 ➡️</button>
</div>
<div class="bottom-nav hidden" id="quizNav">
<button class="btn btn-secondary" onclick="quitQuiz()">退出</button>
<button id="nextQuizBtn" class="btn btn-primary" onclick="nextQuestion()" disabled style="opacity: 0.5;">下一题 ➡️</button>
</div>
<script>
// --- 全局状态 ---
const state = {
mode: 'lesson', // 'lesson', 'quiz', 'result'
step: 0,
subStep: 0,
quizIndex: 0,
score: 0,
canProceedQuiz: false
};
// --- 题库数据 (10道) ---
const quizData = [
{
q: "1. 下面哪个数是奇数?",
opts: ["18 (末尾是8)", "24 (末尾是4)", "35 (末尾是5)", "100 (末尾是0)"],
a: 2, // Index of correct answer
expl: "个位是 1, 3, 5, 7, 9 的数是奇数。35是奇数,因为它不能被2整除。"
},
{
q: "2. 如果一个数的质因数分解里包含 2,那它一定是什么数?",
opts: ["奇数", "偶数", "质数", "不知道"],
a: 1,
expl: "只要乘法里有一个 2,结果就是 2 的倍数,也就是偶数。"
},
{
q: "3. 题目要求找“不是 2 的倍数”的因数,我们应该?",
opts: ["把 3 的桶清空", "把 5 的桶清空", "把 2 的桶封锁 (一个都不拿)", "所有桶都能拿"],
a: 2,
expl: "为了保证结果是奇数,我们绝对不能选任何一个 2。"
},
{
q: "4. 数字 12 = 2² × 3¹。如果不选 2,还有几种选法?",
opts: ["1种 (只选3¹)", "2种 (选3⁰和3¹)", "3种", "4种"],
a: 1,
expl: "2不能选。只看3¹。3¹有 (1+1)=2 种选法:选0个3 或 选1个3。"
},
{
q: "5. 计算 200 (2³ × 5²) 的因数中,不是 2 的倍数的有多少个?",
opts: ["2个", "3个", "4个", "5个"],
a: 1,
expl: "无视 2³。只看 5²。5² 的选法有 2+1 = 3 种。"
},
{
q: "6. 数字 75 = 3 × 5²。它本身含不含因数 2?",
opts: ["含", "不含"],
a: 1,
expl: "75 的质因数只有 3 和 5,没有 2。所以它的所有因数天然都是奇数。"
},
{
q: "7. 挑战:360 = 2³ × 3² × 5¹,有多少个奇数因数?",
opts: ["3个", "6个", "9个", "12个"],
a: 1,
expl: "挡住 2³!剩下 3² × 5¹。计算:(2+1) × (1+1) = 3 × 2 = 6 种。"
},
{
q: "8. 如果要把题目改成“找不是 5 的倍数”,该怎么办?",
opts: ["去掉 2 的桶", "去掉 3 的桶", "去掉 5 的桶", "都不去掉"],
a: 2,
expl: "举一反三!不是谁的倍数,就不能拿谁。不是5的倍数,就去掉5的桶。"
},
{
q: "9. 1000 = 2³ × 5³。它的奇数因数有几个?",
opts: ["2个", "4个", "8个", "16个"],
a: 1,
expl: "去掉 2³。只看 5³。选法有 3+1 = 4 种。"
},
{
q: "10. 终极一战:5400 = 2³ × 3³ × 5²,求奇数因数个数。",
opts: ["9个", "12个", "15个", "24个"],
a: 1,
expl: "奇数因数 → 去掉2。只看 3³ × 5²。<br>公式:(3+1) × (2+1) = 4 × 3 = 12 个!"
}
];
// --- 学习模式数据 ---
// (保持之前的逻辑,最后一步链接到Quiz)
const lessonData = [
{
title: "任务简报",
init: () => {
document.getElementById('lessonContent').innerHTML = `
<div class="text-lg">侦探,你接到了一个紧急任务!</div>
<div class="card" style="border: 2px dashed var(--primary); background: #FFF8E1;">
<div class="text-xl">找出 1800 的因数中<br>有多少个 <span style="color:var(--accent)">不是</span> 2 的倍数?</div>
</div>
<div class="text-lg">我们要分三步走:<br>1. 拆解它 (质因数分解)<br>2. 制定规则 (筛选条件)<br>3. 计算数量 (乘法原理)</div>
`;
document.getElementById('stage').innerHTML = '<div style="font-size:5rem; margin-top:50px;">🕵️♂️</div>';
hideActionBtn();
}
},
{
title: "第一步:层层拆解",
init: () => {
document.getElementById('lessonContent').innerHTML = `
<div class="text-lg">1800 太大了,我们像剥洋葱一样把它拆开。</div>
<div class="text-lg" style="color:#666; font-size:0.9rem;">点击下方按钮,一步步拆解 👇</div>
`;
document.getElementById('stage').innerHTML = `
<div id="node-root" class="tree-node" style="top:20px; left:50%; transform:translateX(-50%); width:80px;">1800</div>
`;
state.subStep = 0;
showActionBtn("拆成 18 和 100");
},
action: () => {
const stage = document.getElementById('stage');
if(state.subStep === 0) {
createNode(stage, "18", 100, 30);
createNode(stage, "100", 100, 70);
drawLines(stage, "node-root", ["node-18", "node-100"]);
showActionBtn("继续拆分");
} else if(state.subStep === 1) {
createNode(stage, "2", 180, 15, "highlight-2");
createNode(stage, "9", 180, 45);
createNode(stage, "10a", 180, 60, "", "10");
createNode(stage, "10b", 180, 85, "", "10");
drawLines(stage, "node-18", ["node-2", "node-9"]);
drawLines(stage, "node-100", ["node-10a", "node-10b"]);
showActionBtn("拆到底!");
} else if(state.subStep === 2) {
createNode(stage, "3a", 260, 35, "highlight-3", "3");
createNode(stage, "3b", 260, 55, "highlight-3", "3");
drawLines(stage, "node-9", ["node-3a", "node-3b"]);
createNode(stage, "2a", 260, 55, "highlight-2", "2");
createNode(stage, "5a", 260, 65, "highlight-5", "5");
drawLines(stage, "node-10a", ["node-2a", "node-5a"]);
setTimeout(() => {
stage.innerHTML = '';
stage.style.flexDirection = "row";
stage.style.flexWrap = "wrap";
stage.style.alignItems = "center";
stage.style.justifyContent = "center";
stage.style.gap = "10px";
const primes = [{v:2,c:'highlight-2'},{v:2,c:'highlight-2'},{v:2,c:'highlight-2'},{v:3,c:'highlight-3'},{v:3,c:'highlight-3'},{v:5,c:'highlight-5'},{v:5,c:'highlight-5'}];
primes.forEach((p,i) => {
const el = document.createElement('div');
el.className = `tree-node ${p.c}`;
el.style.position = 'relative'; el.style.transform = 'scale(0)'; el.innerText = p.v;
stage.appendChild(el);
setTimeout(() => el.style.transform = 'scale(1)', i * 100);
});
document.getElementById('explanation').innerHTML = "拆解完成!得到:三个 2,两个 3,两个 5";
document.getElementById('explanation').classList.remove('hidden');
hideActionBtn();
}, 1000);
}
state.subStep++;
}
},
{
title: "第二步:整理归纳",
init: () => {
document.getElementById('lessonContent').innerHTML = `<div class="text-lg">把它们装进“桶”里,变成指数形式。</div>`;
document.getElementById('stage').innerHTML = `
<div class="bucket-container">
<div class="bucket" id="bucket-2"><div class="bucket-label">2的桶</div></div>
<div class="bucket" id="bucket-3"><div class="bucket-label">3的桶</div></div>
<div class="bucket" id="bucket-5"><div class="bucket-label">5的桶</div></div>
</div>`;
showActionBtn("开始装桶");
state.subStep = 0;
},
action: () => {
const b2 = document.getElementById('bucket-2');
const b3 = document.getElementById('bucket-3');
const b5 = document.getElementById('bucket-5');
if(state.subStep === 0) {
[1,2,3].forEach(i => addBall(b2, "2", "highlight-2", i*200));
setTimeout(() => b2.innerHTML += `<div style="margin-top:10px; font-weight:bold;">2<sup>3</sup></div>`, 800);
state.subStep++;
} else if(state.subStep === 1) {
[1,2].forEach(i => addBall(b3, "3", "highlight-3", i*200));
setTimeout(() => b3.innerHTML += `<div style="margin-top:10px; font-weight:bold;">3<sup>2</sup></div>`, 600);
state.subStep++;
} else if(state.subStep === 2) {
[1,2].forEach(i => addBall(b5, "5", "highlight-5", i*200));
setTimeout(() => {
b5.innerHTML += `<div style="margin-top:10px; font-weight:bold;">5<sup>2</sup></div>`;
document.getElementById('explanation').innerHTML = "得到:<b>1800 = 2<sup>3</sup> × 3<sup>2</sup> × 5<sup>2</sup></b>";
document.getElementById('explanation').classList.remove('hidden');
hideActionBtn();
}, 600);
}
}
},
{
title: "第三步:破解“不是2的倍数”",
init: () => {
document.getElementById('lessonContent').innerHTML = `
<div class="text-lg">题目要求:<span style="color:var(--accent)">不是2的倍数</span>。也就是必须是奇数。</div>`;
document.getElementById('stage').innerHTML = `
<div class="bucket-container">
<div class="bucket" style="border-color:var(--primary)">
<div class="bucket-label">2的桶 (有3个)</div>
<div class="tree-node highlight-2" style="position:static; width:40px; height:40px; margin:5px;">2</div>
<div class="tree-node highlight-2" style="position:static; width:40px; height:40px; margin:5px;">2</div>
<div class="tree-node highlight-2" style="position:static; width:40px; height:40px; margin:5px;">2</div>
</div>
<div id="result-area" style="display:flex; align-items:center; font-size:1.5rem; font-weight:bold;">❓</div>
</div>`;
showActionBtn("试着拿一个 2");
state.subStep = 0;
},
action: () => {
const res = document.getElementById('result-area');
if(state.subStep === 0) {
res.innerHTML = `<span style="color:var(--accent)">偶数 ❌</span>`;
res.style.animation = "shake 0.5s";
document.getElementById('explanation').innerText = "拿了 2,乘积就是偶数。";
document.getElementById('explanation').classList.remove('hidden');
showActionBtn("封锁它!");
} else if(state.subStep === 1) {
const buckets = document.querySelector('.bucket');
buckets.style.opacity = '0.3';
const overlay = document.createElement('div');
overlay.innerText = "🚫 禁止";
overlay.style.position = 'absolute'; overlay.style.top = '40%'; overlay.style.left = '20%';
overlay.style.color = 'red'; overlay.style.fontWeight = 'bold'; overlay.style.transform = 'rotate(-20deg)'; overlay.style.fontSize='1.5rem'; overlay.style.border='3px solid red'; overlay.style.padding='5px';
buckets.appendChild(overlay);
res.innerHTML = `<span style="color:var(--success)">不拿 ✅</span>`;
document.getElementById('explanation').innerHTML = "必须一个都不拿!(只有 1 种选法:空手)";
hideActionBtn();
}
state.subStep++;
}
},
{
title: "第四步:盘点剩下的选择",
init: () => {
document.getElementById('lessonContent').innerHTML = `<div class="text-lg">3 和 5 的桶可以随便拿!</div>`;
document.getElementById('stage').innerHTML = `
<div style="display:flex; width:100%; justify-content:space-around;">
<div style="width:45%">
<h3 style="text-align:center; color:#F72585;">3 的桶 (3<sup>2</sup>)</h3>
<div id="opts-3" class="option-list"></div>
</div>
<div style="width:45%">
<h3 style="text-align:center; color:#4361EE;">5 的桶 (5<sup>2</sup>)</h3>
<div id="opts-5" class="option-list"></div>
</div>
</div>`;
state.subStep = 0;
showActionBtn("展示拿法");
},
action: () => {
const l3 = document.getElementById('opts-3');
const l5 = document.getElementById('opts-5');
if(state.subStep === 0) {
addOption(l3, "不拿", 0); addOption(l3, "拿1个", 100); addOption(l3, "拿2个", 200);
addOption(l5, "不拿", 400); addOption(l5, "拿1个", 500); addOption(l5, "拿2个", 600);
setTimeout(() => {
document.getElementById('explanation').innerHTML = "3 有 2+1=3 种选法<br>5 有 2+1=3 种选法";
document.getElementById('explanation').classList.remove('hidden');
hideActionBtn();
}, 800);
}
}
},
{
title: "🎉 最终计算",
init: () => {
document.getElementById('lessonContent').innerHTML = `<div class="text-lg">根据乘法原理:</div>`;
document.getElementById('stage').innerHTML = `
<div style="display:flex; align-items:center; justify-content:center; gap:10px; flex-wrap:wrap;">
<div class="card" style="width:70px; text-align:center; background:#eee; color:#999; padding:5px;">
<div>2</div><div style="font-size:1.5rem; font-weight:bold;">1</div>
</div>
<div class="text-xl">×</div>
<div class="card" style="width:70px; text-align:center; border:2px solid #F72585; padding:5px;">
<div>3</div><div style="font-size:1.5rem; font-weight:bold;">3</div>
</div>
<div class="text-xl">×</div>
<div class="card" style="width:70px; text-align:center; border:2px solid #4361EE; padding:5px;">
<div>5</div><div style="font-size:1.5rem; font-weight:bold;">3</div>
</div>
<div class="text-xl">=</div>
<div class="card" style="width:70px; text-align:center; background:var(--success); color:white; padding:5px;">
<div>结果</div><div style="font-size:2rem; font-weight:bold;">9</div>
</div>
</div>`;
document.getElementById('explanation').innerHTML = `公式:(3的指数+1) × (5的指数+1) = 9<br><b>学会了吗?去练习场试试身手!</b>`;
document.getElementById('explanation').classList.remove('hidden');
hideActionBtn();
// 特殊处理最后一个按钮
const nextBtn = document.getElementById('nextBtn');
nextBtn.innerHTML = "去练习 ⚔️";
nextBtn.style.backgroundColor = varColor('success');
nextBtn.onclick = startQuiz;
}
}
];
// --- 核心逻辑 ---
// 1. 学习模式
function createNode(c,t,top,left,cl="",dt) {
const el = document.createElement('div'); el.className=`tree-node ${cl}`; el.style.top=top+'px'; el.style.left=left+'%'; el.style.transform='translate(-50%,-50%) scale(0)'; el.id='node-'+t; el.innerText=dt||t; c.appendChild(el);
setTimeout(()=>el.style.transform='translate(-50%,-50%) scale(1)',50);
}
function drawLines(c,pid,cids) {
const p = document.getElementById(pid); const pX = p.offsetLeft; const pY = p.offsetTop + p.offsetHeight/2;
cids.forEach(cid=>{
setTimeout(()=>{
const ch = document.getElementById(cid); const cX = ch.offsetLeft; const cY = ch.offsetTop - ch.offsetHeight/2;
const len = Math.sqrt((cX-pX)**2 + (cY-pY)**2); const ang = Math.atan2(cY-pY, cX-pX)*180/Math.PI;
const l = document.createElement('div'); l.className='tree-line'; l.style.width=len+'px'; l.style.height='2px'; l.style.top=pY+'px'; l.style.left=pX+'px'; l.style.transform=`rotate(${ang}deg)`; l.style.transformOrigin='0 0';
c.insertBefore(l, c.firstChild);
},100);
});
}
function addBall(b,t,c,d) { setTimeout(()=>{const el=document.createElement('div'); el.className=`tree-node ${c}`; el.style.position='static'; el.style.width='30px'; el.style.height='30px'; el.style.fontSize='1rem'; el.style.transform='scale(0)'; el.innerText=t; b.appendChild(el); setTimeout(()=>el.style.transform='scale(1)',50);},d); }
function addOption(l,t,d) { setTimeout(()=>{const i=document.createElement('div'); i.className='option-item'; i.innerText=t; l.appendChild(i); void i.offsetWidth; i.classList.add('show');},d); }
function showActionBtn(t) { const b=document.getElementById('actionBtn'); b.innerText=t+" ▶️"; b.classList.remove('hidden'); b.classList.add('fade-in'); }
function hideActionBtn() { document.getElementById('actionBtn').classList.add('hidden'); }
function varColor(n) { return getComputedStyle(document.documentElement).getPropertyValue('--'+n).trim(); }
function renderLesson() {
const data = lessonData[state.step];
document.getElementById('lessonTitle').innerText = data.title;
document.getElementById('explanation').classList.add('hidden');
document.getElementById('stage').innerHTML = '';
data.init();
document.getElementById('prevBtn').disabled = state.step === 0;
const nextBtn = document.getElementById('nextBtn');
if(state.step < lessonData.length - 1) {
nextBtn.innerText = "下一步 ➡️";
nextBtn.style.backgroundColor = ""; // Reset color
nextBtn.onclick = nextStep;
}
}
function triggerAction() { if(lessonData[state.step].action) lessonData[state.step].action(); }
function nextStep() { if(state.step < lessonData.length-1) { state.step++; renderLesson(); } }
function prevStep() { if(state.step>0) { state.step--; renderLesson(); } }
// 2. 练习模式
function startQuiz() {
state.mode = 'quiz';
state.quizIndex = 0;
state.score = 0;
// UI切换
document.getElementById('modeBadge').innerText = "闯关模式";
document.getElementById('modeBadge').style.background = "#E71D36";
document.getElementById('lessonCard').classList.add('hidden');
document.getElementById('resultCard').classList.add('hidden');
document.getElementById('quizCard').classList.remove('hidden');
document.getElementById('lessonNav').classList.add('hidden');
document.getElementById('quizNav').classList.remove('hidden');
renderQuestion();
}
function renderQuestion() {
const q = quizData[state.quizIndex];
state.canProceedQuiz = false;
// 更新进度UI
document.getElementById('quizIndexDisplay').innerText = `第 ${state.quizIndex + 1} / ${quizData.length} 题`;
document.getElementById('scoreDisplay').innerText = `得分: ${state.score}`;
document.getElementById('progressBar').style.width = `${((state.quizIndex)/quizData.length)*100}%`;
document.getElementById('nextQuizBtn').disabled = true;
document.getElementById('nextQuizBtn').style.opacity = '0.5';
// 渲染题目
document.getElementById('questionText').innerText = q.q;
const optsCon = document.getElementById('optionsContainer');
optsCon.innerHTML = '';
q.opts.forEach((optText, idx) => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.innerText = optText;
btn.onclick = () => handleAnswer(idx, btn);
optsCon.appendChild(btn);
});
document.getElementById('feedbackContainer').innerHTML = '';
document.getElementById('feedbackContainer').classList.add('hidden');
}
function handleAnswer(selectedIndex, btnElem) {
if(state.canProceedQuiz) return; // 已答题,不可重选
state.canProceedQuiz = true;
const q = quizData[state.quizIndex];
const isCorrect = (selectedIndex === q.a);
// 样式更新
if(isCorrect) {
btnElem.classList.add('selected-correct');
state.score += 10;
document.getElementById('scoreDisplay').innerText = `得分: ${state.score}`;
showFeedback(true, q.expl);
} else {
btnElem.classList.add('selected-wrong');
// 标出正确答案
const allOpts = document.querySelectorAll('.quiz-option');
allOpts[q.a].classList.add('selected-correct');
showFeedback(false, q.expl);
}
// 启用下一步
const nextBtn = document.getElementById('nextQuizBtn');
nextBtn.disabled = false;
nextBtn.style.opacity = '1';
}
function showFeedback(isCorrect, text) {
const box = document.getElementById('feedbackContainer');
box.classList.remove('hidden');
box.innerHTML = `
<div class="feedback-box ${isCorrect ? 'feedback-correct' : 'feedback-wrong'}">
<strong>${isCorrect ? '🎉 回答正确!' : '❌ 哎呀,错了...'}</strong><br>
${text}
</div>
`;
// Auto scroll to bottom
setTimeout(() => {
document.getElementById('mainContainer').scrollTop = document.getElementById('mainContainer').scrollHeight;
}, 100);
}
function nextQuestion() {
if(state.quizIndex < quizData.length - 1) {
state.quizIndex++;
renderQuestion();
} else {
showResult();
}
}
function showResult() {
state.mode = 'result';
document.getElementById('quizCard').classList.add('hidden');
document.getElementById('quizNav').classList.add('hidden');
const resCard = document.getElementById('resultCard');
resCard.classList.remove('hidden');
document.getElementById('finalScore').innerText = state.score + "分";
let comment = "";
let emoji = "";
if(state.score === 100) { comment = "简直是数学天才!全对!"; emoji="🏆"; }
else if(state.score >= 80) { comment = "非常优秀!思路很清晰!"; emoji="🥈"; }
else if(state.score >= 60) { comment = "及格啦,继续加油!"; emoji="🥉"; }
else { comment = "别灰心,回去再复习一下?"; emoji="💪"; }
document.getElementById('finalComment').innerText = comment;
resCard.querySelector('div').innerText = emoji;
}
function restartQuiz() {
startQuiz();
}
function backToLesson() {
location.reload(); // 简单重置,回到学习首页
}
function quitQuiz() {
if(confirm("确定要退出练习吗?进度将不会保存。")) {
backToLesson();
}
}
// --- 初始化 ---
renderLesson();
</script>
</body>
</html>
💡 这段代码完全由 gemini 生成。
登录后可复制完整代码