<!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>第09讲:还原问题(倒推法)</title>
<style>
/* 基础重置 */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* 隐藏滚动条 */
::-webkit-scrollbar { display: none; width: 0 !important; height: 0 !important; }
* { -ms-overflow-style: none; scrollbar-width: none; }
html, body { margin: 0; padding: 0; background: #F0F0F0; overflow: hidden; height: 100vh; }
/* 主容器 */
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: #fff; display: flex; flex-direction: column; }
.content-area { flex: 1; overflow-y: auto; padding-bottom: 80px; scroll-behavior: smooth; }
/* 页面通用样式 */
.page {
padding: 20px;
min-height: calc(100vh - 80px);
}
/* 标题样式 */
.page-title {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
text-align: center;
}
.page-subtitle {
font-size: 18px;
color: #667eea;
text-align: center;
margin-bottom: 30px;
}
.title-icon {
font-size: 36px;
text-align: center;
margin-bottom: 10px;
}
/* 概念卡片 */
.concept-card {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 16px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.concept-point {
margin: 15px 0;
padding-left: 10px;
border-left: 4px solid #667eea;
}
.concept-point strong {
color: #667eea;
}
/* 故事引入区域 */
.story-area {
background: #fef6e6;
border-radius: 16px;
padding: 20px;
margin: 20px 0;
line-height: 1.6;
font-size: 16px;
border: 2px dashed #ffc107;
}
.listen-btn {
display: block;
margin: 20px auto 0;
background: linear-gradient(to right, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.listen-btn:active {
transform: scale(0.95);
}
/* Tab 导航 */
.tab-nav { display: flex; border-bottom: 2px solid #e0e0e0; margin-bottom: 20px; }
.tab-item { flex: 1; padding: 12px; text-align: center; cursor: pointer; border-bottom: 3px solid transparent; font-weight: bold; }
.tab-item.active { color: #667eea; border-bottom-color: #667eea; }
/* 动画区域 */
.animation-area {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 16px;
padding: 20px;
min-height: 320px;
max-height: 320px;
position: relative;
overflow: hidden;
}
.svg-container { width: 100%; height: 100%; min-height: 280px; }
.demo-section { display: none; }
.demo-section.active { display: block; }
/* 公式和控制栏样式 */
.math-area {
background: white;
border: 2px solid #667eea;
border-radius: 12px;
padding: 12px;
margin: 15px 0;
min-height: 90px;
display: flex;
align-items: center;
justify-content: center;
}
.math-formula { font-size: 24px; font-weight: bold; color: #333; text-align: center; }
.step-explanation {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
color: #666;
font-size: 16px;
text-align: center;
margin-bottom: 20px;
line-height: 1.5;
}
.control-bar {
position: fixed;
bottom: 75px;
left: 50%;
transform: translateX(-50%);
width: 90%;
max-width: 480px;
z-index: 10001;
background: #ffffff;
padding: 10px;
border-radius: 15px;
box-shadow: 0 -5px 20px rgba(0,0,0,0.1);
display: flex;
justify-content: space-between;
}
.step-btn {
background: #667eea;
color: white;
border: none;
padding: 10px 30px;
border-radius: 20px;
font-size: 16px;
cursor: pointer;
transition: transform 0.2s, opacity 0.2s;
}
.step-btn:disabled { background: #ccc; cursor: not-allowed; opacity: 0.6; }
.step-btn:active { transform: scale(0.95); }
/* 悬浮步进按钮 */
.step-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 100;
background: rgba(102, 126, 234, 0.85);
color: white;
border: none;
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
.step-nav-btn:disabled {
background: rgba(204, 204, 204, 0.85);
cursor: not-allowed;
}
.step-nav-btn:active:not(:disabled) { transform: translateY(-50%) scale(0.95); }
.step-prev { left: 15px; }
.step-next { right: 15px; }
/* 讲解页例题卡片 */
.example-card {
background: white;
border-radius: 12px;
padding: 0;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
overflow: hidden;
border: 1px solid #eaeaea;
}
.example-header {
background: linear-gradient(to right, #a8edea, #fed6e3);
padding: 15px;
font-weight: bold;
color: #333;
display: flex;
justify-content: space-between;
cursor: pointer;
align-items: center;
}
.example-icon { font-size: 20px; }
.example-content {
padding: 0;
max-height: 0;
overflow: hidden;
transition: all 0.4s ease;
background: #f9f9f9;
}
.example-content.open {
padding: 20px;
max-height: 500px;
}
.example-question {
font-size: 16px;
margin-bottom: 15px;
line-height: 1.5;
}
.example-answer {
background: #e8f5e9;
padding: 10px;
border-radius: 8px;
margin-top: 10px;
font-weight: bold;
color: #2e7d32;
}
.example-explanation {
background: #fff3e0;
padding: 10px;
border-radius: 8px;
margin-top: 10px;
font-size: 14px;
line-height: 1.5;
}
/* 练习/奥数通用样式 */
.question-card {
background: white;
border-radius: 20px;
padding: 25px;
margin: 20px;
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
min-height: 400px;
display: flex;
flex-direction: column;
}
.question-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.question-number {
font-size: 18px;
font-weight: bold;
color: #667eea;
}
.score-display {
font-size: 18px;
font-weight: bold;
color: #ff9800;
}
.question-text {
font-size: 20px;
line-height: 1.6;
margin-bottom: 30px;
flex-grow: 1;
}
.options-container {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 25px;
}
.option-btn {
background: #f5f7ff;
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 18px;
font-size: 16px;
text-align: left;
cursor: pointer;
transition: all 0.2s;
}
.option-btn:active:not(:disabled) { transform: scale(0.98); }
.option-btn.correct { background: #e8f5e9; border-color: #4caf50; color: #2e7d32; }
.option-btn.wrong { background: #ffebee; border-color: #f44336; color: #c62828; }
.option-btn:disabled { cursor: default; opacity: 0.8; }
.feedback-area {
padding: 20px;
border-radius: 12px;
margin-top: 20px;
line-height: 1.6;
animation: fadeIn 0.5s;
}
.feedback-correct { background: #e8f5e9; color: #2e7d32; border: 1px solid #4caf50; }
.feedback-wrong { background: #fff3e0; color: #ef6c00; border: 1px solid #ff9800; }
.next-btn {
align-self: center;
background: linear-gradient(to right, #667eea, #764ba2);
color: white;
border: none;
padding: 15px 40px;
border-radius: 25px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin-top: 20px;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.next-btn:active { transform: scale(0.95); }
/* 奥数提示 */
.hint-box {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 15px;
margin: 15px 0;
border-radius: 8px;
font-size: 14px;
line-height: 1.5;
}
.difficulty-badge {
display: inline-block;
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
margin-left: 10px;
font-weight: bold;
}
.difficulty-1 { background: #e8f5e9; color: #2e7d32; }
.difficulty-2 { background: #fff3e0; color: #ef6c00; }
.difficulty-3 { background: #ffebee; color: #c62828; }
/* 完成页 */
.completion-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 500px;
text-align: center;
padding: 40px;
}
.completion-emoji { font-size: 80px; margin-bottom: 20px; }
.completion-title { font-size: 32px; font-weight: bold; color: #333; margin-bottom: 20px; }
.final-score {
font-size: 60px;
font-weight: bold;
color: #ff9800;
margin: 20px 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.restart-btn {
background: linear-gradient(to right, #667eea, #764ba2);
color: white;
border: none;
padding: 18px 50px;
border-radius: 30px;
font-size: 20px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin-top: 30px;
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
}
.restart-btn:active { transform: scale(0.95); }
/* 秘籍卡片(必须) */
.secrets-container {
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
}
.secret-card {
width: 100%;
min-height: 300px;
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: 20px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.secret-icon {
font-size: 60px;
margin-bottom: 25px;
}
.secret-title {
font-size: 28px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.secret-content {
font-size: 18px;
line-height: 1.6;
color: #555;
}
/* 底部导航 */
.bottom-nav {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 480px;
height: 70px;
background: white;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-around;
align-items: center;
z-index: 9999;
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
padding: 8px 0;
transition: all 0.2s;
}
.nav-item.active { color: #667eea; }
.nav-item.active .nav-icon { transform: scale(1.2); }
.nav-icon { font-size: 24px; transition: transform 0.2s; margin-bottom: 4px; }
.nav-label { font-size: 12px; }
/* 动画 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.shake { animation: shake 0.5s; }
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<!-- 页面1: 概念引入 -->
<div v-show="currentPage === 1" class="page intro-page">
<div class="title-icon">🔄</div>
<div class="page-title">还原问题(倒推法)</div>
<div class="page-subtitle">像倒放电影一样思考</div>
<div class="story-area">
<p>📖 <strong>小明的糖果故事</strong></p>
<p>小明现在有5颗糖。老师问:“如果你刚才从桌上拿了3颗,又给了小红2颗,那你最开始有多少糖呢?”</p>
<p>这就要用到<strong>倒推法</strong>!我们从他现在的5颗开始,反着做:<br>
给了小红的2颗,要拿回来:5 + 2 = 7<br>
从桌上拿的3颗,要放回去:7 - 3 = 4<br>
原来小明有4颗糖!</p>
</div>
<div class="concept-card">
<div class="concept-point">
<strong>倒推法</strong>:从结果出发,一步步往回推,就像倒着放电影。
</div>
<div class="concept-point">
<strong>反运算</strong>:加变减,减变加,乘变除,除变乘。
</div>
<div class="concept-point">
<strong>路线图</strong>:画出变化的路线,然后反着走一遍。
</div>
</div>
<button class="listen-btn" @click="speak('欢迎学习还原问题。倒推法就是从结果出发,一步步往回推,就像倒着放电影。关键是要记住反运算:加变减,减变加,乘变除,除变乘。')">
🔊 听老师讲解
</button>
</div>
<!-- 页面2: 演示动画 -->
<div v-show="currentPage === 2" class="page demo-page">
<div class="page-title">动画演示</div>
<div class="tab-nav">
<div class="tab-item" :class="{active: demoTab === 'box'}" @click="switchDemoTab('box')">盒子还原</div>
<div class="tab-item" :class="{active: demoTab === 'route'}" @click="switchDemoTab('route')">路线倒推</div>
</div>
<!-- 盒子还原 Tab -->
<div class="demo-section" :class="{active: demoTab === 'box'}">
<div class="animation-area">
<button class="step-nav-btn step-prev" @click="prevBoxStep" :disabled="boxStep === 0">◀</button>
<button class="step-nav-btn step-next" @click="nextBoxStep" :disabled="boxStep === 3">▶</button>
<svg class="svg-container" viewBox="0 0 400 330">
<!-- 初始数字5 -->
<circle id="start-circle" cx="100" cy="150" r="30" fill="#4caf50" stroke="#2e7d32" stroke-width="2" />
<text id="start-text" x="100" y="155" text-anchor="middle" fill="white" font-size="20" font-weight="bold">5</text>
<text x="100" y="200" text-anchor="middle" fill="#333" font-size="14">开始</text>
<!-- 箭头 -->
<path id="arrow1" d="M130,150 L170,150" stroke="#667eea" stroke-width="3" fill="none" marker-end="url(#arrowhead)" />
<!-- +3盒子 -->
<rect id="plus-box" x="180" y="120" width="60" height="60" rx="8" fill="#a8edea" stroke="#667eea" stroke-width="2" />
<text id="plus-text" x="210" y="155" text-anchor="middle" fill="#333" font-size="20" font-weight="bold">+3</text>
<text x="210" y="200" text-anchor="middle" fill="#333" font-size="14">第一步</text>
<!-- 中间数字8 -->
<circle id="mid-circle" cx="280" cy="150" r="30" fill="#ff9800" stroke="#ef6c00" stroke-width="2" />
<text id="mid-text" x="280" y="155" text-anchor="middle" fill="white" font-size="20" font-weight="bold">8</text>
<!-- 箭头2 -->
<path id="arrow2" d="M310,150 L350,150" stroke="#667eea" stroke-width="3" fill="none" marker-end="url(#arrowhead)" />
<!-- ×2盒子 -->
<rect id="multiply-box" x="360" y="120" width="60" height="60" rx="8" fill="#fed6e3" stroke="#667eea" stroke-width="2" />
<text id="multiply-text" x="390" y="155" text-anchor="middle" fill="#333" font-size="20" font-weight="bold">×2</text>
<text x="390" y="200" text-anchor="middle" fill="#333" font-size="14">第二步</text>
<!-- 结果数字16 -->
<circle id="end-circle" cx="460" cy="150" r="30" fill="#f44336" stroke="#c62828" stroke-width="2" />
<text id="end-text" x="460" y="155" text-anchor="middle" fill="white" font-size="20" font-weight="bold">16</text>
<text x="460" y="200" text-anchor="middle" fill="#333" font-size="14">结果</text>
<!-- 还原箭头 (反向) -->
<path id="reverse-arrow1" d="M360,250 L320,250" stroke="#9c27b0" stroke-width="3" fill="none" stroke-dasharray="5,5" marker-end="url(#reverse-arrowhead)" opacity="0" />
<path id="reverse-arrow2" d="M280,250 L240,250" stroke="#9c27b0" stroke-width="3" fill="none" stroke-dasharray="5,5" marker-end="url(#reverse-arrowhead)" opacity="0" />
<!-- 还原盒子 ÷2 -->
<rect id="divide-box" x="360" y="220" width="60" height="60" rx="8" fill="#e1bee7" stroke="#9c27b0" stroke-width="2" opacity="0" />
<text id="divide-text" x="390" y="255" text-anchor="middle" fill="#333" font-size="20" font-weight="bold" opacity="0">÷2</text>
<!-- 还原盒子 -3 -->
<rect id="minus-box" x="180" y="220" width="60" height="60" rx="8" fill="#c8e6c9" stroke="#9c27b0" stroke-width="2" opacity="0" />
<text id="minus-text" x="210" y="255" text-anchor="middle" fill="#333" font-size="20" font-weight="bold" opacity="0">-3</text>
<!-- 箭头头定义 -->
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#667eea" />
</marker>
<marker id="reverse-arrowhead" markerWidth="10" markerHeight="7" refX="1" refY="3.5" orient="auto">
<polygon points="10 0, 0 3.5, 10 7" fill="#9c27b0" />
</marker>
</defs>
</svg>
</div>
<!-- 公式展示区 -->
<div class="math-area">
<div class="math-formula" id="box-formula">
{{boxFormulas[boxStep]}}
</div>
</div>
<!-- 步骤文字讲解 -->
<div class="step-explanation">
{{boxSteps[boxStep]}}
</div>
</div>
<!-- 路线倒推 Tab -->
<div class="demo-section" :class="{active: demoTab === 'route'}">
<div class="animation-area">
<button class="step-nav-btn step-prev" @click="prevRouteStep" :disabled="routeStep === 0">◀</button>
<button class="step-nav-btn step-next" @click="nextRouteStep" :disabled="routeStep === 3">▶</button>
<svg class="svg-container" viewBox="0 0 400 330">
<!-- 苹果图片(简化表示) -->
<circle id="apple-whole" cx="200" cy="100" r="40" fill="#f44336" stroke="#c62828" stroke-width="3" />
<path id="apple-stem" d="M200,60 L200,50" stroke="#8d6e63" stroke-width="4" stroke-linecap="round" />
<ellipse id="apple-leaf" cx="190" cy="55" rx="15" ry="8" fill="#7cb342" transform="rotate(-30 190 55)" />
<!-- 被咬一口的苹果 -->
<circle id="apple-bite1" cx="200" cy="180" r="40" fill="#f44336" stroke="#c62828" stroke-width="3" opacity="0" />
<path d="M200,180 L200,140" stroke="#8d6e63" stroke-width="4" stroke-linecap="round" opacity="0" id="stem-bite1"/>
<ellipse cx="190" cy="135" rx="15" ry="8" fill="#7cb342" transform="rotate(-30 190 135)" opacity="0" id="leaf-bite1"/>
<!-- 咬痕 -->
<circle cx="230" cy="180" r="15" fill="#f0f0f0" stroke="#ddd" stroke-width="2" opacity="0" id="bite-mark1"/>
<!-- 被咬两口的苹果 -->
<circle id="apple-bite2" cx="200" cy="260" r="40" fill="#f44336" stroke="#c62828" stroke-width="3" opacity="0" />
<path d="M200,260 L200,220" stroke="#8d6e63" stroke-width="4" stroke-linecap="round" opacity="0" id="stem-bite2"/>
<ellipse cx="190" cy="215" rx="15" ry="8" fill="#7cb342" transform="rotate(-30 190 215)" opacity="0" id="leaf-bite2"/>
<!-- 两个咬痕 -->
<circle cx="230" cy="260" r="15" fill="#f0f0f0" stroke="#ddd" stroke-width="2" opacity="0" id="bite-mark2a"/>
<circle cx="170" cy="275" r="12" fill="#f0f0f0" stroke="#ddd" stroke-width="2" opacity="0" id="bite-mark2b"/>
<!-- 文字标签 -->
<text x="200" y="50" text-anchor="middle" fill="#333" font-size="14">完整苹果</text>
<text x="200" y="230" text-anchor="middle" fill="#333" font-size="14" opacity="0" id="label-bite1">被咬一口</text>
<text x="200" y="310" text-anchor="middle" fill="#333" font-size="14" opacity="0" id="label-bite2">被咬两口</text>
<!-- 倒放箭头 (向上) -->
<path id="reverse-arrow-up1" d="M200,140 L200,120" stroke="#9c27b0" stroke-width="3" fill="none" stroke-dasharray="5,5" marker-end="url(#reverse-arrowhead-up)" opacity="0" />
<path id="reverse-arrow-up2" d="M200,220 L200,200" stroke="#9c27b0" stroke-width="3" fill="none" stroke-dasharray="5,5" marker-end="url(#reverse-arrowhead-up)" opacity="0" />
<defs>
<marker id="reverse-arrowhead-up" markerWidth="10" markerHeight="7" refX="5" refY="3.5" orient="auto">
<polygon points="0 7, 10 3.5, 0 0" fill="#9c27b0" />
</marker>
</defs>
</svg>
</div>
<!-- 公式展示区 -->
<div class="math-area">
<div class="math-formula" id="route-formula">
{{routeFormulas[routeStep]}}
</div>
</div>
<!-- 步骤文字讲解 -->
<div class="step-explanation">
{{routeSteps[routeStep]}}
</div>
</div>
</div>
<!-- 页面3: 讲解页 -->
<div v-show="currentPage === 3" class="page explain-page">
<div class="page-title">例题讲解</div>
<div class="tab-nav">
<div class="tab-item" :class="{active: explainTab === 'method'}" @click="switchExplainTab('method')">倒推方法</div>
<div class="tab-item" :class="{active: explainTab === 'principle'}" @click="switchExplainTab('principle')">核心原理</div>
<div class="tab-item" :class="{active: explainTab === 'example'}" @click="switchExplainTab('example')">典型例题</div>
</div>
<!-- 倒推方法 Tab -->
<div class="demo-section" :class="{active: explainTab === 'method'}">
<div class="concept-card">
<h3>倒推法三步骤</h3>
<div class="concept-point">
<strong>第一步:明确结果</strong><br>
题目最后告诉我们的数字,就是倒推的起点。
</div>
<div class="concept-point">
<strong>第二步:反着运算</strong><br>
从最后一步开始,往前一步一步做“反运算”。
</div>
<div class="concept-point">
<strong>第三步:得到答案</strong><br>
倒推到第一步,就找到了原来的数。
</div>
</div>
</div>
<!-- 核心原理 Tab -->
<div class="demo-section" :class="{active: explainTab === 'principle'}">
<div class="concept-card">
<h3>反运算原理</h3>
<p style="line-height: 1.6; margin-bottom: 15px;">倒推法的核心是让运算“反过来”,抵消掉原来的变化。</p>
<div class="concept-point">
<strong>加与减是好朋友</strong><br>
加了3,就减去3还原。<br>
<span style="color: #667eea;">□ + 3 = 8 → □ = 8 - 3</span>
</div>
<div class="concept-point">
<strong>乘与除是好朋友</strong><br>
乘以2,就除以2还原。<br>
<span style="color: #667eea;">□ × 2 = 10 → □ = 10 ÷ 2</span>
</div>
<div class="concept-point">
<strong>顺序不能乱</strong><br>
倒推的顺序,要和原来变化的顺序完全相反。
</div>
</div>
</div>
<!-- 典型例题 Tab -->
<div class="demo-section" :class="{active: explainTab === 'example'}">
<div v-for="(example, index) in typicalExamples" :key="index" class="example-card">
<div class="example-header" @click="toggleExample(index)">
<span>{{ example.title }}</span>
<span class="example-icon">{{ openExampleIndex === index ? '🔽' : '▶' }}</span>
</div>
<div class="example-content" :class="{open: openExampleIndex === index}">
<div class="example-question">
<strong>题目</strong>:{{ example.question }}
</div>
<div class="example-answer">
<strong>答案</strong>:{{ example.answer }}
</div>
<div class="example-explanation">
<strong>解析</strong>:{{ example.explanation }}
</div>
</div>
</div>
</div>
</div>
<!-- 页面4: 课内练习 -->
<div v-show="currentPage === 4" class="practice-page">
<div v-if="!practiceCompleted">
<div class="question-card">
<div class="question-header">
<div class="question-number">第 {{currentQuestion + 1}}/{{practiceQuestions.length}} 题</div>
<div class="score-display">⭐ {{score}}分</div>
</div>
<div class="question-text" v-html="practiceQuestions[currentQuestion].text"></div>
<div class="options-container">
<button
v-for="(option, index) in practiceQuestions[currentQuestion].options"
:key="'opt'+index"
class="option-btn"
:class="{
'correct': answered && option.correct,
'wrong': answered && selectedOption === index && !option.correct
}"
@click="selectOption(index, option.correct)"
:disabled="answered"
>
{{option.text}}
</button>
</div>
<div v-if="answered" class="feedback-area" :class="isCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isCorrect">
<strong>🎉 太棒了!</strong><br>
{{practiceQuestions[currentQuestion].explanation}}
</div>
<div v-else>
<strong>💡 让我们一起分析:</strong><br>
{{practiceQuestions[currentQuestion].explanation}}
</div>
</div>
<button v-if="answered" class="next-btn" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '完成练习 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page">
<div class="completion-emoji">🎊</div>
<div class="completion-title">课内练习完成!</div>
<div class="final-score">{{score}}分</div>
<button class="restart-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<!-- 页面5: 奥数挑战 -->
<div v-show="currentPage === 5" class="practice-page">
<div v-if="!olympiadCompleted">
<div class="question-card">
<div class="question-header">
<div>
<span class="question-number">第 {{currentOlympiad + 1}}/{{olympiadQuestions.length}} 题</span>
<span class="difficulty-badge" :class="'difficulty-' + olympiadQuestions[currentOlympiad].difficulty">
{{olympiadQuestions[currentOlympiad].difficultyText}}
</span>
</div>
<div class="score-display">⭐ {{olympiadScore}}分</div>
</div>
<div class="question-text" v-html="olympiadQuestions[currentOlympiad].text"></div>
<div class="options-container">
<button
v-for="(option, index) in olympiadQuestions[currentOlympiad].options"
:key="'olopt'+index"
class="option-btn"
:class="{
'correct': olympiadAnswered && option.correct,
'wrong': olympiadAnswered && selectedOlympiadOption === index && !option.correct
}"
@click="selectOlympiadOption(index, option.correct)"
:disabled="olympiadAnswered"
>
{{option.text}}
</button>
</div>
<div v-if="wrongAttempts > 0 && !olympiadAnswered">
<div class="hint-box" v-if="wrongAttempts === 1">
<strong>💡 提示1:</strong> {{olympiadQuestions[currentOlympiad].hint1}}
</div>
<div class="hint-box" v-if="wrongAttempts === 2">
<strong>💡 提示2:</strong> {{olympiadQuestions[currentOlympiad].hint2}}
</div>
</div>
<div v-if="olympiadAnswered" class="feedback-area" :class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isOlympiadCorrect">
<strong>🏆 厉害!</strong><br>
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
<div v-else>
<strong>📖 详细解析:</strong><br>
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
</div>
<button v-if="olympiadAnswered || wrongAttempts >= 3" class="next-btn" @click="nextOlympiad">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '完成挑战 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page">
<div class="completion-emoji">🏆</div>
<div class="completion-title">奥数挑战完成!</div>
<div class="final-score">{{olympiadScore}}分</div>
<button class="restart-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<!-- 页面6: 通关秘籍 -->
<div v-show="currentPage === 6" class="secrets-container">
<div class="page-title">通关秘籍</div>
<div class="secret-card" style="background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);">
<div class="secret-icon">🔄</div>
<div class="secret-title">秘籍1:反着来</div>
<div class="secret-content">加变减,减变加,乘变除,除变乘。</div>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #fed6e3 0%, #fda085 100%);">
<div class="secret-icon">🗺️</div>
<div class="secret-title">秘籍2:画路线</div>
<div class="secret-content">把变化的步骤画下来,然后沿着箭头反方向走。</div>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #fda085 0%, #a8edea 100%);">
<div class="secret-icon">🎯</div>
<div class="secret-title">秘籍3:一半问题</div>
<div class="secret-content">看到“一半”,还原时记得乘以 2。</div>
</div>
</div>
</div>
<!-- 底部导航 -->
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<div class="nav-icon">💡</div>
<div class="nav-label">概念</div>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<div class="nav-icon">🎬</div>
<div class="nav-label">演示</div>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<div class="nav-icon">📝</div>
<div class="nav-label">讲解</div>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<div class="nav-icon">✏️</div>
<div class="nav-label">练习</div>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<div class="nav-icon">🏆</div>
<div class="nav-label">奥数</div>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<div class="nav-icon">🎁</div>
<div class="nav-label">秘籍</div>
</div>
</div>
</div>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/vue.global.prod.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/gsap.min.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/confetti.browser.min.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
currentPage: 1,
demoTab: 'box',
explainTab: 'method',
boxStep: 0,
routeStep: 0,
openExampleIndex: null,
// 概念页数据
boxFormulas: [
'正向变化:5 → (+3) → 8 → (×2) → 16',
'从结果开始:16',
'第一步还原:16 ÷ 2 = 8',
'第二步还原:8 - 3 = 5 (原来的数)'
],
boxSteps: [
'正向过程:一个数5,先加上3变成8,再乘以2变成16。',
'倒推开始:我们知道最后的结果是16。',
'最后一步是“×2”,所以反着做“÷2”:16 ÷ 2 = 8。',
'倒数第二步是“+3”,所以反着做“-3”:8 - 3 = 5。成功还原!'
],
routeFormulas: [
'正向:完整 → 咬一口 → 咬两口',
'倒推起点:被咬两口的苹果',
'第一步倒推:补回第二口,变成被咬一口',
'第二步倒推:补回第一口,变回完整苹果'
],
routeSteps: [
'想象一个苹果被连续咬了两口。',
'倒推就像把吃掉的果肉“变回来”。',
'把最后一口“补上”,苹果就只被咬了一口。',
'再把第一口也“补上”,苹果就恢复完整了!'
],
// 讲解页数据
typicalExamples: [
{
title: '例题1:基础题',
question: '某数加上 5 等于 12,这个数是多少?',
answer: '7',
explanation: '倒推:12 - 5 = 7。'
},
{
title: '例题2:进阶题',
question: '一个数减去 4,再乘以 2,得 12。这个数是多少?',
answer: '10',
explanation: '从 12 开始倒推。先除以 2:12 ÷ 2 = 6。再加 4:6 + 4 = 10。'
},
{
title: '例题3:易错题',
question: '小明一半的糖果是 4 颗,他原来有多少颗?',
answer: '8 颗',
explanation: '【易错点】不要用除法。一半是 4,说明除以 2 是 4。还原要乘以 2。4 × 2 = 8。'
}
],
// 练习题数据
practiceQuestions: [
{
text: '一个数减 8 等于 10,这个数是?',
options: [
{text: 'A. 2', correct: false},
{text: 'B. 18', correct: true},
{text: 'C. 80', correct: false},
{text: 'D. 2', correct: false}
],
explanation: '18。反着做加法:10 + 8 = 18。'
},
{
text: '一个数乘以 3 等于 15,这个数是?',
options: [
{text: 'A. 45', correct: false},
{text: 'B. 12', correct: false},
{text: 'C. 5', correct: true},
{text: 'D. 18', correct: false}
],
explanation: '5。反着做除法:15 ÷ 3 = 5。'
},
{
text: '某数加 4,再减 5,等于 6。原数是?',
options: [
{text: 'A. 5', correct: false},
{text: 'B. 6', correct: false},
{text: 'C. 7', correct: true},
{text: 'D. 8', correct: false}
],
explanation: '11-4=7。从6开始,倒着做:先+5得11,再-4得7。'
},
{
text: '小红有一些卡片,送给小明一半后还剩 5 张。原来有几张?',
options: [
{text: 'A. 10', correct: true},
{text: 'B. 2.5', correct: false},
{text: 'C. 15', correct: false},
{text: 'D. 20', correct: false}
],
explanation: '5 × 2 = 10。剩的一半是5,还原时要乘以2。'
},
{
text: '公交车下去 5 人,上来 3 人,现在车上 20 人。原来几人?',
options: [
{text: 'A. 18', correct: false},
{text: 'B. 22', correct: true},
{text: 'C. 25', correct: false},
{text: 'D. 15', correct: false}
],
explanation: '22。倒推:现在20人,把上来的3人“赶下去”:20-3=17。再把下去的5人“叫回来”:17+5=22。'
},
{
text: '一个数除以 2 再加上 3 等于 8。这个数是?',
options: [
{text: 'A. 10', correct: true},
{text: 'B. 5', correct: false},
{text: 'C. 22', correct: false},
{text: 'D. 16', correct: false}
],
explanation: '10。倒推:先算 8-3=5,再算 5×2=10。'
},
{
text: '妈妈买苹果吃了一半,又吃了 1 个,还剩 2 个。原来几个?',
options: [
{text: 'A. 5', correct: false},
{text: 'B. 6', correct: true},
{text: 'C. 4', correct: false},
{text: 'D. 8', correct: false}
],
explanation: '3 × 2 = 6。倒推:剩2个,补回吃了的1个,变成3。3个是“一半”,所以原来有3×2=6个。'
},
{
text: '(□ + 5) × 4 = 24,□ = ?',
options: [
{text: 'A. 1', correct: true},
{text: 'B. 2', correct: false},
{text: 'C. 6', correct: false},
{text: 'D. 4', correct: false}
],
explanation: '□ = 1。倒推:24 ÷ 4 = 6。□ + 5 = 6,所以□ = 1。'
}
],
// 奥数题数据
olympiadQuestions: [
{
text: '小松鼠存松果,第一天吃了一半,第二天吃了剩下的一半,第三天吃了剩下的一半,还剩 1 颗。原来有几颗?',
options: [
{text: 'A. 4', correct: false},
{text: 'B. 6', correct: false},
{text: 'C. 8', correct: true},
{text: 'D. 16', correct: false}
],
hint1: '第三天剩1,说明吃前是2。',
hint2: '第二天吃前是 2×2=4。',
hint3: '第一天吃前是 4×2=8。',
explanation: '连续翻倍。从最后1颗开始倒推:第三天吃前:1×2=2颗;第二天吃前:2×2=4颗;第一天吃前(原来):4×2=8颗。',
difficulty: 2,
difficultyText: '⭐⭐'
},
{
text: '一条毛毛虫幼虫,每天长度长一倍,5 天后长到 16 厘米。问几天长到 4 厘米?',
options: [
{text: 'A. 1', correct: false},
{text: 'B. 2', correct: false},
{text: 'C. 3', correct: true},
{text: 'D. 4', correct: false}
],
hint1: '第5天16,第4天是8。',
hint2: '第3天是4。',
hint3: '选C。',
explanation: '倒推减半。第5天:16厘米;第4天:16÷2=8厘米;第3天:8÷2=4厘米。所以是第3天长到4厘米。',
difficulty: 2,
difficultyText: '⭐⭐'
},
{
text: '商场卖衬衫,第一次卖出一半多 2 件,第二次卖出剩下的一半多 2 件,还剩 3 件。原来有多少件?',
options: [
{text: 'A. 20', correct: false},
{text: 'B. 22', correct: false},
{text: 'C. 24', correct: true},
{text: 'D. 26', correct: false}
],
hint1: '第二次卖前:(3+2)×2 = 10。',
hint2: '第一次卖前:(10+2)×2 = 24。',
hint3: '注意“多2件”还原时是“加2”。',
explanation: '复杂倒推。从最后剩3件开始:第二次卖前:(3+2)×2 = 10件;第一次卖前:(10+2)×2 = 24件。原来有24件。',
difficulty: 3,
difficultyText: '⭐⭐⭐'
},
{
text: '甲乙两桶油,从甲倒给乙 10 千克,两桶就一样重,都是 20 千克。甲原来有多少?',
options: [
{text: 'A. 10', correct: false},
{text: 'B. 20', correct: false},
{text: 'C. 30', correct: true},
{text: 'D. 40', correct: false}
],
hint1: '甲现在20。',
hint2: '甲给了乙10,拿回来。',
hint3: '20+10=30。',
explanation: '简单还原。甲现在20千克,这是给了乙10千克后的重量。所以甲原来有:20 + 10 = 30千克。',
difficulty: 1,
difficultyText: '⭐'
},
{
text: '一个数加上 3,乘以 3,再减去 3,最后除以 3,结果是 3。这个数是?',
options: [
{text: 'A. 0', correct: false},
{text: 'B. 1', correct: true},
{text: 'C. 2', correct: false},
{text: 'D. 3', correct: false}
],
hint1: '结果3,乘3得9。',
hint2: '9+3=12。',
hint3: '12÷3=4,4-3=1。',
explanation: '步步为营。从结果3开始倒推:最后是“除以3”,所以倒推“乘以3”:3×3=9;上一步是“减去3”,所以倒推“加3”:9+3=12;再上一步是“乘以3”,所以倒推“除以3”:12÷3=4;第一步是“加3”,所以倒推“减3”:4-3=1。',
difficulty: 3,
difficultyText: '⭐⭐⭐'
},
{
text: '三棵树上共有麻雀 60 只。如果不飞走,第一棵树上飞 4 只到第二棵,第二棵飞 7 只到第三棵,那么三棵树上的麻雀一样多。求第一棵树原来几只?',
options: [
{text: 'A. 20', correct: false},
{text: 'B. 24', correct: true},
{text: 'C. 16', correct: false},
{text: 'D. 23', correct: false}
],
hint1: '最后一样多,都是20。',
hint2: '第一棵最后是20。',
hint3: '它飞走4只才变20。',
explanation: '最后一样多,都是20。最后第一棵树有20只,这是它飞走4只后的数量。所以第一棵原来有:20 + 4 = 24只。',
difficulty: 2,
difficultyText: '⭐⭐'
},
{
text: '奶奶取钱,第一次取了一半,第二次取了剩下的一半,还剩 500 元。奶奶原来存了多少钱?',
options: [
{text: 'A. 1000', correct: false},
{text: 'B. 1500', correct: false},
{text: 'C. 2000', correct: true},
{text: 'D. 3000', correct: false}
],
hint1: '剩500,第二次取前是1000。',
hint2: '第一次取前是 2000。',
hint3: '2000元。',
explanation: '折半还原。最后剩500元,是第二次取后剩下的一半,所以第二次取前有:500 × 2 = 1000元;这1000元是第一次取后剩下的一半,所以原来有:1000 × 2 = 2000元。',
difficulty: 1,
difficultyText: '⭐'
},
{
text: '李白街上走,提壶去买酒。遇店加一倍,遇花喝一斗。三遇店和花,喝光壶中酒。原有多少酒?',
options: [
{text: 'A. 0.5斗', correct: false},
{text: 'B. 0.875斗', correct: true},
{text: 'C. 1斗', correct: false},
{text: 'D. 0.75斗', correct: false}
],
hint1: '最后是0。第三次遇花前是1。',
hint2: '第三次遇店前是0.5。第二次遇花前1.5。',
hint3: '第二次遇店前0.75。第一次遇花前1.75。第一次遇店前 1.75÷2=0.875。',
explanation: '经典诗词数学。倒推法:最后壶中酒0斗。第三次遇花前:0+1=1斗;第三次遇店前(加一倍前):1÷2=0.5斗;第二次遇花前:0.5+1=1.5斗;第二次遇店前:1.5÷2=0.75斗;第一次遇花前:0.75+1=1.75斗;第一次遇店前(原有酒):1.75÷2=0.875斗。',
difficulty: 3,
difficultyText: '⭐⭐⭐'
}
],
// 练习状态
currentQuestion: 0,
answered: false,
selectedOption: null,
isCorrect: false,
score: 0,
practiceCompleted: false,
// 奥数状态
currentOlympiad: 0,
olympiadAnswered: false,
selectedOlympiadOption: null,
isOlympiadCorrect: false,
olympiadScore: 0,
olympiadCompleted: false,
wrongAttempts: 0
};
},
methods: {
switchPage(page) {
this.stopSpeak();
this.currentPage = page;
if (page === 2) {
this.boxStep = 0;
this.routeStep = 0;
this.runBoxAnimation();
}
if (page === 4 && this.practiceCompleted) {
this.practiceCompleted = false;
this.currentQuestion = 0;
this.score = 0;
}
if (page === 5 && this.olympiadCompleted) {
this.olympiadCompleted = false;
this.currentOlympiad = 0;
this.olympiadScore = 0;
}
},
// 停止语音
stopSpeak() {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
const audio = document.getElementById('tts-audio');
if (audio) {
audio.pause();
audio.currentTime = 0;
}
} else {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
}
},
switchDemoTab(tab) {
this.stopSpeak();
this.demoTab = tab;
if (tab === 'box') {
this.boxStep = 0;
this.runBoxAnimation();
} else if (tab === 'route') {
this.routeStep = 0;
this.runRouteAnimation();
}
},
switchExplainTab(tab) {
this.stopSpeak();
this.explainTab = tab;
},
// 语音合成
speak(text) {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
let audio = document.getElementById('tts-audio');
if (!audio) {
audio = document.createElement('audio');
audio.id = 'tts-audio';
audio.style.display = 'none';
document.body.appendChild(audio);
}
const url = `https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
audio.play().catch(err => { console.log('语音播放失败:', err); });
} else {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
},
toggleExample(index) {
if (this.openExampleIndex === index) {
this.openExampleIndex = null;
} else {
this.openExampleIndex = index;
}
},
// GSAP 动画逻辑 - 盒子还原
runBoxAnimation() {
if (typeof gsap === 'undefined') return;
const tl = gsap.timeline();
tl.set(['#reverse-arrow1', '#reverse-arrow2', '#divide-box', '#divide-text', '#minus-box', '#minus-text'], { opacity: 0 });
tl.set(['#start-circle', '#start-text', '#mid-circle', '#mid-text', '#end-circle', '#end-text', '#plus-box', '#plus-text', '#multiply-box', '#multiply-text'], { opacity: 1 });
if (this.boxStep >= 1) {
tl.to('#end-circle', { scale: 1.2, duration: 0.3, ease: 'power2.out', yoyo: true, repeat: 1 });
}
if (this.boxStep >= 2) {
tl.to('#reverse-arrow1', { opacity: 1, duration: 0.5 }, '>');
tl.to('#divide-box', { opacity: 1, duration: 0.5 }, '<');
tl.to('#divide-text', { opacity: 1, duration: 0.5 }, '<');
tl.to('#multiply-box', { opacity: 0.3, duration: 0.5 }, '<');
tl.to('#multiply-text', { opacity: 0.3, duration: 0.5 }, '<');
tl.to('#end-circle', { fill: '#9c27b0', stroke: '#7b1fa2', duration: 0.5 }, '<');
tl.to('#mid-circle', { scale: 1.2, duration: 0.3, ease: 'bounce.out' }, '>');
}
if (this.boxStep >= 3) {
tl.to('#reverse-arrow2', { opacity: 1, duration: 0.5 }, '>');
tl.to('#minus-box', { opacity: 1, duration: 0.5 }, '<');
tl.to('#minus-text', { opacity: 1, duration: 0.5 }, '<');
tl.to('#plus-box', { opacity: 0.3, duration: 0.5 }, '<');
tl.to('#plus-text', { opacity: 0.3, duration: 0.5 }, '<');
tl.to('#mid-circle', { fill: '#9c27b0', stroke: '#7b1fa2', duration: 0.5 }, '<');
tl.to('#start-circle', { scale: 1.2, duration: 0.3, ease: 'bounce.out' }, '>');
}
},
// GSAP 动画逻辑 - 路线倒推
runRouteAnimation() {
if (typeof gsap === 'undefined') return;
const tl = gsap.timeline();
// 重置所有元素
tl.set(['#apple-whole', '#apple-stem', '#apple-leaf'], { opacity: 1 });
tl.set(['#apple-bite1', '#stem-bite1', '#leaf-bite1', '#bite-mark1', '#label-bite1'], { opacity: 0 });
tl.set(['#apple-bite2', '#stem-bite2', '#leaf-bite2', '#bite-mark2a', '#bite-mark2b', '#label-bite2', '#reverse-arrow-up1', '#reverse-arrow-up2'], { opacity: 0 });
if (this.routeStep >= 1) {
// 显示被咬两口的苹果
tl.to('#apple-bite2', { opacity: 1, duration: 0.5 });
tl.to(['#stem-bite2', '#leaf-bite2', '#bite-mark2a', '#bite-mark2b', '#label-bite2'], { opacity: 1, duration: 0.5 }, '<');
tl.to('#apple-bite2', { scale: 1.1, duration: 0.3, ease: 'bounce.out', yoyo: true, repeat: 1 }, '>');
}
if (this.routeStep >= 2) {
// 显示倒推箭头1,并补上一口(显示被咬一口的苹果)
tl.to('#reverse-arrow-up1', { opacity: 1, duration: 0.5 }, '>');
tl.to(['#bite-mark2b'], { opacity: 0, duration: 0.3 }, '<');
tl.to('#apple-bite1', { opacity: 1, duration: 0.5 }, '<');
tl.to(['#stem-bite1', '#leaf-bite1', '#bite-mark1', '#label-bite1'], { opacity: 1, duration: 0.5 }, '<');
tl.to('#apple-bite1', { scale: 1.1, duration: 0.3, ease: 'bounce.out' }, '>');
}
if (this.routeStep >= 3) {
// 显示倒推箭头2,并补上第二口(显示完整苹果)
tl.to('#reverse-arrow-up2', { opacity: 1, duration: 0.5 }, '>');
tl.to(['#bite-mark1'], { opacity: 0, duration: 0.3 }, '<');
tl.to(['#apple-bite1', '#stem-bite1', '#leaf-bite1', '#label-bite1'], { opacity: 0, duration: 0.5 }, '<');
tl.to(['#apple-whole', '#apple-stem', '#apple-leaf'], { opacity: 1, duration: 0.5 }, '<');
tl.to('#apple-whole', { scale: 1.1, duration: 0.3, ease: 'bounce.out', yoyo: true, repeat: 1 }, '>');
}
},
// 步骤导航
nextBoxStep() {
if (this.boxStep < 3) {
this.boxStep++;
this.runBoxAnimation();
// 注释掉语音自动播放
// if (this.boxSteps && this.boxSteps[this.boxStep]) {
// this.speak(this.boxSteps[this.boxStep]);
// }
}
},
prevBoxStep() {
if (this.boxStep > 0) {
this.boxStep--;
this.runBoxAnimation();
// 注释掉语音自动播放
// if (this.boxSteps && this.boxSteps[this.boxStep]) {
// this.speak(this.boxSteps[this.boxStep]);
// }
}
},
nextRouteStep() {
if (this.routeStep < 3) {
this.routeStep++;
this.runRouteAnimation();
}
},
prevRouteStep() {
if (this.routeStep > 0) {
this.routeStep--;
this.runRouteAnimation();
}
},
// 练习题逻辑
selectOption(index, correct) {
if (this.answered) return;
this.answered = true;
this.selectedOption = index;
this.isCorrect = correct;
if (correct) {
this.score += 20;
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
}
} else {
const btn = document.querySelectorAll('.option-btn')[index];
if (btn) {
btn.classList.add('shake');
setTimeout(() => btn.classList.remove('shake'), 500);
}
}
},
nextQuestion() {
if (this.currentQuestion < this.practiceQuestions.length - 1) {
this.currentQuestion++;
this.answered = false;
this.selectedOption = null;
} else {
this.practiceCompleted = true;
}
},
// 奥数题逻辑
selectOlympiadOption(index, correct) {
if (this.olympiadAnswered) return;
this.selectedOlympiadOption = index;
this.isOlympiadCorrect = correct;
if (correct) {
this.olympiadAnswered = true;
this.olympiadScore += 30;
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 150, spread: 80, origin: { y: 0.6 } });
}
} else {
this.wrongAttempts++;
const btn = document.querySelectorAll('.option-btn')[index];
if (btn) {
btn.classList.add('shake');
setTimeout(() => btn.classList.remove('shake'), 500);
}
if (this.wrongAttempts >= 3) {
this.olympiadAnswered = true;
}
}
},
nextOlympiad() {
if (this.currentOlympiad < this.olympiadQuestions.length - 1) {
this.currentOlympiad++;
this.olympiadAnswered = false;
this.selectedOlympiadOption = null;
this.wrongAttempts = 0;
} else {
this.olympiadCompleted = true;
}
}
},
mounted() {
// 初始动画
if (this.currentPage === 2) {
this.runBoxAnimation();
}
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码