
三模式整合修改方案要实现练习模式、挑战模式限时 2 分钟计分和双人模式限时 2 分钟抢答的无缝切换核心在于重构 JavaScript 的状态管理逻辑并动态渲染 HTML 界面。1. 修改位置概览修改区域操作说明关键逻辑HTML 头部修改.mode-switch按钮组增加“挑战模式”按钮统一绑定setMode()函数。HTML 主体重构.game-area内部结构保留单人容器保留双人容器新增“挑战模式”专用的计时/计分显示栏。CSS 样式添加模式控制类使用.hidden类控制不同模式下的容器显示/隐藏添加倒计时变红动画。JavaScript重写状态机与定时器引入gameState对象统一管理分数、时间、当前题目将键盘事件解耦为模式独立处理。直接复制以下代码保存为.html文件即可运行。代码已内置三种模式的完整逻辑。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title核苷酸拼装 - 三模式整合版/title style :root { --bg-color: #f0f4f8; --primary-blue: #4a90e2; --slot-border: #cbd5e0; --text-main: #2d3748; --p1-color: #e53e3e; --p2-color: #3182ce; --color-p: #ff9f43; --color-sugar-deoxy: #5f27cd; --color-sugar-ribo: #1dd1a1; --color-a: #ff6b6b; --color-g: #54a0ff; --color-c: #48dbfb; --color-t: #feca57; --color-u: #10ac84; } body { font-family: Segoe UI, Roboto, Helvetica, Arial, sans-serif; background-color: var(--bg-color); margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; user-select: none; } .game-container { width: 100%; max-width: 1000px; background: white; border-radius: 20px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); padding: 20px; display: flex; flex-direction: column; gap: 20px; } /* 头部样式 */ .header { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #f1f1f1; padding-bottom: 15px; } .logo-area { display: flex; align-items: center; gap: 10px; } .dna-icon { width: 40px; height: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white; display: flex; justify-content: center; align-items: center; font-weight: bold; font-size: 20px; } .title h1 { margin: 0; font-size: 24px; color: var(--text-main); } .title p { margin: 5px 0 0; font-size: 14px; color: #718096; } .mode-switch { display: flex; gap: 10px; } .btn-mode { padding: 8px 16px; border: 1px solid #e2e8f0; background: white; border-radius: 20px; cursor: pointer; font-size: 14px; transition: all 0.2s; } .btn-mode.active { background: var(--primary-blue); color: white; border-color: var(--primary-blue); } /* 游戏主区域 */ .game-area { background: #f8fafc; border-radius: 15px; padding: 20px; position: relative; min-height: 240px; } /* 顶部状态栏 (挑战/双人模式共用) */ .top-status-bar { display: none; justify-content: space-between; align-items: center; margin-bottom: 20px; background: #edf2f7; padding: 10px 20px; border-radius: 10px; } .timer-display { font-family: monospace; font-size: 28px; font-weight: bold; color: #2d3748; } .timer-urgent { color: #e53e3e; animation: pulse 1s infinite; } .score-board { display: flex; gap: 20px; font-weight: bold; font-size: 18px; } .score-item { padding: 4px 12px; border-radius: 6px; color: white; } .bg-p1 { background-color: var(--p1-color); } .bg-p2 { background-color: var(--p2-color); } .bg-single { background-color: var(--primary-blue); } /* 容器布局控制 */ .single-container, .vs-container { display: none; } .single-container.active, .vs-container.active { display: block; } .vs-container.active { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } /* 玩家区域样式 */ .player-zone { background: white; border: 2px solid #e2e8f0; border-radius: 15px; padding: 15px; text-align: center; transition: all 0.3s; } .player-zone.p1 { border-color: var(--p1-color); } .player-zone.p2 { border-color: var(--p2-color); } .player-zone.winner { box-shadow: 0 0 20px rgba(255, 215, 0, 0.6); transform: scale(1.02); border-color: #ecc94b; z-index: 10; } .target-name { font-size: 22px; font-weight: bold; margin-bottom: 15px; min-height: 30px; color: #4a5568; } .player-zone.p1 .target-name { color: var(--p1-color); } .player-zone.p2 .target-name { color: var(--p2-color); } /* 拼装槽位 */ .assembly-slots { display: flex; justify-content: center; align-items: center; gap: 10px; margin-bottom: 15px; height: 90px; } .connector-line { width: 20px; height: 2px; background-color: #cbd5e0; border-style: dashed; } .slot { width: 75px; height: 75px; border: 2px dashed var(--slot-border); border-radius: 12px; display: flex; justify-content: center; align-items: center; font-size: 12px; color: #a0aec0; transition: all 0.2s; background: rgba(255,255,255,0.5); } .slot.filled { border-style: solid; color: white; font-weight: bold; font-size: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); transform: scale(1.05); } .status-text { font-size: 13px; color: #718096; height: 20px; } /* 底部零件库 */ .parts-rack { display: grid; grid-template-columns: repeat(auto-fit, minmax(70px, 1fr)); gap: 10px; padding: 10px; background: #fff; border-top: 2px solid #f1f1f1; } .part-item { display: flex; flex-direction: column; align-items: center; cursor: pointer; transition: transform 0.1s; position: relative; } .part-item:active { transform: scale(0.95); } .part-shape { width: 50px; height: 50px; display: flex; justify-content: center; align-items: center; color: white; font-weight: bold; font-size: 20px; margin-bottom: 5px; box-shadow: 0 3px 0 rgba(0,0,0,0.1); } .shape-circle { border-radius: 50%; } .shape-pentagon { clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%); width: 60px; height: 60px; } .shape-rect { border-radius: 10px; } .part-label { font-size: 11px; color: #718096; text-align: center; } .key-hint { position: absolute; top: -5px; right: -5px; background: #fff; color: #718096; border: 1px solid #cbd5e0; border-radius: 50%; width: 18px; height: 18px; font-size: 9px; display: flex; justify-content: center; align-items: center; font-weight: bold; } /* 动画 */ keyframes popIn { 0% { transform: scale(0); opacity: 0; } 80% { transform: scale(1.1); } 100% { transform: scale(1); opacity: 1; } } .pop-in { animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; } keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } .shake { animation: shake 0.3s ease-in-out; border-color: #fc8181 !important; background-color: #fff5f5 !important; } keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.6; } 100% { opacity: 1; } } /* 弹窗 */ .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); display: none; justify-content: center; align-items: center; z-index: 1000; } .modal-content { background: white; padding: 40px; border-radius: 20px; text-align: center; max-width: 500px; animation: popIn 0.4s ease; } .modal-title { font-size: 32px; font-weight: bold; margin-bottom: 10px; } .btn-restart { background: var(--primary-blue); color: white; border: none; padding: 12px 30px; font-size: 18px; border-radius: 30px; cursor: pointer; margin-top: 20px; } /style /head body div classgame-container !-- 头部 -- div classheader div classlogo-area div classdna-iconDNA/div div classtitle h1核苷酸拼装互动小游戏/h1 p idmodeDesc选择模式开始游戏/p /div /div div classmode-switch button classbtn-mode active onclicksetMode(practice)练习模式/button button classbtn-mode onclicksetMode(challenge)挑战模式/button button classbtn-mode onclicksetMode(vs)双人对战/button /div /div !-- 游戏主区域 -- div classgame-area idgameArea !-- 顶部状态栏 (挑战/双人显示) -- div classtop-status-bar idtopStatusBar div classscore-board idscoreBoard !-- 动态注入分数 -- /div div classtimer-display idtimerDisplay02:00/div /div !-- 单人容器 (练习/挑战) -- div classsingle-container active idsingleContainer div classtarget-name idtargetName腺嘌呤核糖核苷酸/div div classassembly-slots div classslot idslot-phosphate data-typephosphatespan磷酸/span/div div classconnector-line/div div classslot idslot-sugar data-typesugarspan五碳糖/span/div div classconnector-line/div div classslot idslot-base data-typebasespan碱基/span/div /div div classstatus-text idmessageDisplay还差磷酸、五碳糖、碱基/div /div !-- 双人容器 -- div classvs-container idvsContainer !-- 玩家 1 -- div classplayer-zone p1 idp1Zone div stylefont-weight:bold; color:var(--p1-color); margin-bottom:5px;玩家 1 (Q,W,E...)/div div classtarget-name idp1TargetName准备.../div div classassembly-slots div classslot idp1-slot-phosphate data-typephosphatespan磷酸/span/div div classconnector-line/div div classslot idp1-slot-sugar data-typesugarspan五碳糖/span/div div classconnector-line/div div classslot idp1-slot-base data-typebasespan碱基/span/div /div div classstatus-text idp1Message等待中.../div /div !-- 玩家 2 -- div classplayer-zone p2 idp2Zone div stylefont-weight:bold; color:var(--p2-color); margin-bottom:5px;玩家 2 (A,S,D...)/div div classtarget-name idp2TargetName准备.../div div classassembly-slots div classslot idp2-slot-phosphate data-typephosphatespan磷酸/span/div div classconnector-line/div div classslot idp2-slot-sugar data-typesugarspan五碳糖/span/div div classconnector-line/div div classslot idp2-slot-base data-typebasespan碱基/span/div /div div classstatus-text idp2Message等待中.../div /div /div /div !-- 底部零件库 -- div classparts-rack idpartsRack/div /div !-- 结束弹窗 -- div classmodal-overlay idendModal div classmodal-content div classmodal-title idmodalTitle游戏结束/div div classmodal-desc idmodalDesc/div button classbtn-restart onclickrestartGame()再来一局/button /div /div script // --- 数据配置 --- const partsData [ { id: p, name: 磷酸, type: phosphate, shape: shape-circle, color: var(--color-p), label: P, keyP1: Q, keyP2: A }, { id: s_deoxy, name: 脱氧核糖, type: sugar, shape: shape-pentagon, color: var(--color-sugar-deoxy), label: 脱, keyP1: W, keyP2: S }, { id: s_ribo, name: 核糖, type: sugar, shape: shape-pentagon, color: var(--color-sugar-ribo), label: 核, keyP1: E, keyP2: D }, { id: b_a, name: 腺嘌呤, type: base, shape: shape-rect, color: var(--color-a), label: A, keyP1: R, keyP2: F }, { id: b_g, name: 鸟嘌呤, type: base, shape: shape-rect, color: var(--color-g), label: G, keyP1: T, keyP2: G }, { id: b_c, name: 胞嘧啶, type: base, shape: shape-rect, color: var(--color-c), label: C, keyP1: Y, keyP2: H }, { id: b_t, name: 胸腺嘧啶, type: base, shape: shape-rect, color: var(--color-t), label: T, keyP1: U, keyP2: J }, { id: b_u, name: 尿嘧啶, type: base, shape: shape-rect, color: var(--color-u), label: U, keyP1: I, keyP2: K } ]; const questions [ { name: 腺嘌呤核糖核苷酸, phosphate: p, sugar: s_ribo, base: b_a }, { name: 鸟嘌呤脱氧核苷酸, phosphate: p, sugar: s_deoxy, base: b_g }, { name: 胞嘧啶核糖核苷酸, phosphate: p, sugar: s_ribo, base: b_c }, { name: 胸腺嘧啶脱氧核苷酸, phosphate: p, sugar: s_deoxy, base: b_t }, { name: 尿嘧啶核糖核苷酸, phosphate: p, sugar: s_ribo, base: b_u }, { name: 腺嘌呤脱氧核苷酸, phosphate: p, sugar: s_deoxy, base: b_a }, { name: 鸟嘌呤核糖核苷酸, phosphate: p, sugar: s_ribo, base: b_g } ]; // --- 全局状态 --- let currentMode practice; // practice, challenge, vs let timerInterval null; let timeLeft 120; let isGameOver false; // 单人状态 (用于练习和挑战) let singleState { score: 0, qIndex: 0, slots: {}, currentQ: null }; // 双人状态 let vsState { p1: { score: 0, slots: {}, currentQ: null }, p2: { score: 0, slots: {}, currentQ: null }, active: false }; // --- 初始化 --- function init() { renderPartsRack(); setMode(practice); document.addEventListener(keydown, handleGlobalKeydown); } function renderPartsRack() { const rack document.getElementById(partsRack); rack.innerHTML ; partsData.forEach(part { const div document.createElement(div); div.className part-item; // 仅练习模式支持点击 if(currentMode practice) { div.onclick () processSingleMove(part.id); } const labelText part.label.length 1 ? part.label.charAt(0) : part.label; const hint currentMode vs ? ${part.keyP1}/${part.keyP2} : part.keyP1; div.innerHTML div classkey-hint${hint}/div div classpart-shape ${part.shape} stylebackground-color: ${part.color}${labelText}/div div classpart-label${part.name}/div ; rack.appendChild(div); }); } // --- 模式切换核心逻辑 --- function setMode(mode) { currentMode mode; isGameOver false; clearInterval(timerInterval); document.querySelectorAll(.btn-mode).forEach(b b.classList.remove(active)); event.target.classList.add(active); const singleCont document.getElementById(singleContainer); const vsCont document.getElementById(vsContainer); const topBar document.getElementById(topStatusBar); const scoreBoard document.getElementById(scoreBoard); const modeDesc document.getElementById(modeDesc); // 重置 UI 显示 topBar.style.display none; singleCont.classList.remove(active); vsCont.classList.remove(active); if (mode practice) { modeDesc.innerText 无限时间熟悉组件搭配; singleCont.classList.add(active); startPractice(); } else if (mode challenge) { modeDesc.innerText 2 分钟内看你能得多少分; singleCont.classList.add(active); topBar.style.display flex; scoreBoard.innerHTML div classscore-item bg-single得分span idsingleScoreVal0/span/div; startChallenge(); } else if (mode vs) { modeDesc.innerText 2 分钟内先答对 5 题者胜; vsCont.classList.add(active); topBar.style.display flex; scoreBoard.innerHTML div classscore-item bg-p1P1:span idp1ScoreVal0/span/5/div div classscore-item bg-p2P2:span idp2ScoreVal0/span/5/div ; startVs(); } renderPartsRack(); } // --- 练习模式逻辑 --- function startPractice() { singleState { score: 0, qIndex: 0, slots: {}, currentQ: getRandomQuestion() }; loadSingleQuestion(); } // --- 挑战模式逻辑 --- function startChallenge() { singleState { score: 0, qIndex: 0, slots: {}, currentQ: getRandomQuestion() }; timeLeft 120; updateTimerDisplay(); loadSingleQuestion(); timerInterval setInterval(() { timeLeft--; updateTimerDisplay(); if (timeLeft 0) endChallenge(); }, 1000); } function endChallenge() { clearInterval(timerInterval); showModal(时间到, 最终得分${singleState.score}); } // --- 双人模式逻辑 --- function startVs() { vsState { p1: { score: 0, slots: {}, currentQ: getRandomQuestion() }, p2: { score: 0, slots: {}, currentQ: getRandomQuestion() }, active: true }; timeLeft 120; updateTimerDisplay(); loadVsQuestion(p1); loadVsQuestion(p2); updateVsScoreBoard(); timerInterval setInterval(() { timeLeft--; updateTimerDisplay(); if (timeLeft 0) endVs(timeout); }, 1000); } function endVs(reason) { vsState.active false; clearInterval(timerInterval); const p1Score vsState.p1.score; const p2Score vsState.p2.score; let title 时间到; let desc ; if (reason win) { title 胜利; } else { if (p1Score p2Score) desc 玩家 1 获胜 (${p1Score} : ${p2Score}); else if (p2Score p1Score) desc 玩家 2 获胜 (${p2Score} : ${p1Score}); else desc 平局 (${p1Score} : ${p2Score}); } showModal(title, desc); } // --- 通用工具函数 --- function getRandomQuestion() { return questions[Math.floor(Math.random() * questions.length)]; } function updateTimerDisplay() { const m Math.floor(timeLeft / 60).toString().padStart(2, 0); const s (timeLeft % 60).toString().padStart(2, 0); const timerEl document.getElementById(timerDisplay); timerEl.innerText ${m}:${s}; if(timeLeft 10) timerEl.classList.add(timer-urgent); else timerEl.classList.remove(timer-urgent); } function showModal(title, desc) { document.getElementById(modalTitle).innerText title; document.getElementById(modalDesc).innerText desc; document.getElementById(endModal).style.display flex; } function restartGame() { document.getElementById(endModal).style.display none; document.getElementById(p1Zone).classList.remove(winner); document.getElementById(p2Zone).classList.remove(winner); // 重新触发当前模式的启动函数 if(currentMode practice) startPractice(); else if(currentMode challenge) startChallenge(); else if(currentMode vs) startVs(); } // --- 核心交互逻辑 --- function handleGlobalKeydown(e) { if (isGameOver) return; const key e.key.toUpperCase(); if (currentMode vs) { const p1Part partsData.find(p p.keyP1 key); if (p1Part) { processVsMove(p1, p1Part); return; } const p2Part partsData.find(p p.keyP2 key); if (p2Part) { processVsMove(p2, p2Part); } } else { // 练习和挑战模式共用 P1 键位 const part partsData.find(p p.keyP1 key); if (part) processSingleMove(part.id); } } // 单人移动处理 function processSingleMove(partId) { if (isGameOver) return; const part partsData.find(p p.id partId); const targetType part.type; // 填充槽位 const slotEl document.getElementById(slot-${targetType}); fillSlotVisual(slotEl, part); singleState.slots[targetType] part.id; updateSingleMessage(); checkSingleAnswer(); } function loadSingleQuestion() { const q singleState.currentQ; document.getElementById(targetName).innerText q.name; clearSlotsSingle(); } function clearSlotsSingle() { [phosphate, sugar, base].forEach(type { const slot document.getElementById(slot-${type}); slot.innerHTML span${type phosphate ? 磷酸 : (type sugar ? 五碳糖 : 碱基)}/span; slot.classList.remove(filled); slot.style.borderColor var(--slot-border); }); singleState.slots {}; updateSingleMessage(); } function fillSlotVisual(slotEl, part) { slotEl.innerHTML div classpart-shape ${part.shape} pop-in stylebackground-color: ${part.color}; width: 80%; height: 80%; font-size: 24px; ${part.label} /div ; slotEl.classList.add(filled); slotEl.style.borderColor part.color; } function updateSingleMessage() { const missing []; if (!singleState.slots.phosphate) missing.push(磷酸); if (!singleState.slots.sugar) missing.push(五碳糖); if (!singleState.slots.base) missing.push(碱基); const msgEl document.getElementById(messageDisplay); msgEl.innerText missing.length 0 ? 完成 : 还差 missing.join(、); msgEl.style.color missing.length 0 ? green : #718096; } function checkSingleAnswer() { const q singleState.currentQ; const s singleState.slots; if (s.phosphate s.sugar s.base) { const isCorrect (s.phosphate q.phosphate s.sugar q.sugar s.base q.base); if (isCorrect) { // 正确 singleState.score; if(currentMode challenge) { document.getElementById(singleScoreVal).innerText singleState.score; } // 下一题 setTimeout(() { singleState.currentQ getRandomQuestion(); loadSingleQuestion(); }, 300); } else { // 错误 const area document.getElementById(gameArea); area.classList.add(shake); setTimeout(() area.classList.remove(shake), 300); // 挑战模式下错误不扣分但清空练习模式仅提示 if(currentMode challenge) clearSlotsSingle(); } } } // 双人移动处理 function processVsMove(player, part) { if (!vsState.active) return; const state vsState[player]; const targetType part.type; const prefix player p1 ? p1 : p2; const slotEl document.getElementById(${prefix}-slot-${targetType}); fillSlotVisual(slotEl, part); state.slots[targetType] part.id; updateVsMessage(player); checkVsAnswer(player); } function loadVsQuestion(player) { const state vsState[player]; const prefix player p1 ? p1 : p2; document.getElementById(${prefix}TargetName).innerText state.currentQ.name; clearSlotsVs(player); } function clearSlotsVs(player) { const prefix player p1 ? p1 : p2; [phosphate, sugar, base].forEach(type { const slot document.getElementById(${prefix}-slot-${type}); slot.innerHTML span${type phosphate ? 磷酸 : (type sugar ? 五碳糖 : 碱基)}/span; slot.classList.remove(filled); slot.style.borderColor var(--slot-border); }); vsState[player].slots {}; updateVsMessage(player); } function updateVsMessage(player) { const state vsState[player]; const missing []; if (!state.slots.phosphate) missing.push(磷酸); if (!state.slots.sugar) missing.push(五碳糖); if (!state.slots.base) missing.push(碱基); const msgEl document.getElementById(player p1 ? p1Message : p2Message); msgEl.innerText missing.length 0 ? 完成 : 还差 missing.join(、); msgEl.style.color missing.length 0 ? green : #7180 ---- ## 参考来源 - [Gemini 2.5 Pro工程价值实测从benchmark到SLA的三根硬指标](https://blog.csdn.net/weixin_30321449/article/details/95233083) - [企业AI落地的18个隐形障碍与破局实战指南](https://blog.csdn.net/chongshi3083/article/details/100804705)