<!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>GCD & LCM - 完整增强版</title>
<style>
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
html, body {
margin: 0;
padding: 0;
background: #F0F4F8;
overflow: hidden;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
-webkit-text-size-adjust: 100%;
}
#app {
height: 100vh;
width: 100%;
max-width: 480px;
margin: 0 auto;
background: #fff;
display: flex;
flex-direction: column;
box-shadow: 0 0 20px rgba(0,0,0,0.05);
}
.content-area {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 80px;
-webkit-overflow-scrolling: touch;
}
/* 顶部标签栏 */
.top-tabs {
display: flex;
background: linear-gradient(135deg, #667EEA 0%, #764BA2 100%);
padding: 10px 10px 0 10px;
gap: 5px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.top-tab {
flex-shrink: 0;
padding: 10px 15px;
border-radius: 12px 12px 0 0;
font-size: 13px;
font-weight: bold;
color: rgba(255,255,255,0.7);
cursor: pointer;
transition: all 0.3s;
background: rgba(255,255,255,0.1);
min-width: 100px;
text-align: center;
}
.top-tab.active {
background: white;
color: #8B5CF6;
transform: translateY(-2px);
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
animation: fadeIn 0.3s;
}
/* 底部导航 */
.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: 1000;
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 8px;
transition: all 0.3s;
color: #64748B;
}
.nav-item.active { color: #8B5CF6; }
.nav-icon { font-size: 24px; margin-bottom: 4px; }
.nav-label { font-size: 12px; font-weight: 500; }
/* 通用样式 */
.page-container { padding: 20px; }
.section-title {
font-size: 20px;
font-weight: 800;
color: #1E293B;
margin: 20px 0 15px 0;
display: flex;
align-items: center;
gap: 8px;
}
.card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #E2E8F0;
}
.tag {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
margin-bottom: 8px;
}
.tag-blue { background: #E0F2FE; color: #0284C7; }
.tag-purple { background: #F3E8FF; color: #7C3AED; }
.tag-green { background: #F0FDF4; color: #166534; }
.tag-orange { background: #FFEDD5; color: #C2410C; }
.speak-btn {
background: linear-gradient(135deg, #8B5CF6 0%, #7C3AED 100%);
color: white;
border: none;
padding: 12px 20px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
box-shadow: 0 4px 10px rgba(139, 92, 246, 0.3);
width: 100%;
}
/* 概念动画舞台 */
.concept-stage {
height: 200px;
background: #1E293B;
border-radius: 16px;
margin: 15px 0;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
/* GCD切割动画 */
.gcd-demo {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.wood-bar {
width: 280px;
height: 40px;
background: linear-gradient(135deg, #D97706 0%, #B45309 100%);
border-radius: 4px;
position: relative;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
}
.wood-bar::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: repeating-linear-gradient(
90deg,
transparent,
transparent 10px,
rgba(0,0,0,0.1) 10px,
rgba(0,0,0,0.1) 11px
);
}
.cutter {
width: 6px;
height: 80px;
background: linear-gradient(180deg, #EF4444 0%, #DC2626 100%);
position: absolute;
top: -80px;
left: 50%;
transform: translateX(-50%);
border-radius: 3px;
box-shadow: 0 4px 10px rgba(239, 68, 68, 0.5);
}
.cutter::after {
content: '✂️';
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
font-size: 24px;
}
.cut-pieces {
display: flex;
gap: 10px;
margin-top: 20px;
opacity: 0;
}
.piece {
width: 60px;
height: 30px;
background: linear-gradient(135deg, #D97706 0%, #B45309 100%);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
/* LCM堆叠动画 */
.lcm-demo {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 20px;
gap: 40px;
}
.stack-column {
display: flex;
flex-direction: column-reverse;
align-items: center;
gap: 5px;
}
.stack-label {
color: white;
font-weight: bold;
margin-bottom: 10px;
font-size: 18px;
}
.stack-box {
width: 50px;
height: 25px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 12px;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
.stack-a { background: linear-gradient(135deg, #3B82F6 0%, #2563EB 100%); }
.stack-b { background: linear-gradient(135deg, #10B981 0%, #059669 100%); }
/* 计算方法演示 */
.demo-nav {
display: flex;
background: #F1F5F9;
border-radius: 12px;
padding: 4px;
margin-bottom: 15px;
}
.demo-tab {
flex: 1;
padding: 10px;
text-align: center;
border-radius: 8px;
font-size: 13px;
font-weight: bold;
color: #64748B;
cursor: pointer;
transition: all 0.3s;
}
.demo-tab.active {
background: white;
color: #8B5CF6;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.step-controls {
display: flex;
gap: 15px;
margin-bottom: 15px;
justify-content: center;
}
.step-btn {
background: #8B5CF6;
color: white;
border: none;
padding: 10px 25px;
border-radius: 20px;
font-weight: bold;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.step-btn:disabled { background: #CBD5E1; cursor: not-allowed; }
.step-btn:active:not(:disabled) { transform: scale(0.95); }
.anim-stage {
background: #1E293B;
border-radius: 16px;
padding: 30px 20px;
min-height: 450px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid #334155;
}
.anim-text {
color: white;
margin-top: auto;
font-size: 15px;
font-weight: bold;
text-align: center;
min-height: 60px;
background: rgba(0,0,0,0.4);
padding: 15px 20px;
border-radius: 20px;
backdrop-filter: blur(4px);
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* 短除法 */
.short-div-container {
color: white;
font-family: 'Courier New', monospace;
font-size: 26px;
font-weight: bold;
margin-top: 40px;
}
.sd-row {
display: flex;
opacity: 0;
margin-bottom: 5px;
}
.sd-left {
border-right: 3px solid #FCD34D;
border-bottom: 3px solid #FCD34D;
padding: 8px 15px;
color: #FCD34D;
min-width: 50px;
text-align: center;
}
.sd-right {
border-bottom: 3px solid white;
padding: 8px 15px;
letter-spacing: 20px;
min-width: 150px;
}
.sd-bottom {
padding: 8px 15px;
letter-spacing: 20px;
margin-left: 65px;
opacity: 0;
}
.highlight-box {
position: absolute;
border: 3px solid #3B82F6;
border-radius: 8px;
opacity: 0;
pointer-events: none;
}
.highlight-gcd { border-color: #3B82F6; }
.highlight-lcm { border-color: #10B981; }
/* 质因数分解 */
.prime-container {
display: flex;
width: 100%;
justify-content: space-around;
margin-top: 40px;
color: white;
}
.prime-column {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.prime-label {
font-size: 16px;
color: #94A3B8;
font-weight: bold;
}
.prime-item {
background: #334155;
padding: 12px 16px;
border-radius: 10px;
font-weight: bold;
min-width: 70px;
text-align: center;
border: 3px solid transparent;
transition: all 0.3s;
font-size: 18px;
}
.prime-item sup {
color: #FCD34D;
font-size: 14px;
}
.prime-result {
margin-top: 40px;
font-size: 20px;
font-weight: bold;
color: #FCD34D;
opacity: 0;
text-align: center;
padding: 15px;
background: rgba(252, 211, 77, 0.1);
border-radius: 10px;
border: 2px solid #FCD34D;
}
/* 例题样式 */
.example-item {
border: 1px solid #E2E8F0;
border-radius: 8px;
margin-bottom: 10px;
overflow: hidden;
}
.example-header {
padding: 15px;
background: #F8FAFC;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
font-size: 15px;
}
.example-content {
padding: 15px;
border-top: 1px solid #E2E8F0;
background: white;
line-height: 1.8;
font-size: 14px;
color: #334155;
display: none;
}
.example-item.active .example-content { display: block; }
.example-question {
margin-bottom: 12px;
font-weight: bold;
color: #1E293B;
font-size: 15px;
}
.example-analysis {
background: #F0F9FF;
padding: 12px;
border-radius: 8px;
color: #0369A1;
font-size: 14px;
line-height: 1.8;
}
/* 题目样式 */
.question-card {
background: white;
border-radius: 16px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
margin-bottom: 20px;
}
.question-text {
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
line-height: 1.6;
color: #1E293B;
}
.option-btn {
padding: 15px;
border: 2px solid #E2E8F0;
border-radius: 12px;
background: white;
text-align: left;
font-size: 16px;
cursor: pointer;
margin-bottom: 10px;
transition: all 0.2s;
width: 100%;
}
.option-btn:hover { background: #F8FAFC; }
.option-btn.correct { border-color: #10B981; background: #ECFDF5; color: #047857; }
.option-btn.wrong { border-color: #EF4444; background: #FEF2F2; color: #B91C1C; }
.feedback-box {
margin-top: 15px;
padding: 12px;
background: #F1F5F9;
border-radius: 8px;
font-size: 14px;
line-height: 1.6;
}
.feedback-correct { background: #ECFDF5; color: #047857; }
.feedback-wrong { background: #FEF2F2; color: #B91C1C; }
.next-btn {
background: #8B5CF6;
color: white;
border: none;
padding: 12px;
border-radius: 8px;
width: 100%;
font-weight: bold;
font-size: 16px;
cursor: pointer;
margin-top: 15px;
}
.completion-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 40px 20px;
}
.completion-icon { font-size: 80px; margin-bottom: 20px; }
.completion-title { font-size: 28px; font-weight: bold; color: #1E293B; margin-bottom: 10px; }
.completion-score { font-size: 20px; color: #64748B; margin-bottom: 30px; }
/* 总结表格 */
.summary-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: 8px;
overflow: hidden;
font-size: 14px;
}
.summary-table th {
background: #8B5CF6;
color: white;
padding: 12px;
font-weight: bold;
}
.summary-table td {
background: #F3F4F6;
padding: 12px;
border-bottom: 1px solid white;
color: #374151;
}
.formula-box {
font-size: 18px;
text-align: center;
font-weight: bold;
color: #B45309;
padding: 20px;
background: #FFEDD5;
border-radius: 12px;
margin: 10px 0;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
</style><script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "\\[", right: "\\]", display: true},
{left: "$", right: "$", display: false},
{left: "\\(", right: "\\)", display: false}
],
throwOnError: false
});
});
</script>
</head>
<body>
<div id="app">
<div class="content-area">
<!-- Page 1: 形象理解 -->
<div v-show="currentPage === 1">
<div class="top-tabs">
<div class="top-tab" :class="{active: page1Tab === 'gcd'}" @click="page1Tab = 'gcd'">
最大公约数 GCD
</div>
<div class="top-tab" :class="{active: page1Tab === 'lcm'}" @click="page1Tab = 'lcm'">
最小公倍数 LCM
</div>
<div class="top-tab" :class="{active: page1Tab === 'compare'}" @click="page1Tab = 'compare'">
对比记忆
</div>
</div>
<div class="page-container">
<!-- GCD标签 -->
<div v-show="page1Tab === 'gcd'" class="tab-content" :class="{active: page1Tab === 'gcd'}">
<div class="section-title">🔍 最大公约数 (GCD)</div>
<div class="card">
<div class="tag tag-blue">第1步:什么是"约数"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>约数就是能整除的数</strong>(分苹果能分匀的人数)
</p>
<div style="background: #F0F9FF; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #0369A1; line-height: 2;">
<strong>12的约数:</strong><span id="divisors-12" style="color: #0284C7; font-weight: bold;"></span><br>
<strong>18的约数:</strong><span id="divisors-18" style="color: #0284C7; font-weight: bold;"></span>
</div>
</div>
<div class="concept-stage" style="height: 150px;">
<div style="width: 100%; height: 100%; display: flex; justify-content: space-around; align-items: center; padding: 20px;">
<div style="text-align: center;">
<div style="color: white; font-size: 18px; font-weight: bold; margin-bottom: 10px;">12个🍎</div>
<div id="apples-demo" style="display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; max-width: 150px;"></div>
</div>
<div style="text-align: center;">
<div style="color: white; font-size: 18px; font-weight: bold; margin-bottom: 10px;">18个🍊</div>
<div id="oranges-demo" style="display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; max-width: 180px;"></div>
</div>
</div>
</div>
<button class="speak-btn" @click="animateStep1" style="margin-top: 10px;">
▶️ 演示第1步:找约数
</button>
</div>
<div class="card">
<div class="tag tag-purple">第2步:什么是"公"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>公 = 大家都有的</strong>(两组数都能用的人数)
</p>
<div style="background: #F3E8FF; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #6B21A8; line-height: 2;">
<div><strong>12的约数:</strong><span id="divisors-12-compare"></span></div>
<div><strong>18的约数:</strong><span id="divisors-18-compare"></span></div>
<div style="margin-top: 10px; padding-top: 10px; border-top: 2px solid #A78BFA;">
<strong>公共约数:</strong><span id="common-divisors" style="font-size: 16px; color: #7C3AED; font-weight: bold;"></span>
</div>
</div>
</div>
<button class="speak-btn" @click="animateStep2" style="margin-top: 10px; background: linear-gradient(135deg, #A78BFA 0%, #7C3AED 100%);">
▶️ 演示第2步:找公共约数
</button>
</div>
<div class="card">
<div class="tag tag-green">第3步:什么是"最大"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>最大 = 挑个头最大的</strong>(在公共约数里选最大的)
</p>
<div style="background: #F0FDF4; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #166534; line-height: 2;">
<div><strong>公共约数:</strong><span id="common-divisors-final"></span></div>
<div style="margin-top: 15px; padding: 15px; background: white; border-radius: 8px; text-align: center;">
<div style="font-size: 16px; color: #059669; margin-bottom: 5px;">🏆 最大公约数 (GCD)</div>
<div id="gcd-result-box" style="font-size: 36px; font-weight: bold; color: #10B981;"></div>
</div>
</div>
</div>
<button class="speak-btn" @click="animateStep3" style="margin-top: 10px; background: linear-gradient(135deg, #10B981 0%, #059669 100%);">
▶️ 演示第3步:找最大的
</button>
</div>
<div class="card">
<div class="tag tag-orange">形象理解:剪彩带</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
有两根彩带,要剪成<strong>同样长的小段</strong>,而且<strong>不能有剩余</strong>。<br>
请问最长能剪多长?
</p>
<div class="concept-stage" style="height: 250px;">
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 20px; padding: 20px;">
<div style="text-align: center; width: 100%;">
<div style="color: white; font-size: 14px; margin-bottom: 8px;">🔴 红彩带 12厘米</div>
<div style="position: relative; height: 30px;">
<div id="red-ribbon" style="position: absolute; left: 50%; transform: translateX(-50%); width: 240px; height: 30px; background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%); border-radius: 4px;"></div>
<div id="red-cuts" style="position: absolute; left: 50%; transform: translateX(-50%); width: 240px; height: 30px; display: flex;"></div>
</div>
</div>
<div style="text-align: center; width: 100%;">
<div style="color: white; font-size: 14px; margin-bottom: 8px;">🔵 蓝彩带 18厘米</div>
<div style="position: relative; height: 30px;">
<div id="blue-ribbon" style="position: absolute; left: 50%; transform: translateX(-50%); width: 240px; height: 30px; background: linear-gradient(135deg, #3B82F6 0%, #2563EB 100%); border-radius: 4px;"></div>
<div id="blue-cuts" style="position: absolute; left: 50%; transform: translateX(-50%); width: 240px; height: 30px; display: flex;"></div>
</div>
</div>
<div id="ribbon-text" style="color: white; font-size: 14px; font-weight: bold; text-align: center; min-height: 40px; display: flex; align-items: center; justify-content: center;"></div>
</div>
</div>
<button class="speak-btn" @click="animateRibbon" style="margin-top: 10px; background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);">
▶️ 演示剪彩带
</button>
</div>
<div class="card">
<div class="tag tag-blue">特殊情况</div>
<div style="font-size: 14px; color: #334155; line-height: 2; margin-top: 10px;">
<p><strong>1️⃣ 倍数关系(大吃小):</strong></p>
<div style="background: #EFF6FF; padding: 12px; border-radius: 6px; margin: 8px 0;">
比如 4 和 12,因为 12 = 4×3<br>
最大公约数就是<strong>小的那个 = 4</strong>
</div>
<p style="margin-top: 15px;"><strong>2️⃣ 互质关系(只有1):</strong></p>
<div style="background: #EFF6FF; padding: 12px; border-radius: 6px; margin: 8px 0;">
比如 8 和 9,或者 3 和 5<br>
除了1,没有别的公共约数<br>
最大公约数 = <strong>1</strong>
</div>
</div>
</div>
<div class="card" style="background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);">
<div style="font-size: 16px; font-weight: bold; color: #92400E; margin-bottom: 10px;">💡 一句话总结</div>
<p style="font-size: 15px; color: #78350F; line-height: 1.8; margin: 0; font-weight: bold;">
最大公约数 = 能同时把几个数整除的那个<strong>最大的数</strong>
</p>
</div>
<button class="speak-btn" @click="speakGCDFull">
🔊 听完整讲解
</button>
</div>
<!-- LCM标签 -->
<div v-show="page1Tab === 'lcm'" class="tab-content" :class="{active: page1Tab === 'lcm'}">
<div class="section-title">📦 最小公倍数 (LCM)</div>
<div class="card">
<div class="tag tag-blue">第1步:什么是"倍数"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>倍数就是不停地翻倍</strong>(就像铺砖块,不断往前铺)
</p>
<div style="background: #EFF6FF; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #1E40AF; line-height: 2;">
<strong>4的倍数:</strong><span id="multiples-4" style="color: #2563EB; font-weight: bold;"></span><br>
<strong>6的倍数:</strong><span id="multiples-6" style="color: #2563EB; font-weight: bold;"></span>
</div>
</div>
<div class="concept-stage" style="height: 180px;">
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; gap: 25px; padding: 20px;">
<div style="text-align: left;">
<div style="color: white; font-size: 14px; margin-bottom: 8px;">📏 铺4米的砖</div>
<div id="tiles-4" style="display: flex; gap: 2px; flex-wrap: wrap;"></div>
</div>
<div style="text-align: left;">
<div style="color: white; font-size: 14px; margin-bottom: 8px;">📏 铺6米的砖</div>
<div id="tiles-6" style="display: flex; gap: 2px; flex-wrap: wrap;"></div>
</div>
</div>
</div>
<button class="speak-btn" @click="animateLCMStep1" style="margin-top: 10px;">
▶️ 演示第1步:找倍数
</button>
</div>
<div class="card">
<div class="tag tag-purple">第2步:什么是"公"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>公 = 撞车的数字</strong>(两边都有的位置,两个数相遇了)
</p>
<div style="background: #F3E8FF; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #6B21A8; line-height: 2;">
<div><strong>4的倍数:</strong><span id="multiples-4-compare"></span></div>
<div><strong>6的倍数:</strong><span id="multiples-6-compare"></span></div>
<div style="margin-top: 10px; padding-top: 10px; border-top: 2px solid #A78BFA;">
<strong>公共倍数:</strong><span id="common-multiples" style="font-size: 16px; color: #7C3AED; font-weight: bold;"></span>
</div>
</div>
</div>
<button class="speak-btn" @click="animateLCMStep2" style="margin-top: 10px; background: linear-gradient(135deg, #A78BFA 0%, #7C3AED 100%);">
▶️ 演示第2步:找公共倍数
</button>
</div>
<div class="card">
<div class="tag tag-green">第3步:什么是"最小"?</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>最小 = 第一次相遇</strong>(在撞车的数字里,选第一个出现的)
</p>
<div style="background: #F0FDF4; padding: 15px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 14px; color: #166534; line-height: 2;">
<div><strong>公共倍数:</strong><span id="common-multiples-final"></span></div>
<div style="margin-top: 15px; padding: 15px; background: white; border-radius: 8px; text-align: center;">
<div style="font-size: 16px; color: #059669; margin-bottom: 5px;">🏆 最小公倍数 (LCM)</div>
<div id="lcm-result-box" style="font-size: 36px; font-weight: bold; color: #10B981;"></div>
</div>
</div>
</div>
<div style="background: #FEF3C7; padding: 12px; border-radius: 8px; margin-top: 15px;">
<div style="font-size: 13px; color: #92400E; line-height: 1.8;">
<strong>🤔 为什么不说"最大公倍数"?</strong><br>
因为倍数可以无限大(12, 24, 36, 48...永远找不到头),所以只能找"最小"的!
</div>
</div>
<button class="speak-btn" @click="animateLCMStep3" style="margin-top: 10px; background: linear-gradient(135deg, #10B981 0%, #059669 100%);">
▶️ 演示第3步:找最小的
</button>
</div>
<div class="card">
<div class="tag tag-orange">形象理解:公交车相遇</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
两路公交车从同一个起点站发车,什么时候再次同时发车?
</p>
<div class="concept-stage" style="height: 280px;">
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; padding: 20px; gap: 15px;">
<div style="text-align: center;">
<div style="color: white; font-size: 15px; font-weight: bold; margin-bottom: 10px;">🚌 时间轴(分钟)</div>
<div style="display: flex; justify-content: space-between; color: #94A3B8; font-size: 12px; padding: 0 5px;">
<span>0</span><span>2</span><span>4</span><span>6</span><span>8</span><span>10</span><span>12</span><span>14</span>
</div>
<div style="height: 2px; background: #475569; margin-top: 5px; position: relative;">
<div id="timeline-marks" style="position: absolute; width: 100%; height: 100%;"></div>
</div>
</div>
<div>
<div style="color: #EF4444; font-size: 14px; font-weight: bold; margin-bottom: 8px;">🔴 4路车(每4分钟一班)</div>
<div id="bus-4-schedule" style="display: flex; gap: 8px; flex-wrap: wrap;"></div>
</div>
<div>
<div style="color: #3B82F6; font-size: 14px; font-weight: bold; margin-bottom: 8px;">🔵 6路车(每6分钟一班)</div>
<div id="bus-6-schedule" style="display: flex; gap: 8px; flex-wrap: wrap;"></div>
</div>
<div id="bus-meet-text" style="color: #FCD34D; font-size: 15px; font-weight: bold; text-align: center; min-height: 40px; display: flex; align-items: center; justify-content: center;"></div>
</div>
</div>
<button class="speak-btn" @click="animateBus" style="margin-top: 10px; background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);">
▶️ 演示公交车相遇
</button>
</div>
<div class="card">
<div class="tag tag-blue">实际用途:分数通分</div>
<div style="font-size: 14px; color: #334155; line-height: 2; margin-top: 10px;">
<p><strong>问题:</strong>计算 1/4 + 1/6 = ?</p>
<div style="background: #FEF2F2; padding: 12px; border-radius: 6px; margin: 10px 0;">
❌ <strong>不能直接加</strong>(一个切4块,一个切6块,每块大小不一样)
</div>
<div style="background: #ECFDF5; padding: 12px; border-radius: 6px; margin: 10px 0;">
✅ <strong>要通分</strong>(切成同样大小的块)<br>
找LCM(4, 6) = 12<br>
• 1/4 = 3/12<br>
• 1/6 = 2/12<br>
• 结果 = 5/12
</div>
</div>
</div>
<div class="card">
<div class="tag tag-orange">特殊情况</div>
<div style="font-size: 14px; color: #334155; line-height: 2; margin-top: 10px;">
<p><strong>1️⃣ 倍数关系(大吃小):</strong></p>
<div style="background: #EFF6FF; padding: 12px; border-radius: 6px; margin: 8px 0;">
比如 4 和 12,因为 12 = 4×3<br>
最小公倍数就是<strong>大的那个 = 12</strong><br>
<span style="color: #0284C7; font-weight: bold;">口诀:成倍数,选大的</span>
</div>
<p style="margin-top: 15px;"><strong>2️⃣ 互质关系(毫不相干):</strong></p>
<div style="background: #EFF6FF; padding: 12px; border-radius: 6px; margin: 8px 0;">
比如 3 和 5(没有共同的小因子)<br>
要想相遇,必须把对方"乘进来"<br>
最小公倍数 = <strong>3 × 5 = 15</strong><br>
<span style="color: #0284C7; font-weight: bold;">口诀:互质数,直接乘</span>
</div>
</div>
</div>
<div class="card" style="background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);">
<div style="font-size: 16px; font-weight: bold; color: #92400E; margin-bottom: 10px;">💡 一句话总结</div>
<p style="font-size: 15px; color: #78350F; line-height: 1.8; margin: 0; font-weight: bold;">
最小公倍数 = 能被几个数同时整除的那个<strong>最小的数</strong>(第一次相遇点)
</p>
</div>
<button class="speak-btn" @click="speakLCMFull">
🔊 听完整讲解
</button>
</div>
<!-- 对比标签 -->
<div v-show="page1Tab === 'compare'" class="tab-content" :class="{active: page1Tab === 'compare'}">
<div class="section-title">⚖️ GCD vs LCM 对比</div>
<div class="card">
<div style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<thead>
<tr style="background: linear-gradient(135deg, #8B5CF6 0%, #7C3AED 100%);">
<th style="padding: 12px; color: white; text-align: left; border-right: 1px solid rgba(255,255,255,0.2);">对比项</th>
<th style="padding: 12px; color: white; text-align: left; border-right: 1px solid rgba(255,255,255,0.2);">最大公约数 GCD</th>
<th style="padding: 12px; color: white; text-align: left;">最小公倍数 LCM</th>
</tr>
</thead>
<tbody>
<tr style="background: #F8FAFC;">
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; font-weight: bold;">📍 核心动作</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; border-right: 1px solid #E2E8F0;">切分、变小</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0;">拼凑、变大</td>
</tr>
<tr>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; font-weight: bold;">🎯 找什么</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; border-right: 1px solid #E2E8F0;">大家都有的<strong>最大零件</strong></td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0;">大家共同的<strong>最近终点</strong></td>
</tr>
<tr style="background: #F8FAFC;">
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; font-weight: bold;">📊 数值特点</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; border-right: 1px solid #E2E8F0;">通常<strong>较小</strong>(≤原数)</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0;">通常<strong>较大</strong>(≥原数)</td>
</tr>
<tr>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; font-weight: bold;">🏷️ 关键词</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; border-right: 1px solid #E2E8F0;">
<span style="background: #DBEAFE; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">剪</span>
<span style="background: #DBEAFE; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">分</span>
<span style="background: #DBEAFE; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">切</span>
<span style="background: #DBEAFE; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">最多</span>
</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0;">
<span style="background: #D1FAE5; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">遇</span>
<span style="background: #D1FAE5; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">至少</span>
<span style="background: #D1FAE5; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">再次</span>
<span style="background: #D1FAE5; padding: 2px 6px; border-radius: 3px; margin: 2px; display: inline-block;">同时</span>
</td>
</tr>
<tr style="background: #F8FAFC;">
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; font-weight: bold;">🎨 形象比喻</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0; border-right: 1px solid #E2E8F0;">裁纸条、剪彩带</td>
<td style="padding: 10px; border-bottom: 1px solid #E2E8F0;">公交车相遇、铺地砖</td>
</tr>
<tr>
<td style="padding: 10px; font-weight: bold;">📚 数学用途</td>
<td style="padding: 10px; border-right: 1px solid #E2E8F0;"><strong>分数约分</strong>(化简)</td>
<td style="padding: 10px;"><strong>分数通分</strong>(加减)</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card" style="background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);">
<div style="font-size: 16px; font-weight: bold; color: #92400E; margin-bottom: 15px;">🎯 快速判断口诀</div>
<div style="display: flex; gap: 15px;">
<div style="flex: 1; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
<div style="font-size: 15px; font-weight: bold; color: #0284C7; margin-bottom: 8px;">🔪 求GCD</div>
<div style="font-size: 13px; color: #334155; line-height: 1.8;">
• 切、分、剪<br>
• 最多、最大<br>
• 分组、整除
</div>
</div>
<div style="flex: 1; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
<div style="font-size: 15px; font-weight: bold; color: #059669; margin-bottom: 8px;">📦 求LCM</div>
<div style="font-size: 13px; color: #334155; line-height: 1.8;">
• 遇、重合<br>
• 至少、再次<br>
• 同时、第一次
</div>
</div>
</div>
</div>
<div class="card">
<div style="font-size: 16px; font-weight: bold; color: #1E293B; margin-bottom: 15px;">💡 典型例题对比</div>
<div style="background: #EFF6FF; padding: 15px; border-radius: 8px; margin-bottom: 15px; border-left: 4px solid #0284C7;">
<div style="font-weight: bold; color: #1E40AF; margin-bottom: 8px;">🔪 GCD题型:分水果</div>
<div style="font-size: 13px; color: #334155; line-height: 1.6;">
12个苹果和18个橙子,平均分给<strong>最多几个</strong>小朋友?<br>
<span style="color: #0284C7; font-weight: bold;">→ 关键词"最多" → 求GCD(12,18) = 6人</span>
</div>
</div>
<div style="background: #F0FDF4; padding: 15px; border-radius: 8px; border-left: 4px solid #10B981;">
<div style="font-weight: bold; color: #047857; margin-bottom: 8px;">📦 LCM题型:公交车</div>
<div style="font-size: 13px; color: #334155; line-height: 1.6;">
4路车4分钟一班,6路车6分钟一班,<strong>多久后再次同时</strong>发车?<br>
<span style="color: #059669; font-weight: bold;">→ 关键词"再次同时" → 求LCM(4,6) = 12分钟</span>
</div>
</div>
</div>
<div class="card">
<div style="font-size: 16px; font-weight: bold; color: #1E293B; margin-bottom: 15px;">🔗 黄金关系式</div>
<div style="background: #FEF2F2; padding: 20px; border-radius: 12px; text-align: center; border: 3px dashed #EF4444;">
<div style="font-size: 20px; font-weight: bold; color: #B91C1C; margin-bottom: 8px;">
A × B = GCD(A,B) × LCM(A,B)
</div>
<div style="font-size: 13px; color: #7F1D1D;">
两个数的乘积 = 它们的最大公约数 × 最小公倍数
</div>
</div>
<div style="margin-top: 15px; padding: 12px; background: #F3F4F6; border-radius: 8px; font-size: 13px; color: #374151;">
<strong>例:</strong>已知两数乘积=72,GCD=6,其中一数=12,求另一数?<br>
<strong>解:</strong>先求LCM = 72÷6 = 12,再求另一数 = 12×12÷12 = 12... 等等,不对!<br>
<strong>正解:</strong>另一数 = (GCD×LCM)÷已知数 = (6×12)÷12 = 6
</div>
</div>
<button class="speak-btn" @click="speakCompare">
🔊 听对比讲解
</button>
</div>
</div>
</div>
<!-- Page 2: 计算方法 -->
<div v-show="currentPage === 2" class="page-container">
<div class="section-title">🧮 三大计算方法</div>
<div class="demo-nav">
<div class="demo-tab" :class="{active: demoMode === 'short'}" @click="setDemoMode('short')">
短除法
</div>
<div class="demo-tab" :class="{active: demoMode === 'prime_gcd'}" @click="setDemoMode('prime_gcd')">
质因数→GCD
</div>
<div class="demo-tab" :class="{active: demoMode === 'prime_lcm'}" @click="setDemoMode('prime_lcm')">
质因数→LCM
</div>
</div>
<div class="step-controls">
<button class="step-btn" @click="prevStep" :disabled="animStep === 0">◀ 上一步</button>
<button class="step-btn" @click="nextStep" :disabled="isMaxStep">下一步 ▶</button>
<button class="step-btn" @click="resetDemo" style="background: #64748B;">🔄 重置</button>
</div>
<div class="anim-stage">
<!-- 短除法演示 -->
<div v-if="demoMode === 'short'" class="short-div-container">
<div class="sd-row" id="sd-row1">
<div class="sd-left">2</div>
<div class="sd-right">12 18</div>
</div>
<div class="sd-row" id="sd-row2">
<div class="sd-left">3</div>
<div class="sd-right"> 6 9</div>
</div>
<div class="sd-bottom" id="sd-bottom">2 3</div>
<div class="highlight-box highlight-gcd" id="hl-gcd"></div>
<div class="highlight-box highlight-lcm" id="hl-lcm"></div>
</div>
<!-- 质因数GCD演示 -->
<div v-if="demoMode === 'prime_gcd'" class="prime-container">
<div class="prime-column">
<div class="prime-label">数A = 360</div>
<div class="prime-item" id="pf-gcd-a2">2<sup>3</sup></div>
<div class="prime-item" id="pf-gcd-a3">3<sup>2</sup></div>
<div class="prime-item" id="pf-gcd-a5">5<sup>1</sup></div>
</div>
<div class="prime-column">
<div class="prime-label">数B = 108</div>
<div class="prime-item" id="pf-gcd-b2">2<sup>2</sup></div>
<div class="prime-item" id="pf-gcd-b3">3<sup>3</sup></div>
</div>
</div>
<div v-if="demoMode === 'prime_gcd'" class="prime-result" id="gcd-result"></div>
<!-- 质因数LCM演示 -->
<div v-if="demoMode === 'prime_lcm'" class="prime-container">
<div class="prime-column">
<div class="prime-label">数A = 360</div>
<div class="prime-item" id="pf-lcm-a2">2<sup>3</sup></div>
<div class="prime-item" id="pf-lcm-a3">3<sup>2</sup></div>
<div class="prime-item" id="pf-lcm-a5">5<sup>1</sup></div>
</div>
<div class="prime-column">
<div class="prime-label">数B = 108</div>
<div class="prime-item" id="pf-lcm-b2">2<sup>2</sup></div>
<div class="prime-item" id="pf-lcm-b3">3<sup>3</sup></div>
</div>
</div>
<div v-if="demoMode === 'prime_lcm'" class="prime-result" id="lcm-result"></div>
<div class="anim-text">{{ animText }}</div>
</div>
<div class="card" style="margin-top: 15px;">
<div v-if="demoMode === 'short'">
<div class="tag tag-blue">短除法口诀</div>
<p style="font-size: 14px; line-height: 1.8; color: #334155;">
<strong>步骤:</strong><br>
1️⃣ 找质数除,两数都能整除才写<br>
2️⃣ 互质就停止<br>
3️⃣ GCD = 左边一列相乘(I型)<br>
4️⃣ LCM = 左边+底部全乘(L型)
</p>
</div>
<div v-if="demoMode === 'prime_gcd'">
<div class="tag tag-purple">质因数求GCD</div>
<p style="font-size: 14px; line-height: 1.8; color: #334155;">
<strong>口诀:取同取小</strong><br>
1️⃣ 只要两边都有的质因数<br>
2️⃣ 选指数小的那个<br>
3️⃣ 全部相乘得GCD
</p>
</div>
<div v-if="demoMode === 'prime_lcm'">
<div class="tag tag-green">质因数求LCM</div>
<p style="font-size: 14px; line-height: 1.8; color: #334155;">
<strong>口诀:取全取大</strong><br>
1️⃣ 所有出现过的质因数都要<br>
2️⃣ 选指数大的那个<br>
3️⃣ 全部相乘得LCM
</p>
</div>
</div>
</div>
<!-- Page 3: 实战例题 -->
<div v-show="currentPage === 3" class="page-container">
<div class="section-title">⚔️ 实战演练</div>
<div class="example-item" :class="{active: activeExample === index}" v-for="(ex, index) in examples" :key="index">
<div class="example-header" @click="toggleExample(index)">
<span>{{ ex.title }}</span>
<span>{{ activeExample === index ? '▲' : '▼' }}</span>
</div>
<div class="example-content">
<div class="example-question">{{ ex.question }}</div>
<div class="example-analysis">
<strong>💡 思路:</strong>{{ ex.logic }}<br><br>
<strong>📝 解析:</strong><span v-html="ex.analysis"></span>
</div>
</div>
</div>
</div>
<!-- Page 4: 基础练习 -->
<div v-show="currentPage === 4" class="page-container">
<div v-if="!basicDone" class="quiz-container">
<div class="question-card">
<div class="tag tag-blue">基础题 {{ currentBasicIndex + 1 }}/10</div>
<div class="question-text">{{ basicQuestions[currentBasicIndex].text }}</div>
<div v-for="(opt, idx) in basicQuestions[currentBasicIndex].options"
:key="idx"
class="option-btn"
:class="{
'correct': basicAnswered && opt.correct,
'wrong': basicAnswered && selectedBasicOpt === idx && !opt.correct
}"
@click="handleBasicAnswer(idx, opt.correct)">
{{ String.fromCharCode(65 + idx) }}. {{ opt.text }}
</div>
<div v-if="basicAnswered" class="feedback-box" :class="isBasicCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isBasicCorrect" style="font-weight: bold; margin-bottom: 5px;">🎉 回答正确!</div>
<div v-else style="font-weight: bold; margin-bottom: 5px;">💡 解析</div>
<div v-html="basicQuestions[currentBasicIndex].expl"></div>
</div>
<button v-if="basicAnswered" class="next-btn" @click="nextBasic">
{{ currentBasicIndex < 9 ? '下一题 →' : '完成练习 🎯' }}
</button>
</div>
</div>
<div v-else class="card completion-card">
<div class="completion-icon">🏆</div>
<div class="completion-title">基础训练完成!</div>
<div class="completion-score">得分:{{ basicScore }}/100</div>
<button class="next-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<!-- Page 5: 奥数挑战 -->
<div v-show="currentPage === 5" class="page-container">
<div v-if="!olympiadDone" class="quiz-container">
<div class="question-card" style="border: 3px solid #8B5CF6;">
<div class="tag tag-purple">🏆 奥数题 {{ currentOlympiadIndex + 1 }}/10</div>
<div class="question-text">{{ olympiadQuestions[currentOlympiadIndex].text }}</div>
<div v-for="(opt, idx) in olympiadQuestions[currentOlympiadIndex].options"
:key="idx"
class="option-btn"
:class="{
'correct': olympiadAnswered && opt.correct,
'wrong': olympiadAnswered && selectedOlympiadOpt === idx && !opt.correct
}"
@click="handleOlympiadAnswer(idx, opt.correct)">
{{ String.fromCharCode(65 + idx) }}. {{ opt.text }}
</div>
<div v-if="olympiadAnswered" class="feedback-box" style="background: #F3E8FF; color: #6B21A8;">
<strong>🎓 深度解析:</strong><br>
<span v-html="olympiadQuestions[currentOlympiadIndex].expl"></span>
</div>
<button v-if="olympiadAnswered" class="next-btn" @click="nextOlympiad">
{{ currentOlympiadIndex < 9 ? '下一题 →' : '查看总结 📚' }}
</button>
</div>
</div>
<div v-else class="card completion-card">
<div class="completion-icon">🥇</div>
<div class="completion-title">奥数挑战通关!</div>
<div class="completion-score">得分:{{ olympiadScore }}/100</div>
<button class="next-btn" @click="switchPage(6)">查看知识总结 →</button>
</div>
</div>
<!-- Page 6: 知识总结 -->
<div v-show="currentPage === 6" class="page-container">
<div class="section-title">📝 知识点总结</div>
<div class="card">
<table class="summary-table">
<tr>
<th>关键词</th>
<th>求什么</th>
<th>质因数法口诀</th>
</tr>
<tr>
<td>剪开、分割<br>分组、最多</td>
<td><strong>最大公约数</strong><br>(GCD)</td>
<td>取同取小<br>选小指数</td>
</tr>
<tr>
<td>相遇、重合<br>至少、再次</td>
<td><strong>最小公倍数</strong><br>(LCM)</td>
<td>取全取大<br>选大指数</td>
</tr>
</table>
</div>
<div class="card">
<div class="tag tag-orange">黄金公式</div>
<div class="formula-box">
A × B = GCD(A,B) × LCM(A,B)
</div>
<p style="font-size: 13px; color: #64748B; text-align: center; margin-top: 10px;">
两个数的乘积 = 它们的最大公约数 × 最小公倍数
</p>
</div>
<div class="card" style="background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);">
<div style="font-size: 16px; font-weight: bold; color: #92400E; margin-bottom: 15px;">🎯 做题三步走</div>
<div style="font-size: 14px; color: #78350F; line-height: 2;">
1️⃣ <strong>看关键词</strong> → 判断求GCD还是LCM<br>
2️⃣ <strong>选方法</strong> → 小数短除法,大数质因数<br>
3️⃣ <strong>验算</strong> → 用公式 A×B = GCD×LCM 检查
</div>
</div>
<div class="card">
<div style="font-size: 16px; font-weight: bold; color: #1E293B; margin-bottom: 10px;">🌟 特殊情况记忆</div>
<div style="font-size: 14px; color: #334155; line-height: 1.8;">
• 互质的两个数:GCD=1,LCM=A×B<br>
• 倍数关系:GCD=较小数,LCM=较大数<br>
• 相邻自然数:一定互质<br>
• 1与任何数:GCD=1,LCM=那个数
</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,
page1Tab: 'gcd',
// 动画状态
demoMode: 'short',
animStep: 0,
totalSteps: 4,
animText: '点击"下一步"开始演示',
// 例题展开状态
activeExample: null,
// 例题数据
examples: [
{
title: '例1:分水果问题(分组)',
question: '有12个苹果和18个橙子,要平均分给尽可能多的小朋友,每人得到的苹果数和橙子数都一样。最多可以分给几个小朋友?',
logic: '关键词"平均分"+"尽可能多" → 求最大公约数GCD',
analysis: '<strong>解:</strong>GCD(12, 18) = 6<br>答:最多可以分给<strong>6个小朋友</strong><br>每人得到:12÷6=2个苹果,18÷6=3个橙子'
},
{
title: '例2:公交车相遇(周期)',
question: '1路车每6分钟发一趟,2路车每8分钟发一趟。早上8:00两车同时发车,下次同时发车是什么时候?',
logic: '关键词"同时"+"再次" → 求最小公倍数LCM',
analysis: '<strong>解:</strong>LCM(6, 8) = 24分钟<br>答:8:00 + 24分钟 = <strong>8:24</strong>再次同时发车'
},
{
title: '例3:剪纸问题(切割)',
question: '长24厘米、宽18厘米的长方形纸,要剪成同样大小的正方形,且没有剩余。正方形的边长最大是多少厘米?能剪几个?',
logic: '关键词"剪成"+"最大" → 求最大公约数GCD',
analysis: '<strong>解:</strong>GCD(24, 18) = 6厘米<br>个数:(24÷6) × (18÷6) = 4 × 3 = 12个<br>答:边长最大<strong>6厘米</strong>,能剪<strong>12个</strong>'
},
{
title: '例4:跑步相遇(倍数关系)',
question: '甲3分钟跑一圈,乙4分钟跑一圈,两人同时同地同向出发,多久后第一次在起点相遇?',
logic: '关键词"第一次相遇"+"起点" → 求最小公倍数LCM',
analysis: '<strong>解:</strong>LCM(3, 4) = 12分钟<br>此时甲跑了12÷3=4圈,乙跑了12÷4=3圈<br>答:<strong>12分钟</strong>后第一次在起点相遇'
},
{
title: '例5:铁丝问题(带余数)',
question: '18米和30米的两根铁丝,都剪成相等的小段,都剩2米。每段最长多少米?',
logic: '先去掉余数!18-2=16,30-2=28,再求GCD',
analysis: '<strong>解:</strong>实际可分的长度:18-2=16米,30-2=28米<br>GCD(16, 28) = 4米<br>答:每段最长<strong>4米</strong>'
}
],
// 基础练习
currentBasicIndex: 0,
basicScore: 0,
basicAnswered: false,
isBasicCorrect: false,
selectedBasicOpt: null,
basicDone: false,
basicQuestions: [
{
text: '求28和42的最大公约数和最小公倍数',
options: [
{text: 'GCD=14, LCM=84', correct: true},
{text: 'GCD=7, LCM=168', correct: false},
{text: 'GCD=14, LCM=168', correct: false},
{text: 'GCD=7, LCM=84', correct: false}
],
expl: '<strong>短除法:</strong><br>2 | 28 42<br>7 | 14 21<br> 2 3<br>GCD = 2×7 = <strong>14</strong><br>LCM = 2×7×2×3 = <strong>84</strong>'
},
{
text: '已知A=2²×3×5,B=2×3²×5,求GCD(A,B)',
options: [
{text: '30', correct: true},
{text: '60', correct: false},
{text: '180', correct: false},
{text: '15', correct: false}
],
expl: '<strong>取同取小:</strong><br>2: 选2¹ (小)<br>3: 选3¹ (小)<br>5: 选5¹ (相同)<br>GCD = 2×3×5 = <strong>30</strong>'
},
{
text: '三个数8、12、16的最小公倍数是',
options: [
{text: '48', correct: true},
{text: '96', correct: false},
{text: '32', correct: false},
{text: '24', correct: false}
],
expl: '<strong>方法一:</strong>16已经是8的倍数,所以LCM(8,16)=16<br>再求LCM(16,12)=48<br><strong>方法二:</strong>8=2³, 12=2²×3, 16=2⁴<br>LCM=2⁴×3=<strong>48</strong>'
},
{
text: '判断:相邻的两个自然数的最大公约数一定是1',
options: [
{text: '正确', correct: true},
{text: '错误', correct: false}
],
expl: '<strong>正确!</strong><br>相邻自然数一定<strong>互质</strong>,没有除1以外的公约数<br>例如:GCD(5,6)=1,GCD(99,100)=1'
},
{
text: '两个数的乘积等于它们的最大公约数与最小公倍数的乘积',
options: [
{text: '正确', correct: true},
{text: '错误', correct: false}
],
expl: '<strong>正确!黄金公式</strong><br>A × B = GCD(A,B) × LCM(A,B)<br>这是验算的利器!'
},
{
text: '路长100米,原来每隔4米栽一棵树,现改为每隔5米栽一棵。有多少棵树不需要移动?',
options: [
{text: '6棵', correct: true},
{text: '5棵', correct: false},
{text: '7棵', correct: false},
{text: '4棵', correct: false}
],
expl: '<strong>解:</strong>不需要移动的树在LCM(4,5)=20米的倍数位置<br>位置:0, 20, 40, 60, 80, 100<br>共<strong>6棵</strong>'
},
{
text: '大齿轮36齿,小齿轮24齿,两轮咬合转动。转过多少个齿后再次回到原位置?',
options: [
{text: '72', correct: true},
{text: '36', correct: false},
{text: '24', correct: false},
{text: '144', correct: false}
],
expl: '<strong>解:</strong>LCM(36, 24) = 72<br>大轮转72÷36=2圈,小轮转72÷24=3圈<br>答:转过<strong>72个齿</strong>'
},
{
text: '男生24人,女生32人,分成若干小组,每组人数相同且最多。每组多少人?',
options: [
{text: '8人', correct: true},
{text: '4人', correct: false},
{text: '16人', correct: false},
{text: '2人', correct: false}
],
expl: '<strong>解:</strong>GCD(24, 32) = 8<br>分组:24÷8=3组男生,32÷8=4组女生<br>答:每组<strong>8人</strong>'
},
{
text: '一个数除3余2,除5也余2,这个数最小是',
options: [
{text: '17', correct: true},
{text: '15', correct: false},
{text: '12', correct: false},
{text: '32', correct: false}
],
expl: '<strong>余数相同,加上余数:</strong><br>LCM(3, 5) + 2 = 15 + 2 = <strong>17</strong><br>验证:17÷3=5...2 ✓,17÷5=3...2 ✓'
},
{
text: '两个数的GCD=6,LCM=72,其中一个数是18,另一个数是',
options: [
{text: '24', correct: true},
{text: '12', correct: false},
{text: '36', correct: false},
{text: '48', correct: false}
],
expl: '<strong>用黄金公式:</strong><br>A × B = GCD × LCM<br>18 × B = 6 × 72<br>B = 432 ÷ 18 = <strong>24</strong>'
}
],
// 奥数挑战
currentOlympiadIndex: 0,
olympiadScore: 0,
olympiadAnswered: false,
selectedOlympiadOpt: null,
olympiadDone: false,
olympiadQuestions: [
{
text: '一个数除3余2,除5余3,除7余4,这个数最小是多少?',
options: [
{text: '53', correct: true},
{text: '23', correct: false},
{text: '158', correct: false},
{text: '83', correct: false}
],
expl: '<strong>中国剩余定理变形</strong><br>观察:3-2=1, 5-3=2, 7-4=3<br>差值不同,需要逐一试算<br>LCM(3,5,7)=105,在105k-2附近找<br>试算得:<strong>53</strong>满足所有条件'
},
{
text: '三个数的GCD=1,前两数之积为24,后两数之积为30,求这三个数',
options: [
{text: '4, 6, 5', correct: true},
{text: '3, 8, 5', correct: false},
{text: '2, 12, 5', correct: false},
{text: '6, 4, 5', correct: false}
],
expl: '<strong>分解推理:</strong><br>中间数是24和30的公约数<br>GCD(24,30)=6<br>第一个数:24÷6=4<br>第三个数:30÷6=5<br>验证GCD(4,6,5)=1 ✓'
},
{
text: '两数之和为50,GCD=5,这两个数的乘积最大是',
options: [
{text: '525', correct: true},
{text: '625', correct: false},
{text: '500', correct: false},
{text: '600', correct: false}
],
expl: '<strong>设两数为5a和5b,其中a,b互质</strong><br>5a+5b=50 → a+b=10<br>互质数对(1,9)(3,7),求最大积<br>15×35=525 > 5×45=225<br>答:<strong>525</strong>'
},
{
text: '甲跑一圈3分钟,乙4分钟,丙6分钟。同时出发,多久后三人同时在起点?甲跑了几圈?',
options: [
{text: '12分钟, 4圈', correct: true},
{text: '24分钟, 8圈', correct: false},
{text: '12分钟, 3圈', correct: false},
{text: '6分钟, 2圈', correct: false}
],
expl: '<strong>三数LCM:</strong><br>LCM(3,4,6)=12分钟<br>甲:12÷3=4圈<br>乙:12÷4=3圈<br>丙:12÷6=2圈'
},
{
text: '爷爷和孙子的年龄都是质数,且互为倍数。若干年后仍是倍数关系,那时是几倍?',
options: [
{text: '2倍', correct: true},
{text: '3倍', correct: false},
{text: '5倍', correct: false},
{text: '不确定', correct: false}
],
expl: '<strong>质数倍数分析:</strong><br>设爷爷=ka,孙子=a(a,k都是质数)<br>若干年后差值不变,倍数↓<br>最后必趋于<strong>2倍</strong>(最小质数倍数)<br>例:爷爷66,孙子6 → 都加54 → 120:60=2:1'
},
{
text: '一个数除199余7,除255余15,求这个除数最大是多少?',
options: [
{text: '24', correct: true},
{text: '48', correct: false},
{text: '16', correct: false},
{text: '12', correct: false}
],
expl: '<strong>带余除法:</strong><br>199-7=192,255-15=240<br>除数是GCD(192,240)的约数,且>15<br>GCD(192,240)=48<br>48的约数:1,2,3,4,6,8,12,16,24,48<br>>15的:16,24,48,最大<strong>24</strong>(验证可除尽)'
},
{
text: '1~100中,与12的最大公约数是4的数有几个?',
options: [
{text: '8个', correct: true},
{text: '10个', correct: false},
{text: '6个', correct: false},
{text: '12个', correct: false}
],
expl: '<strong>GCD(x,12)=4 → x=4k,且GCD(k,3)=1</strong><br>12=4×3<br>k不能有因子3<br>k∈{1,2,4,5,7,8,10,11,13,...,25}<br>4k≤100 → k≤25<br>满足的k:1,2,4,5,7,8,10,11(共8个)'
},
{
text: '两数GCD=4,LCM=252,这样的数对有几组?',
options: [
{text: '2组', correct: true},
{text: '3组', correct: false},
{text: '4组', correct: false},
{text: '1组', correct: false}
],
expl: '<strong>设两数为4a和4b,a,b互质</strong><br>LCM=4ab=252 → ab=63<br>63的互质因数对:(1,63), (7,9)<br>对应数对:(4,252), (28,36)<br>共<strong>2组</strong>'
},
{
text: '甲每5天、乙每9天、丙每12天进城一次。某天三人同时在城里,问几天后再次同时?',
options: [
{text: '180天', correct: true},
{text: '60天', correct: false},
{text: '540天', correct: false},
{text: '360天', correct: false}
],
expl: '<strong>三数LCM:</strong><br>5=5<br>9=3²<br>12=2²×3<br>LCM=2²×3²×5=4×9×5=<strong>180天</strong>'
},
{
text: '两数之积为3600,GCD=12,求两数之和的最小值',
options: [
{text: '120', correct: true},
{text: '312', correct: false},
{text: '240', correct: false},
{text: '180', correct: false}
],
expl: '<strong>设两数12a, 12b,a,b互质</strong><br>12a×12b=3600 → ab=25<br>25=1×25=5×5<br>互质的只有1×25<br>但5×5虽不互质,和更小<br>数对:(60,60)和=120<br>(12,300)和=312<br>答:<strong>120</strong>'
}
]
}
},
computed: {
isMaxStep() {
return this.animStep >= this.totalSteps;
}
},
methods: {
// 语音播报
speak(text) {
if (window.speechSynthesis) window.speechSynthesis.cancel();
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
// 微信环境使用服务器TTS
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(e => {
// 微信需要用户交互后才能播放
if (window.WeixinJSBridge) {
window.WeixinJSBridge.invoke('getNetworkType', {}, () => audio.play());
}
});
} else {
// 非微信环境使用浏览器TTS
if (window.speechSynthesis) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
}
},
speakGCDFull() {
this.speak('最大公约数分三步理解。第一步,约数就是能整除的数,比如12的约数有1、2、3、4、6、12。第二步,公就是大家都有的,12和18的公共约数有1、2、3、6。第三步,最大就是挑最大的,所以答案是6。');
},
// GCD第1步动画:找约数
animateStep1() {
if (typeof gsap === 'undefined') return;
const divisors12 = [1, 2, 3, 4, 6, 12];
const divisors18 = [1, 2, 3, 6, 9, 18];
// 清空之前的内容
document.getElementById('divisors-12').innerHTML = '';
document.getElementById('divisors-18').innerHTML = '';
document.getElementById('apples-demo').innerHTML = '';
document.getElementById('oranges-demo').innerHTML = '';
// 动画显示12的约数
divisors12.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 5px; padding: 4px 8px; background: #BAE6FD; border-radius: 4px; opacity: 0;';
document.getElementById('divisors-12').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0.5, 1], duration: 0.3});
}, index * 300);
});
// 动画显示18的约数
setTimeout(() => {
divisors18.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 5px; padding: 4px 8px; background: #BAE6FD; border-radius: 4px; opacity: 0;';
document.getElementById('divisors-18').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0.5, 1], duration: 0.3});
}, index * 300);
});
}, 2000);
// 显示苹果
for (let i = 0; i < 12; i++) {
const apple = document.createElement('div');
apple.textContent = '🍎';
apple.style.cssText = 'font-size: 20px; opacity: 0;';
document.getElementById('apples-demo').appendChild(apple);
gsap.to(apple, {opacity: 1, duration: 0.2, delay: i * 0.1});
}
// 显示橙子
for (let i = 0; i < 18; i++) {
const orange = document.createElement('div');
orange.textContent = '🍊';
orange.style.cssText = 'font-size: 20px; opacity: 0;';
document.getElementById('oranges-demo').appendChild(orange);
gsap.to(orange, {opacity: 1, duration: 0.2, delay: 2 + i * 0.1});
}
this.speak('第一步,找约数。12的约数有1、2、3、4、6、12。18的约数有1、2、3、6、9、18。');
},
// GCD第2步动画:找公共约数
animateStep2() {
if (typeof gsap === 'undefined') return;
const divisors12 = [1, 2, 3, 4, 6, 12];
const divisors18 = [1, 2, 3, 6, 9, 18];
const common = [1, 2, 3, 6];
// 显示12的约数
let html12 = '';
divisors12.forEach(num => {
const isCommon = common.includes(num);
html12 += `<span style="display: inline-block; margin: 0 5px; padding: 4px 8px; background: ${isCommon ? '#DDD6FE' : '#E5E7EB'}; border-radius: 4px; font-weight: ${isCommon ? 'bold' : 'normal'};">${num}</span>`;
});
document.getElementById('divisors-12-compare').innerHTML = html12;
// 显示18的约数
let html18 = '';
divisors18.forEach(num => {
const isCommon = common.includes(num);
html18 += `<span style="display: inline-block; margin: 0 5px; padding: 4px 8px; background: ${isCommon ? '#DDD6FE' : '#E5E7EB'}; border-radius: 4px; font-weight: ${isCommon ? 'bold' : 'normal'};">${num}</span>`;
});
document.getElementById('divisors-18-compare').innerHTML = html18;
// 清空公共约数
document.getElementById('common-divisors').innerHTML = '';
// 动画显示公共约数
common.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 8px; padding: 6px 12px; background: #A78BFA; color: white; border-radius: 6px; font-size: 18px; opacity: 0;';
document.getElementById('common-divisors').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0, 1.2, 1], duration: 0.5});
}, index * 1000);
});
this.speak('第二步,找公共的。公就是大家都有的。12和18都有的约数是1、2、3、6。');
},
// GCD第3步动画:找最大
animateStep3() {
if (typeof gsap === 'undefined') return;
const common = [1, 2, 3, 6];
// 显示公共约数
let html = '';
common.forEach(num => {
html += `<span style="display: inline-block; margin: 0 8px; padding: 6px 12px; background: #D1FAE5; border: 2px solid ${num === 6 ? '#10B981' : '#A7F3D0'}; border-radius: 6px; font-size: ${num === 6 ? '20px' : '16px'}; font-weight: ${num === 6 ? 'bold' : 'normal'}; color: #047857;">${num}</span>`;
});
document.getElementById('common-divisors-final').innerHTML = html;
// 动画显示最大公约数
document.getElementById('gcd-result-box').textContent = '';
setTimeout(() => {
document.getElementById('gcd-result-box').textContent = '6';
gsap.fromTo('#gcd-result-box',
{scale: 0, rotation: -180},
{scale: 1, rotation: 0, duration: 0.8, ease: 'back.out(1.7)'}
);
}, 500);
this.speak('第三步,选最大的。在1、2、3、6里面,6最大,所以12和18的最大公约数是6。');
},
// 剪彩带动画
animateRibbon() {
if (typeof gsap === 'undefined') return;
const lengths = [
{len: 1, red: 12, blue: 18, text: '剪成1厘米?可以!(太碎了)'},
{len: 2, red: 6, blue: 9, text: '剪成2厘米?可以!'},
{len: 3, red: 4, blue: 6, text: '剪成3厘米?可以!'},
{len: 4, red: 3, blue: -1, text: '剪成4厘米?红的行,蓝的不行(18÷4有余数)'},
{len: 6, red: 2, blue: 3, text: '剪成6厘米?都可以!✓'},
{len: 7, red: -1, blue: -1, text: '剪成7厘米?都不行。所以最长6厘米!'}
];
let currentIndex = 0;
const showCut = () => {
if (currentIndex >= lengths.length) return;
const cut = lengths[currentIndex];
const redCuts = document.getElementById('red-cuts');
const blueCuts = document.getElementById('blue-cuts');
const ribbonText = document.getElementById('ribbon-text');
redCuts.innerHTML = '';
blueCuts.innerHTML = '';
ribbonText.textContent = cut.text;
if (cut.red > 0) {
for (let i = 0; i < cut.red; i++) {
const piece = document.createElement('div');
piece.style.cssText = `flex: 1; height: 100%; background: rgba(251, 191, 36, 0.3); border-right: 2px solid white; opacity: 0;`;
redCuts.appendChild(piece);
gsap.to(piece, {opacity: 1, duration: 0.3, delay: i * 0.1});
}
}
if (cut.blue > 0) {
for (let i = 0; i < cut.blue; i++) {
const piece = document.createElement('div');
piece.style.cssText = `flex: 1; height: 100%; background: rgba(251, 191, 36, 0.3); border-right: 2px solid white; opacity: 0;`;
blueCuts.appendChild(piece);
gsap.to(piece, {opacity: 1, duration: 0.3, delay: i * 0.1});
}
}
if (cut.len === 6) {
gsap.to(ribbonText, {scale: [1, 1.2, 1], duration: 0.5, delay: 0.5});
}
currentIndex++;
if (currentIndex < lengths.length) {
setTimeout(showCut, 2000);
}
};
showCut();
this.speak('我们试着剪彩带。剪1厘米可以但太碎。剪2厘米可以。剪3厘米可以。剪4厘米红的行蓝的不行。剪6厘米都可以。再长就不行了。所以最长能剪6厘米。');
},
speakGCD() {
this.speak('最大公约数就像一个安检员,把多余的东西全部切掉,只留下大家都有的核心零件。比如把18厘米的木条切成相等的小段,最大能切成6厘米一段。');
},
speakLCM() {
this.speak('最小公倍数就像一个超级收集狂,要建一个大仓库把所有人的东西都装进去。比如A每次叠3个,B每次叠2个,第一次一样高的时候有6个。');
},
speakLCMFull() {
this.speak('最小公倍数分三步理解。第一步,倍数就是不停翻倍,比如4的倍数是4、8、12、16。第二步,公就是撞车的数字,4和6都有的倍数是12、24。第三步,最小就是第一次相遇,所以答案是12。');
},
// LCM第1步动画:找倍数
animateLCMStep1() {
if (typeof gsap === 'undefined') return;
const multiples4 = [4, 8, 12, 16, 20, 24];
const multiples6 = [6, 12, 18, 24, 30];
// 清空
document.getElementById('multiples-4').innerHTML = '';
document.getElementById('multiples-6').innerHTML = '';
document.getElementById('tiles-4').innerHTML = '';
document.getElementById('tiles-6').innerHTML = '';
// 动画显示4的倍数
multiples4.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 5px; padding: 4px 8px; background: #DBEAFE; border-radius: 4px; opacity: 0;';
document.getElementById('multiples-4').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0.5, 1], duration: 0.3});
// 添加砖块
const tile = document.createElement('div');
tile.style.cssText = 'width: 30px; height: 25px; background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%); border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-size: 10px; font-weight: bold; opacity: 0; border: 1px solid white;';
tile.textContent = num;
document.getElementById('tiles-4').appendChild(tile);
gsap.to(tile, {opacity: 1, duration: 0.3});
}, index * 400);
});
// 动画显示6的倍数
setTimeout(() => {
multiples6.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 5px; padding: 4px 8px; background: #DBEAFE; border-radius: 4px; opacity: 0;';
document.getElementById('multiples-6').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0.5, 1], duration: 0.3});
// 添加砖块
const tile = document.createElement('div');
tile.style.cssText = 'width: 30px; height: 25px; background: linear-gradient(135deg, #3B82F6 0%, #2563EB 100%); border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-size: 10px; font-weight: bold; opacity: 0; border: 1px solid white;';
tile.textContent = num;
document.getElementById('tiles-6').appendChild(tile);
gsap.to(tile, {opacity: 1, duration: 0.3});
}, index * 400);
});
}, 2500);
this.speak('第一步,找倍数。4的倍数有4、8、12、16、20、24,就像不断铺砖块。6的倍数有6、12、18、24、30,也在不断往前铺。');
},
// LCM第2步动画:找公共倍数
animateLCMStep2() {
if (typeof gsap === 'undefined') return;
const multiples4 = [4, 8, 12, 16, 20, 24];
const multiples6 = [6, 12, 18, 24, 30];
const common = [12, 24];
// 显示4的倍数
let html4 = '';
multiples4.forEach(num => {
const isCommon = common.includes(num);
html4 += `<span style="display: inline-block; margin: 0 5px; padding: 4px 8px; background: ${isCommon ? '#DDD6FE' : '#E5E7EB'}; border-radius: 4px; font-weight: ${isCommon ? 'bold' : 'normal'}; color: ${isCommon ? '#7C3AED' : '#6B7280'};">${num}</span>`;
});
document.getElementById('multiples-4-compare').innerHTML = html4;
// 显示6的倍数
let html6 = '';
multiples6.forEach(num => {
const isCommon = common.includes(num);
html6 += `<span style="display: inline-block; margin: 0 5px; padding: 4px 8px; background: ${isCommon ? '#DDD6FE' : '#E5E7EB'}; border-radius: 4px; font-weight: ${isCommon ? 'bold' : 'normal'}; color: ${isCommon ? '#7C3AED' : '#6B7280'};">${num}</span>`;
});
document.getElementById('multiples-6-compare').innerHTML = html6;
// 清空公共倍数
document.getElementById('common-multiples').innerHTML = '';
// 动画显示公共倍数
common.forEach((num, index) => {
setTimeout(() => {
const span = document.createElement('span');
span.textContent = num;
span.style.cssText = 'display: inline-block; margin: 0 8px; padding: 6px 12px; background: #A78BFA; color: white; border-radius: 6px; font-size: 18px; opacity: 0;';
document.getElementById('common-multiples').appendChild(span);
gsap.to(span, {opacity: 1, scale: [0, 1.2, 1], duration: 0.5});
}, index * 1200);
});
this.speak('第二步,找公共的。公就是撞车的数字。看4和6的倍数,两边都有的是12和24,它们在这两个位置相遇了。');
},
// LCM第3步动画:找最小
animateLCMStep3() {
if (typeof gsap === 'undefined') return;
const common = [12, 24, 36];
// 显示公共倍数,12特别高亮
let html = '';
common.forEach(num => {
if (num === 12) {
html += `<span style="display: inline-block; margin: 0 8px; padding: 8px 14px; background: linear-gradient(135deg, #10B981 0%, #059669 100%); border: 3px solid #FCD34D; border-radius: 8px; font-size: 22px; font-weight: bold; color: white; box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);">${num}</span>`;
} else {
html += `<span style="display: inline-block; margin: 0 8px; padding: 6px 12px; background: #D1FAE5; border: 2px solid #A7F3D0; border-radius: 6px; font-size: 16px; color: #047857;">${num}</span>`;
}
});
document.getElementById('common-multiples-final').innerHTML = html;
// 动画显示最小公倍数
document.getElementById('lcm-result-box').textContent = '';
setTimeout(() => {
document.getElementById('lcm-result-box').textContent = '12';
gsap.fromTo('#lcm-result-box',
{scale: 0, rotation: 180},
{scale: 1, rotation: 0, duration: 0.8, ease: 'back.out(1.7)'}
);
}, 500);
this.speak('第三步,选最小的。在公共倍数12、24、36里面,12是第一次相遇,最小,所以4和6的最小公倍数是12。为什么不说最大公倍数呢?因为倍数可以无限大,永远找不到头,所以只能找最小的。');
},
// 公交车相遇动画
animateBus() {
if (typeof gsap === 'undefined') return;
const times = [0, 2, 4, 6, 8, 10, 12, 14];
const bus4Times = [0, 4, 8, 12];
const bus6Times = [0, 6, 12];
// 清空
document.getElementById('bus-4-schedule').innerHTML = '';
document.getElementById('bus-6-schedule').innerHTML = '';
document.getElementById('bus-meet-text').textContent = '';
// 显示时间轴标记
const marks = document.getElementById('timeline-marks');
marks.innerHTML = '';
times.forEach((time, index) => {
const mark = document.createElement('div');
mark.style.cssText = `position: absolute; left: ${(index / (times.length - 1)) * 100}%; top: -5px; width: 2px; height: 12px; background: #94A3B8;`;
marks.appendChild(mark);
});
let currentStep = 0;
const steps = [
{time: 4, text: '4分钟:只有🔴4路车发车'},
{time: 6, text: '6分钟:只有🔵6路车发车'},
{time: 8, text: '8分钟:只有🔴4路车发车'},
{time: 12, text: '12分钟:两车同时发车!相遇了!🎉'}
];
const showStep = () => {
if (currentStep >= steps.length) return;
const step = steps[currentStep];
document.getElementById('bus-meet-text').textContent = step.text;
// 添加4路车发车标记
if (bus4Times.includes(step.time)) {
const badge = document.createElement('div');
badge.textContent = `${step.time}分🚌`;
badge.style.cssText = 'padding: 5px 10px; background: #FEE2E2; color: #B91C1C; border-radius: 6px; font-size: 13px; font-weight: bold; opacity: 0; border: 2px solid #EF4444;';
document.getElementById('bus-4-schedule').appendChild(badge);
gsap.to(badge, {opacity: 1, scale: [0.5, 1.1, 1], duration: 0.4});
}
// 添加6路车发车标记
if (bus6Times.includes(step.time)) {
const badge = document.createElement('div');
badge.textContent = `${step.time}分🚌`;
badge.style.cssText = 'padding: 5px 10px; background: #DBEAFE; color: #1E40AF; border-radius: 6px; font-size: 13px; font-weight: bold; opacity: 0; border: 2px solid #3B82F6;';
document.getElementById('bus-6-schedule').appendChild(badge);
gsap.to(badge, {opacity: 1, scale: [0.5, 1.1, 1], duration: 0.4});
}
// 12分钟时特效
if (step.time === 12) {
gsap.to('#bus-meet-text', {
scale: [1, 1.2, 1],
color: ['#FCD34D', '#F59E0B', '#FCD34D'],
duration: 0.6,
repeat: 2
});
}
currentStep++;
if (currentStep < steps.length) {
setTimeout(showStep, 2000);
}
};
showStep();
this.speak('两路公交车从起点同时发车。4路车每4分钟一班,6路车每6分钟一班。4分钟时只有4路车,6分钟时只有6路车,8分钟时又是只有4路车,到了12分钟,两辆车同时发车,相遇了!这个12分钟就是最小公倍数。');
},
speakCompare() {
this.speak('记住!GCD和LCM有三大区别。第一,动作不同,GCD是切分变小,LCM是拼凑变大。第二,关键词不同,看到切、分、剪、最多就求GCD,看到遇、至少、再次、同时就求LCM。第三,用途不同,GCD用来分数约分,LCM用来分数通分。还有一个黄金公式,两个数的乘积等于它们的最大公约数乘最小公倍数。');
},
// 页面切换
switchPage(page) {
if (window.speechSynthesis) window.speechSynthesis.cancel();
this.currentPage = page;
window.scrollTo(0, 0);
if (page === 1) {
this.$nextTick(() => {
this.startPage1Animations();
});
} else if (page === 2) {
this.setDemoMode('short');
}
},
// Page 1 动画
startPage1Animations() {
if (typeof gsap === 'undefined') return;
// GCD切割动画
if (this.page1Tab === 'gcd') {
gsap.to('#gcd-cutter', {
top: 60,
duration: 0.8,
repeat: -1,
yoyo: true,
repeatDelay: 1.5,
ease: 'power2.inOut'
});
gsap.to('#gcd-pieces', {
opacity: 1,
duration: 0.5,
delay: 0.8,
repeat: -1,
repeatDelay: 2.3
});
}
// LCM堆叠动画
if (this.page1Tab === 'lcm') {
const tl = gsap.timeline({repeat: -1, repeatDelay: 2});
tl.to('#lcm-a2', {opacity: 1, duration: 0.3})
.to('#lcm-b2', {opacity: 1, duration: 0.3}, '-=0.1')
.to('#lcm-a3', {opacity: 1, duration: 0.3})
.to({}, {duration: 1.5}); // pause
}
},
// Page 2 动画控制
setDemoMode(mode) {
this.demoMode = mode;
this.animStep = 0;
this.totalSteps = mode === 'short' ? 4 : 4;
this.animText = '点击"下一步"开始演示';
if (typeof gsap !== 'undefined') {
gsap.killTweensOf('*');
if (mode === 'short') {
gsap.set('#sd-row1, #sd-row2, #sd-bottom', {opacity: 0});
gsap.set('#hl-gcd, #hl-lcm', {opacity: 0});
} else {
gsap.set('.prime-item', {borderColor: 'transparent', scale: 1, opacity: 1});
gsap.set('.prime-result', {opacity: 0});
}
}
},
nextStep() {
if (this.animStep < this.totalSteps) {
this.animStep++;
this.renderAnimation();
}
},
prevStep() {
if (this.animStep > 0) {
this.animStep--;
this.renderAnimation();
}
},
resetDemo() {
this.setDemoMode(this.demoMode);
},
renderAnimation() {
if (typeof gsap === 'undefined') return;
if (this.demoMode === 'short') {
this.renderShortDivision();
} else if (this.demoMode === 'prime_gcd') {
this.renderPrimeGCD();
} else if (this.demoMode === 'prime_lcm') {
this.renderPrimeLCM();
}
},
renderShortDivision() {
const step = this.animStep;
gsap.set('#sd-row1, #sd-row2, #sd-bottom, #hl-gcd, #hl-lcm', {opacity: 0});
if (step >= 1) {
this.animText = '第1步:12和18都是偶数,用质数2整除';
gsap.to('#sd-row1', {opacity: 1, duration: 0.5});
this.speak('12和18都是偶数,先用质数2整除');
}
if (step >= 2) {
this.animText = '第2步:6和9都是3的倍数,继续用质数3整除';
gsap.to('#sd-row2', {opacity: 1, duration: 0.5, delay: 0.5});
this.speak('6和9都是3的倍数,再用质数3整除');
}
if (step >= 3) {
this.animText = '第3步:2和3互质,停止。底部是最终结果';
gsap.to('#sd-bottom', {opacity: 1, duration: 0.5, delay: 1});
this.speak('2和3互质了,不能再除了');
}
if (step >= 4) {
this.animText = 'GCD=左边乘(2×3=6),LCM=L型乘(2×3×2×3=36)';
// 高亮GCD (I型)
gsap.fromTo('#hl-gcd',
{top: 30, left: 15, width: 45, height: 95},
{opacity: 1, duration: 0.5, delay: 1.5}
);
// 高亮LCM (L型)
gsap.fromTo('#hl-lcm',
{top: 30, left: 15, width: 180, height: 125},
{opacity: 1, duration: 0.5, delay: 2}
);
this.speak('左边一列2乘3等于6是最大公约数,L型全部乘起来2乘3乘2乘3等于36是最小公倍数');
}
},
renderPrimeGCD() {
const step = this.animStep;
gsap.set('.prime-item', {borderColor: 'transparent', scale: 1, opacity: 1});
gsap.set('#gcd-result', {opacity: 0});
if (step >= 1) {
this.animText = '对比底数2:A有3个,B有2个 → 选小的2²';
gsap.to('#pf-gcd-b2', {borderColor: '#FCD34D', scale: 1.15, duration: 0.3});
gsap.to('#pf-gcd-a2', {opacity: 0.4, duration: 0.3});
this.speak('看底数2,谁穷听谁的,B比较穷,选2的2次方');
}
if (step >= 2) {
this.animText = '对比底数3:A有2个,B有3个 → 选小的3²';
gsap.to('#pf-gcd-a3', {borderColor: '#FCD34D', scale: 1.15, duration: 0.3, delay: 0.5});
gsap.to('#pf-gcd-b3', {opacity: 0.4, duration: 0.3, delay: 0.5});
this.speak('看底数3,A比较穷,选3的2次方');
}
if (step >= 3) {
this.animText = '5只有A有,不是共有的 → 剔除';
gsap.to('#pf-gcd-a5', {opacity: 0.3, scale: 0.9, duration: 0.3, delay: 1});
this.speak('5只有A有,不是大家都有的,不能要');
}
if (step >= 4) {
this.animText = '最终结果:GCD = 2² × 3² = 4 × 9 = 36';
document.getElementById('gcd-result').textContent = 'GCD = 2² × 3² = 36';
gsap.to('#gcd-result', {opacity: 1, scale: [0.8, 1], duration: 0.5, delay: 1.5});
this.speak('所以最大公约数等于2的2次方乘3的2次方等于36');
}
},
renderPrimeLCM() {
const step = this.animStep;
gsap.set('.prime-item', {borderColor: 'transparent', scale: 1, opacity: 1});
gsap.set('#lcm-result', {opacity: 0});
if (step >= 1) {
this.animText = '对比底数2:A有3个,B有2个 → 选大的2³';
gsap.to('#pf-lcm-a2', {borderColor: '#10B981', scale: 1.15, duration: 0.3});
gsap.to('#pf-lcm-b2', {opacity: 0.4, duration: 0.3});
this.speak('看底数2,谁富听谁的,A比较富,选2的3次方');
}
if (step >= 2) {
this.animText = '对比底数3:A有2个,B有3个 → 选大的3³';
gsap.to('#pf-lcm-b3', {borderColor: '#10B981', scale: 1.15, duration: 0.3, delay: 0.5});
gsap.to('#pf-lcm-a3', {opacity: 0.4, duration: 0.3, delay: 0.5});
this.speak('看底数3,B比较富,选3的3次方');
}
if (step >= 3) {
this.animText = '5只有A有 → 见者有份,收进来!';
gsap.to('#pf-lcm-a5', {borderColor: '#10B981', scale: 1.15, duration: 0.3, delay: 1});
this.speak('5虽然只有A有,但见者有份,也要收进来');
}
if (step >= 4) {
this.animText = '最终结果:LCM = 2³ × 3³ × 5 = 1080';
document.getElementById('lcm-result').textContent = 'LCM = 2³ × 3³ × 5 = 1080';
gsap.to('#lcm-result', {opacity: 1, scale: [0.8, 1], duration: 0.5, delay: 1.5});
this.speak('所以最小公倍数等于2的3次方乘3的3次方乘5等于1080');
}
},
// 例题展开
toggleExample(index) {
this.activeExample = this.activeExample === index ? null : index;
},
// 基础练习
handleBasicAnswer(index, correct) {
if (this.basicAnswered) return;
this.basicAnswered = true;
this.isBasicCorrect = correct;
this.selectedBasicOpt = index;
if (correct) {
this.basicScore += 10;
if (typeof confetti !== 'undefined') {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
}
}
},
nextBasic() {
if (this.currentBasicIndex < 9) {
this.currentBasicIndex++;
this.basicAnswered = false;
this.selectedBasicOpt = null;
} else {
this.basicDone = true;
}
},
// 奥数挑战
handleOlympiadAnswer(index, correct) {
if (this.olympiadAnswered) return;
this.olympiadAnswered = true;
this.selectedOlympiadOpt = index;
if (correct) {
this.olympiadScore += 10;
if (typeof confetti !== 'undefined') {
confetti({
particleCount: 150,
spread: 90,
colors: ['#8B5CF6', '#7C3AED', '#A78BFA']
});
}
}
},
nextOlympiad() {
if (this.currentOlympiadIndex < 9) {
this.currentOlympiadIndex++;
this.olympiadAnswered = false;
this.selectedOlympiadOpt = null;
} else {
this.olympiadDone = true;
}
}
},
watch: {
page1Tab() {
if (this.currentPage === 1) {
this.$nextTick(() => {
gsap.killTweensOf('*');
this.startPage1Animations();
});
}
}
},
mounted() {
this.startPage1Animations();
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码