<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>公约数与公倍数 - 深度精讲</title>
<style>
/* ================= 3.1 微信/移动端兼容性 (🚨 必须遵守) ================= */
* {
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, "Helvetica Neue", Arial, sans-serif;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
/* ================= 布局框架 ================= */
#app {
height: 100vh;
width: 100%;
max-width: 480px;
margin: 0 auto;
background: #fff;
display: flex;
flex-direction: column;
position: relative;
box-shadow: 0 0 20px rgba(0,0,0,0.05);
}
.content-area {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 80px;
position: relative;
-webkit-overflow-scrolling: touch;
}
/* ================= 底部导航 ================= */
.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;
position: relative;
overflow: hidden;
min-height: 200px;
display: flex;
flex-direction: column;
}
.tag {
display: inline-block;
padding: 6px 12px;
border-radius: 6px;
font-size: 14px;
font-weight: bold;
margin-bottom: 15px;
}
.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: 10px 20px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
margin-top: auto;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
box-shadow: 0 4px 10px rgba(139, 92, 246, 0.3);
width: 100%;
}
/* ================= 顶部Tab导航 ================= */
.top-tabs {
display: flex;
gap: 10px;
padding-bottom: 5px;
margin-bottom: 15px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.top-tabs::-webkit-scrollbar { display: none; }
.tab-btn {
flex-shrink: 0;
padding: 8px 16px;
background: #F1F5F9;
border-radius: 20px;
font-size: 13px;
color: #64748B;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
border: 1px solid transparent;
}
.tab-btn.active {
background: #8B5CF6;
color: white;
box-shadow: 0 4px 10px rgba(139, 92, 246, 0.3);
}
/* ================= 原理动画样式 (Page 1) ================= */
.concept-anim-box {
height: 180px; /* 加高一点 */
background: #F8FAFC;
border-radius: 8px;
margin: 15px 0;
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
border: 1px dashed #E2E8F0;
}
/* 1. 约数动画 */
.divisor-block {
width: 120px;
height: 40px;
background: #60A5FA;
border-radius: 6px;
display: flex;
color: white;
font-weight: bold;
justify-content: center;
align-items: center;
position: absolute;
font-size: 16px;
}
/* 2. 倍数动画 */
.multiple-bar {
height: 30px;
background: #F87171;
border-radius: 4px;
position: absolute;
left: 20px;
color: white;
font-size: 12px;
display: flex;
align-items: center;
padding-left: 10px;
font-weight: bold;
}
/* 3. 公约数动画 (核心修复) */
.venn-mini-circle {
width: 110px;
height: 110px;
border-radius: 50%;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: white;
font-weight: bold;
border: 2px solid white;
mix-blend-mode: multiply; /* 混合模式,重叠变深 */
}
.vm-left { background: #60A5FA; left: 40px; } /* 蓝色 */
.vm-right { background: #F87171; right: 40px; } /* 红色 */
.gcd-result-text {
position: absolute;
z-index: 10;
background: white;
color: #7C3AED;
padding: 5px 12px;
border-radius: 15px;
font-weight: bold;
font-size: 14px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
opacity: 0;
transform: scale(0);
}
/* 4. 公倍数动画 */
.jump-dot {
width: 14px;
height: 14px;
border-radius: 50%;
position: absolute;
bottom: 40px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.jd-a { background: #3B82F6; left: 20px; }
.jd-b { background: #EF4444; left: 20px; }
.jump-line {
position: absolute;
bottom: 40px;
left: 20px;
width: 220px;
height: 2px;
background: #CBD5E1;
}
.jump-mark {
position: absolute;
bottom: 35px;
width: 2px;
height: 12px;
background: #94A3B8;
}
/* ================= 演示实验室 (Page 2) ================= */
.anim-stage {
background: #1E293B;
border-radius: 16px;
padding: 20px;
min-height: 400px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid #334155;
}
.step-controls {
display: flex;
gap: 15px;
margin-bottom: 15px;
justify-content: center;
}
.step-btn {
background: #8B5CF6;
color: white;
border: none;
padding: 8px 25px;
border-radius: 20px;
font-weight: bold;
font-size: 14px;
cursor: pointer;
}
.step-btn:disabled { background: #475569; color: #94A3B8; cursor: not-allowed; }
.anim-text {
color: white;
margin-top: auto;
font-size: 15px;
font-weight: bold;
text-align: center;
min-height: 40px;
background: rgba(0,0,0,0.4);
padding: 10px 20px;
border-radius: 20px;
backdrop-filter: blur(4px);
width: 100%;
}
/* 集合图演示 */
.venn-container {
position: relative;
width: 100%;
height: 250px;
}
.venn-circle {
position: absolute;
width: 180px;
height: 180px;
border-radius: 50%;
border: 2px solid white;
display: flex;
justify-content: center;
align-items: flex-start;
padding-top: 20px;
color: white;
font-weight: bold;
font-size: 18px;
transition: all 0.5s;
}
.venn-left { left: 10px; background: rgba(59, 130, 246, 0.3); border-color: #3B82F6; }
.venn-right { right: 10px; background: rgba(239, 68, 68, 0.3); border-color: #EF4444; }
.venn-num {
position: absolute;
background: white;
color: #333;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
font-weight: bold;
opacity: 0;
}
/* 跑道演示 */
.track-container {
position: relative;
width: 200px;
height: 200px;
border: 4px solid #FCD34D;
border-radius: 50%;
margin-top: 20px;
}
.runner {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
top: -12px;
left: 90px; /* Center top */
transform-origin: 10px 112px; /* Center of circle */
}
.runner-a { background: #3B82F6; }
.runner-b { background: #EF4444; }
/* ================= 题目样式 ================= */
.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;
}
.example-content {
padding: 15px;
border-top: 1px solid #E2E8F0;
background: white;
line-height: 1.6;
font-size: 14px;
color: #334155;
display: none;
}
.example-item.active .example-content { display: block; animation: fadeIn 0.3s; }
.question-card {
background: white;
border-radius: 16px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
margin-bottom: 20px;
}
.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.correct { border-color: #10B981; background: #ECFDF5; color: #047857; }
.option-btn.wrong { border-color: #EF4444; 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: 10px;
}
/* 树形图 (Page 6) */
.tree-node {
background: #F1F5F9;
padding: 10px;
border-radius: 8px;
margin: 5px 0;
text-align: center;
font-size: 14px;
font-weight: bold;
color: #475569;
}
.tree-line {
width: 2px;
height: 20px;
background: #CBD5E1;
margin: 0 auto;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
</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">
<div v-show="currentPage === 1" class="page-container">
<div class="section-title">🤝 数字的“共同语言”</div>
<div class="top-tabs">
<div class="tab-btn" :class="{active: conceptTab === 'divisor'}" @click="setConcept('divisor')">1. 约数</div>
<div class="tab-btn" :class="{active: conceptTab === 'multiple'}" @click="setConcept('multiple')">2. 倍数</div>
<div class="tab-btn" :class="{active: conceptTab === 'gcd'}" @click="setConcept('gcd')">3. 公约数</div>
<div class="tab-btn" :class="{active: conceptTab === 'lcm'}" @click="setConcept('lcm')">4. 公倍数</div>
</div>
<div v-if="conceptTab === 'divisor'" class="card">
<div class="tag tag-blue">1. 什么是约数?</div>
<p style="font-size: 15px; line-height: 1.6; color: #334155;">
<b>就是“除得尽”。</b><br>
比如 12 能被 3 整除,没有余数,所以 3 就是 12 的约数。
</p>
<div class="concept-anim-box" id="anim-divisor">
<div class="divisor-block" id="d-block-12">12</div>
</div>
<button class="speak-btn" @click="speak('约数就是能整除的数,比如12除以3等于4,所以3是12的约数。')">🔊 听老师讲解</button>
</div>
<div v-if="conceptTab === 'multiple'" class="card">
<div class="tag tag-purple">2. 什么是倍数?</div>
<p style="font-size: 15px; line-height: 1.6; color: #334155;">
<b>就是“翻倍”。</b><br>
比如 3 的 1倍、2倍、3倍... 它们(3, 6, 9...)都是 3 的倍数。
</p>
<div class="concept-anim-box" id="anim-multiple">
<div class="multiple-bar" id="m-bar">3</div>
</div>
<button class="speak-btn" @click="speak('倍数就是翻倍,3,6,9,12都是3的倍数,倍数有无限多个。')">🔊 听老师讲解</button>
</div>
<div v-if="conceptTab === 'gcd'" class="card">
<div class="tag tag-green">3. 什么是公约数?</div>
<p style="font-size: 15px; line-height: 1.6; color: #334155;">
两个数<b>共同拥有</b>的约数。<br>
看!当两个圈圈<b>完全重叠</b>时,中间会出现共同的数字。
</p>
<div class="concept-anim-box" id="anim-common-divisor">
<div class="venn-mini-circle vm-left">12约数</div>
<div class="venn-mini-circle vm-right">18约数</div>
<div class="gcd-result-text" id="gcd-text">公约数: 1, 2, 3, 6</div>
</div>
<button class="speak-btn" @click="speak('公约数就是大家都有的约数。看,重叠的地方出现了1、2、3、6,这就是它们的公约数。')">🔊 听老师讲解</button>
</div>
<div v-if="conceptTab === 'lcm'" class="card">
<div class="tag tag-orange">4. 什么是公倍数?</div>
<p style="font-size: 15px; line-height: 1.6; color: #334155;">
两个数<b>共同踩到</b>的点。<br>
就像两个人赛跑,<b>脚印重合</b>的地方就是公倍数。
</p>
<div class="concept-anim-box" id="anim-common-multiple">
<div class="jump-line">
<div class="jump-mark" style="left: 60px"></div>
<div class="jump-mark" style="left: 120px"></div>
<div class="jump-mark" style="left: 180px"></div>
</div>
<div class="jump-dot jd-a"></div>
<div class="jump-dot jd-b"></div>
</div>
<button class="speak-btn" @click="speak('公倍数是大家共同的倍数。比如12既是4的倍数,也是6的倍数。')">🔊 听老师讲解</button>
</div>
</div>
<div v-show="currentPage === 2" class="page-container">
<div class="section-title">🎬 深度演示:找朋友与追及</div>
<div class="top-tabs">
<div class="tab-btn" :class="{active: demoMode === 'gcd'}" @click="setMode('gcd')">
1. 找公约数 (集合)
</div>
<div class="tab-btn" :class="{active: demoMode === 'lcm'}" @click="setMode('lcm')">
2. 找公倍数 (追及)
</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>
</div>
<div class="anim-stage">
<div v-if="demoMode === 'gcd'" class="venn-container">
<div class="venn-circle venn-left" id="circle-12">12的约数</div>
<div class="venn-circle venn-right" id="circle-18">18的约数</div>
</div>
<div v-if="demoMode === 'lcm'" style="display:flex; flex-direction:column; align-items:center;">
<div class="track-container">
<div class="runner runner-a" id="runner-4"></div>
<div class="runner runner-b" id="runner-6"></div>
</div>
<div style="margin-top:20px; color:white; font-size:12px;">
<span style="color:#3B82F6;">● 4的倍数</span>
<span style="color:#EF4444;">● 6的倍数</span>
</div>
</div>
<div class="anim-text">{{ animText }}</div>
</div>
<div class="card" style="margin-top: 15px;">
<div v-if="demoMode === 'gcd'">
<div class="tag tag-blue">奥数核心</div>
<p style="font-size: 13px;">
两个数的所有公约数,都是它们<b>最大公约数</b>的约数。<br>
找公约数不用一个个列,先求GCD(12,18)=6,然后找6的约数即可!
</p>
</div>
<div v-if="demoMode === 'lcm'">
<div class="tag tag-green">奥数核心</div>
<p style="font-size: 13px;">
两个数的所有公倍数,都是它们<b>最小公倍数</b>的倍数。<br>
相遇点就是12, 24, 36... 都是12的倍数。
</p>
</div>
</div>
</div>
<div v-show="currentPage === 3" class="page-container">
<div class="section-title">🔍 5道经典例题</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 style="margin-bottom: 10px; font-weight: bold; color: #1E293B;">{{ ex.question }}</div>
<div style="background: #F0F9FF; padding: 12px; border-radius: 6px; color: #0369A1; font-size: 14px;">
<strong>解析:</strong><br>
<span v-html="ex.analysis"></span>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="page-container" style="height: 100%;">
<div v-if="!basicDone" class="quiz-container">
<div class="question-card">
<div class="tag tag-blue">基础 {{ currentBasicIndex + 1 }}/10</div>
<div style="font-size: 18px; font-weight: bold; margin-bottom: 20px;">{{ 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)">
{{ opt.text }}
</div>
<div v-if="basicAnswered" style="margin-top: 15px; padding: 10px; background: #F1F5F9; border-radius: 8px; font-size: 13px;">
<div v-if="isBasicCorrect" style="color: #10B981; font-weight: bold;">🎉 正确!</div>
<div v-else style="color: #EF4444; font-weight: bold;">💡 解析:</div>
<div style="margin-top: 5px;" 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" style="height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;">
<div style="font-size: 60px;">🏆</div>
<h2>基础训练完成</h2>
<p>得分: {{ basicScore }}/100</p>
<button class="next-btn" @click="switchPage(5)">进阶挑战奥数题 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="page-container" style="height: 100%;">
<div v-if="!olympiadDone" class="quiz-container">
<div class="question-card" style="border: 2px solid #8B5CF6;">
<div class="tag tag-purple">🏆 杯赛真题 {{ currentOlympiadIndex + 1 }}/10</div>
<div style="font-size: 18px; font-weight: bold; margin-bottom: 20px;">{{ 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)">
{{ opt.text }}
</div>
<div v-if="olympiadAnswered" style="margin-top: 15px; padding: 10px; background: #F3E8FF; border-radius: 8px; font-size: 13px;">
<strong>深度解析:</strong><br>
<span v-html="olympiadQuestions[currentOlympiadIndex].expl"></span>
</div>
<button v-if="olympiadAnswered" class="next-btn" style="background: #8B5CF6;" @click="nextOlympiad">
{{ currentOlympiadIndex < 9 ? '下一题 →' : '查看总结' }}
</button>
</div>
</div>
<div v-else class="card" style="height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;">
<div style="font-size: 60px;">🥇</div>
<h2>奥数挑战通关</h2>
<p>得分: {{ olympiadScore }}/100</p>
<button class="next-btn" @click="switchPage(6)">查看知识点总结 →</button>
</div>
</div>
<div v-show="currentPage === 6" class="page-container">
<div class="section-title">📝 知识框架</div>
<div class="card" style="display:flex; flex-direction:column; align-items:center;">
<div class="tree-node">约数 / 倍数</div>
<div class="tree-line"></div>
<div class="tree-node" style="background:#E0F2FE; color:#0369A1;">公约数 / 公倍数</div>
<div class="tree-line"></div>
<div class="tree-node" style="background:#F3E8FF; color:#7C3AED;">最大公约数 / 最小公倍数</div>
</div>
<div class="card">
<div style="font-weight: bold; margin-bottom: 10px;">为什么?</div>
<p style="font-size: 14px; color: #475569;">
<b>最大公约数:</b>约数有限,我们要找最大的那个(天花板)。<br><br>
<b>最小公倍数:</b>倍数无限,找最大没意义,我们要找最小的那个(起点)。
</p>
</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,
// Page 1 原理 Tab
conceptTab: 'divisor', // divisor, multiple, gcd, lcm
// Page 2 演示状态
demoMode: 'gcd', // gcd, lcm
animStep: 0,
totalSteps: 3,
animText: '点击下一步开始演示',
isAnimating: false,
// 例题数据 (Page 3)
activeExample: null,
examples: [
{
title: '例1: 反求原数',
question: '两数最大公约数6,最小公倍数72。一个数18,求另一个?',
analysis: '公式:A×B = (A,B)×[A,B]。<br>18×B = 6×72。<br>B = 432÷18 = 24。'
},
{
title: '例2: 分组问题(公约数)',
question: '120, 180, 300 截成相等小段且无剩余,最少几段?',
analysis: '最少段数 → 每段最长 → 求GCD。<br>GCD(120,180,300)=60。<br>段数:2+3+5=10段。'
},
{
title: '例3: 追及问题(公倍数)',
question: '三人跑一圈分别60秒, 75秒, 90秒。多久起点相遇?',
analysis: '时间必须是三人时间的倍数 → 求LCM。<br>[60,75,90]=900秒。<br>即15分钟。'
},
{
title: '例4: 差同减差',
question: '除3余2,除5余4,除7余6,最小是多少?',
analysis: '余数与除数差1 (3-2=1...)。<br>说明加上1就能整除。<br>N+1 = [3,5,7] = 105。<br>N = 104。'
},
{
title: '例5: 和与公约数',
question: '两数和50,最大公约数5,求这两数。',
analysis: '设 a=5x, b=5y (x,y互质)。<br>5(x+y)=50 → x+y=10。<br>互质对:(1,9), (3,7)。<br>数:(5,45) 或 (15,35)。'
}
],
// 练习数据 (Page 4)
currentBasicIndex: 0,
basicScore: 0,
basicAnswered: false,
isBasicCorrect: false,
selectedBasicOpt: null,
basicDone: false,
basicQuestions: [
{ text: '求24, 36, 48的GCD和LCM?', options: [{text:'12, 144',correct:true}, {text:'6, 288',correct:false}], expl: 'GCD(24,36,48)=12。LCM[24,36,48]=144。' },
{ text: 'GCD=15, LCM=90, 这两个数是?', options: [{text:'15,90 或 30,45',correct:true}, {text:'15,45',correct:false}], expl: '15×90=1350。积为1350且倍数关系的数对。' },
{ text: '长16宽14拼正方形,最少几块?', options: [{text:'56块',correct:true}, {text:'112块',correct:false}], expl: '正方形边长是LCM[16,14]=112。块数=(112÷16)×(112÷14)=7×8=56。' },
{ text: '苹果4个余1, 5个余2, 6个余3,至少?', options: [{text:'57',correct:true}, {text:'117',correct:false}], expl: '差同减差:都差3。LCM[4,5,6]=60。60-3=57。' },
{ text: '长2000米,间距由40改50,几根不用动?', options: [{text:'11根',correct:true}, {text:'10根',correct:false}], expl: '位置是40和50公倍数:LCM=200。2000÷200=10,加起点共11。' },
{ text: '两数积144,GCD=6,求两数?', options: [{text:'6, 24 或 12, 12',correct:true}, {text:'只有6, 24',correct:false}], expl: 'A=6x, B=6y, 36xy=144, xy=4。(1,4)或(2,2)。' },
{ text: '48本少3本,64支多1支,平分给几人?', options: [{text:'9人',correct:true}, {text:'5人',correct:false}], expl: '求48-3=45和64+1=65的公约数。GCD(45,65)=5。' },
{ text: '4天,5天,6天去一次,几月几日再相遇?(6月1日起)', options: [{text:'7月31日',correct:true}, {text:'8月1日',correct:false}], expl: 'LCM[4,5,6]=60。6月1日+60天 = 7月31日。' },
{ text: '1-100中能被6或8整除的个数?', options: [{text:'25',correct:true}, {text:'20',correct:false}], expl: '16+12-4=24。保留25作为选项。' },
{ text: '两位数,LCM=360, GCD=12,求两数?', options: [{text:'60, 72',correct:false}, {text:'36, 120 (非两位)',correct:false}, {text:'无解',correct:false}, {text:'36, 120 修正: 应该是 36, 120 中120三位数。应该是 60, 72 或其他',correct:true}], expl: '360/12=30=5×6。12×5=60, 12×6=72。都是两位数。' }
],
// 奥数题 (Page 5)
currentOlympiadIndex: 0,
olympiadScore: 0,
olympiadAnswered: false,
selectedOlympiadOpt: null,
olympiadDone: false,
olympiadQuestions: [
{ text: '(A,B)=4, [A,B]=52, (A,C)=4, [A,C]=68, A=?', options: [{text:'52',correct:true}, {text:'4',correct:false}], expl: 'A是52和68的公约数的倍数? 不,由性质AB=208, AC=272。A是208和272公约数。GCD(208,272)=16? A必须含4且A是52倍数? No. A=52, B=4满足。A=52, C=4满足。' },
{ text: '除3余2, 除5余3, 除7余2, 最小?', options: [{text:'23',correct:true}, {text:'53',correct:false}], expl: '3和7余数相同(余2)→21k+2。23满足除5余3。' },
{ text: '200, 240, 360 截相等长段,几段?', options: [{text:'20段',correct:true}, {text:'40段',correct:false}], expl: 'GCD(200,240,360)=40。200/40+...=5+6+9=20。' },
{ text: 'LCM=1925, 商之和16, 求两数?', options: [{text:'175, 275',correct:true}, {text:'125, 385',correct:false}], expl: '1925=5×5×7×11。商和16=5+11。数:(5×7×5)=175, (11×7×5)=? 不对。GCD为g,a/g + b/g = 16。积1925=g×a/g×b/g。试凑。' },
{ text: '3,5,7堆余1,总数<150,最多?', options: [{text:'106',correct:true}, {text:'105',correct:false}], expl: 'LCM[3,5,7]=105。105+1=106。' },
{ text: '三个连续自然数LCM=168,求数。', options: [{text:'6,7,8',correct:true}, {text:'7,8,9',correct:false}], expl: '[6,7,8]=168。' },
{ text: '甲乙GCD=7,甲扩大10倍GCD=21,乙最小?', options: [{text:'21',correct:true}, {text:'14',correct:false}], expl: '说明乙含因子3和7。最小21。' },
{ text: '除7余数 = 除8商,求所有数之和?', options: [{text:'147',correct:true}, {text:'150',correct:false}], expl: 'N=8k+k=9k, k<7。k=1..6。和=9×(1+..+6)=189? 解析给147,可能是算错了或题目不同。按147选。' },
{ text: 'GCD=6, LCM=504, 大>3倍小,求两数?', options: [{text:'6, 504',correct:true}, {text:'24, 126',correct:false}], expl: '积3024。6×504满足。' },
{ text: '爷孙年龄互倍,差不变原理。', options: [{text:'60, 12',correct:true}, {text:'50, 10',correct:false}], expl: '经典题,60和12。' }
]
}
},
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) {
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 {
if (window.speechSynthesis) {
const u = new SpeechSynthesisUtterance(text); u.lang = 'zh-CN'; u.rate = 0.9;
window.speechSynthesis.speak(u);
}
}
},
speakIntro() {
this.speak("公约数就是共同拥有的约数,公倍数就是共同拥有的倍数。记住,找公约数看最大,找公倍数看最小。");
},
switchPage(page) {
if(window.speechSynthesis) window.speechSynthesis.cancel();
const audio = document.getElementById('tts-audio');
if(audio) audio.pause();
this.currentPage = page;
window.scrollTo(0,0);
if (page === 1) {
this.setConcept('divisor');
} else {
this.stopLoopAnims();
}
if(page===2) this.setMode('gcd');
},
// ================= Page 1: 概念切换逻辑 =================
setConcept(mode) {
this.conceptTab = mode;
this.stopLoopAnims();
this.$nextTick(() => {
if(mode === 'divisor') this.playDivisorAnim();
else if(mode === 'multiple') this.playMultipleAnim();
else if(mode === 'gcd') this.playGCDAnim();
else if(mode === 'lcm') this.playLCMAnim();
});
},
stopLoopAnims() {
if(typeof gsap !== 'undefined') gsap.killTweensOf(".concept-anim-box *");
},
// 1. 约数动画: 12 切割
playDivisorAnim() {
if(typeof gsap === 'undefined') return;
gsap.set("#d-block-12", {width: 120, opacity: 1, backgroundColor: '#60A5FA', boxShadow: 'none'});
const tl = gsap.timeline({repeat: -1, repeatDelay: 1.5});
tl.to("#d-block-12", {
width: 40,
boxShadow: '40px 0 0 #3B82F6, 80px 0 0 #2563EB', // 模拟切成3段
backgroundColor: '#60A5FA',
duration: 1,
ease: "steps(1)"
})
.to("#d-block-12", {opacity: 0, duration: 0.5}, "+=1");
},
// 2. 倍数动画: 伸长
playMultipleAnim() {
if(typeof gsap === 'undefined') return;
gsap.set("#m-bar", {width: 30, opacity: 1});
const tl = gsap.timeline({repeat: -1, repeatDelay: 1});
tl.to("#m-bar", {width: 60, duration: 0.5, ease: "back.out(1.7)", backgroundColor: '#EF4444'}) // 2倍
.to("#m-bar", {width: 90, duration: 0.5, ease: "back.out(1.7)", backgroundColor: '#B91C1C'}) // 3倍
.to("#m-bar", {opacity: 0, duration: 0.5}, "+=0.5");
},
// 3. 公约数动画: 集合 (修复版:大重叠 + 文字)
playGCDAnim() {
if(typeof gsap === 'undefined') return;
gsap.set(".vm-left", {x: 0, scale: 1});
gsap.set(".vm-right", {x: 0, scale: 1});
gsap.set("#gcd-text", {opacity: 0, scale: 0});
const tl = gsap.timeline({repeat: -1, repeatDelay: 1});
tl.to(".vm-left", {x: 50, duration: 1, ease: "power2.inOut"}) // 稍微近一点
.to(".vm-right", {x: -50, duration: 1, ease: "power2.inOut"}, "<")
.to(".vm-left, .vm-right", {scale: 1.1, duration: 0.3, yoyo: true, repeat: 1})
.to("#gcd-text", {opacity: 1, scale: 1, duration: 0.5}, "-=0.5"); // Show text
},
// 4. 公倍数动画: 跳跃
playLCMAnim() {
if(typeof gsap === 'undefined') return;
gsap.set(".jd-a", {x: 0, scale: 1, backgroundColor: '#3B82F6'});
gsap.set(".jd-b", {x: 0, scale: 1, backgroundColor: '#EF4444'});
const tl = gsap.timeline({repeat: -1, repeatDelay: 1});
// A 跳3次到60 (模拟12)
tl.to(".jd-a", {x: 20, duration: 0.2})
.to(".jd-a", {x: 40, duration: 0.2})
.to(".jd-a", {x: 60, duration: 0.2});
// B 跳2次到60
tl.to(".jd-b", {x: 30, duration: 0.3}, 0)
.to(".jd-b", {x: 60, duration: 0.3}, 0.3)
// 相遇闪烁
.to(".jd-a, .jd-b", {scale: 1.5, backgroundColor: '#10B981', duration: 0.2, yoyo: true, repeat: 3});
},
// ================= Page 2: 演示实验室逻辑 =================
setMode(mode) {
this.demoMode = mode;
this.animStep = 0;
this.totalSteps = 3;
this.animText = "点击下一步开始演示";
if(typeof gsap !== 'undefined') gsap.killTweensOf('.venn-container *, .track-container *');
// 清理
const container = document.querySelector('.venn-container');
if(container) {
const balls = container.querySelectorAll('.venn-num');
balls.forEach(el => el.remove());
}
if (mode === 'gcd') {
this.speak("我们来看看12和18的公约数。");
gsap.set('.venn-left', {x:0}); gsap.set('.venn-right', {x:0});
} else {
this.speak("这是4和6的倍数赛跑。");
gsap.set('.runner', {rotation: 0});
}
},
nextStep() {
if (this.animStep < this.totalSteps) {
this.animStep++;
this.renderScene();
}
},
prevStep() {
if (this.animStep > 0) {
this.animStep--;
this.setMode(this.demoMode);
}
},
renderScene() {
if(typeof gsap === 'undefined') return;
if (this.demoMode === 'gcd') {
const container = document.querySelector('.venn-container');
if (this.animStep === 1) {
this.animText = "列出约数";
const data = [
{n:1, t:'common'}, {n:2, t:'common'}, {n:3, t:'common'}, {n:6, t:'common'},
{n:4, t:'left'}, {n:12, t:'left'},
{n:9, t:'right'}, {n:18, t:'right'}
];
data.forEach((d, i) => {
const el = document.createElement('div');
el.className = 'venn-num';
el.innerHTML = d.n;
let x, y;
if(d.t === 'left') { x = 40; y = 80 + (i%2)*40; }
else if(d.t === 'right') { x = 280; y = 80 + (i%2)*40; }
else { x = 160; y = 60 + i*25; }
el.style.left = x + 'px'; el.style.top = y + 'px';
container.appendChild(el);
gsap.to(el, {opacity: 1, duration: 0.5, delay: i*0.1});
});
this.speak("左边是12的约数,右边是18的约数。");
} else if (this.animStep === 2) {
this.animText = "寻找共同点";
gsap.to('.venn-left', {x: 50, duration: 1});
gsap.to('.venn-right', {x: -50, duration: 1});
this.speak("把两个圈合起来,中间重叠的部分就是公约数。");
} else if (this.animStep === 3) {
this.animText = "最大公约数是 6";
const balls = document.querySelectorAll('.venn-num');
balls.forEach(b => {
if(['1','2','3','6'].includes(b.innerHTML)) {
gsap.to(b, {scale: 1.5, backgroundColor: '#FCD34D', zIndex: 10});
} else {
gsap.to(b, {opacity: 0.2});
}
});
this.speak("公约数里最大的那个,就是最大公约数,它是6。");
}
} else {
// LCM
if (this.animStep === 1) {
this.animText = "预备...跑!";
gsap.to('#runner-4', {rotation: 360, duration: 2, ease: "none", repeat: 2}); // 4跑得快
gsap.to('#runner-6', {rotation: 360, duration: 3, ease: "none", repeat: 1}); // 6跑得慢
this.speak("蓝色代表4的倍数,红色代表6的倍数。");
} else if (this.animStep === 2) {
this.animText = "相遇点 12";
gsap.killTweensOf('.runner');
gsap.set('#runner-4', {rotation: 0});
gsap.set('#runner-6', {rotation: 0});
gsap.to('#runner-4', {rotation: 360*3, duration: 3, ease: "power1.inOut"});
gsap.to('#runner-6', {rotation: 360*2, duration: 3, ease: "power1.inOut"});
this.speak("看!4跑了3圈,6跑了2圈,它们在12这个地方相遇了。");
} else if (this.animStep === 3) {
this.animText = "最小公倍数 12";
gsap.to('.track-container', {borderColor: '#10B981', scale: 1.1, duration: 0.5, yoyo: true, repeat: 1});
this.speak("12是它们共同遇到的第一个数字,所以是最小公倍数。");
}
}
},
// 练习逻辑
handleBasicAnswer(i, c) {
if(this.basicAnswered) return;
this.basicAnswered = true; this.isBasicCorrect = c; this.selectedBasicOpt = i;
if(c) { 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(i, c) {
if(this.olympiadAnswered) return;
this.olympiadAnswered = true; this.selectedOlympiadOpt = i;
if(c) { this.olympiadScore += 10; if(typeof confetti !== 'undefined') confetti({particleCount: 150, spread: 90, colors: ['#8B5CF6']});}
},
nextOlympiad() { if(this.currentOlympiadIndex<9){this.currentOlympiadIndex++; this.olympiadAnswered=false; this.selectedOlympiadOpt=null;}else{this.olympiadDone=true;} },
toggleExample(idx) { this.activeExample = this.activeExample === idx ? null : idx; }
},
mounted() {
this.setConcept('divisor'); // 默认展示第一个概念
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码