<!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>1.4 提取公因数(进阶变倍)</title>
<style>
/* ================= 基础重置 ================= */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
::-webkit-scrollbar { display: none; width: 0 !important; height: 0 !important; }
* { -ms-overflow-style: none; scrollbar-width: none; }
html, body {
margin: 0; padding: 0;
background: #F0F4F8;
/* 升级版马卡龙点阵背景 */
background-image:
radial-gradient(#E2E8F0 1.5px, transparent 1.5px),
radial-gradient(#E2E8F0 1.5px, transparent 1.5px);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden; height: 100vh;
color: #2D3748;
}
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: rgba(255,255,255,0.95); display: flex; flex-direction: column; position: relative; box-shadow: 0 0 40px rgba(0,0,0,0.05); }
.content-area {
flex: 1;
overflow-y: auto;
padding-bottom: 90px; /* 底部导航避让 */
scroll-behavior: smooth;
}
/* ================= 底部导航 ================= */
.bottom-nav {
position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
width: 90%; max-width: 440px; height: 65px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 35px;
box-shadow: 0 10px 30px rgba(100, 116, 139, 0.15);
display: flex; justify-content: space-around; align-items: center; z-index: 9999;
padding: 0 10px;
}
.nav-item {
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
cursor: pointer; transition: all 0.2s; opacity: 0.6;
}
.nav-item.active { opacity: 1; transform: translateY(-2px); }
.nav-item.active span:first-child { transform: scale(1.1); }
.nav-icon { font-size: 22px; margin-bottom: 2px; }
.nav-text { font-size: 10px; font-weight: bold; }
/* ================= 通用卡片样式 ================= */
.card-base {
background: white; border-radius: 20px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.01);
margin-bottom: 20px; overflow: hidden;
transition: transform 0.2s;
}
/* ================= 动画演示区 ================= */
.animation-area {
background: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
border-radius: 20px; padding: 20px;
height: 300px; position: relative;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.03);
border: 1px solid white;
}
.num-display {
font-size: 36px; font-weight: 800; margin: 0 5px;
display: inline-flex; align-items: center; justify-content: center;
min-width: 64px; height: 64px;
color: #4A5568;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.operator { font-size: 28px; color: #A0AEC0; margin: 0 10px; font-weight: bold; }
.step-btn {
position: absolute; bottom: 20px; z-index: 100;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none; width: 44px; height: 44px;
border-radius: 50%; font-size: 20px; cursor: pointer;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
display: flex; align-items: center; justify-content: center;
transition: all 0.2s;
}
.step-btn:active { transform: scale(0.9); }
.step-btn:disabled { background: #CBD5E0; box-shadow: none; cursor: not-allowed; }
.step-btn.prev { left: 20px; }
.step-btn.next { right: 20px; }
/* ================= 秘籍卡片 (全屏水平滑动) ================= */
.secret-container {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 20px;
padding: 20px;
width: 100%;
height: 100%;
align-items: flex-start;
}
.secret-card {
flex: 0 0 100%; /* 强制宽度占满容器 */
scroll-snap-align: center;
min-height: 500px;
background: white;
border-radius: 24px;
padding: 30px;
position: relative;
box-shadow: 0 15px 35px rgba(0,0,0,0.1);
display: flex; flex-direction: column; align-items: center;
/* 具体的渐变色在 HTML 中通过 class 定义 */
}
.secret-card.theme-1 { background: linear-gradient(135deg, #fff1eb 0%, #ace0f9 100%); }
.secret-card.theme-2 { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); }
.secret-card.theme-3 { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); }
.secret-title { font-size: 24px; font-weight: 800; color: #2D3748; margin-bottom: 20px; text-align: center; }
.secret-text { font-size: 16px; line-height: 1.8; color: #4A5568; margin-bottom: 20px; text-align: center; }
.swipe-hint { position: absolute; bottom: 20px; color: rgba(0,0,0,0.3); font-size: 12px; animation: bounceX 1.5s infinite; }
@keyframes bounceX {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(5px); }
}
/* ================= 练习题 & 选项 ================= */
.question-card {
background: white; border-radius: 24px; padding: 25px;
box-shadow: 0 8px 20px rgba(0,0,0,0.06);
margin: 20px;
}
.question-text { font-size: 20px; font-weight: 600; margin: 20px 0 30px; line-height: 1.5; color: #2D3748; }
.option-btn {
width: 100%; padding: 18px; margin-bottom: 12px;
border: 2px solid #EDF2F7; border-radius: 16px;
background: white; font-size: 16px; color: #4A5568;
font-weight: 600; text-align: left;
transition: all 0.2s; position: relative; overflow: hidden;
}
.option-btn:active { transform: scale(0.98); background: #F7FAFC; }
.option-btn.correct {
background: #C6F6D5; border-color: #9AE6B4; color: #22543D;
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.2);
}
.option-btn.wrong {
background: #FED7D7; border-color: #FEB2B2; color: #822727;
animation: shake 0.4s ease-in-out;
}
/* 交互反馈动画 */
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-8px); }
50% { transform: translateX(8px); }
75% { transform: translateX(-4px); }
}
/* ================= 页面特定元素 ================= */
.intro-title {
font-size: 32px; font-weight: 900;
background: linear-gradient(120deg, #667eea, #764ba2);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
margin: 30px 0 20px; text-align: center;
}
.concept-highlight {
background: rgba(102, 126, 234, 0.08);
border-left: 5px solid #667eea;
padding: 20px; border-radius: 12px;
margin: 20px 0; font-size: 16px; line-height: 1.8;
}
/* 按钮风格 */
.primary-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none; padding: 15px 30px;
border-radius: 50px; font-size: 16px; font-weight: bold;
box-shadow: 0 10px 20px rgba(118, 75, 162, 0.3);
width: 100%; margin-top: 20px; cursor: pointer;
transition: transform 0.2s;
}
.primary-btn:active { transform: scale(0.96); }
.difficulty-badge {
padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: bold; vertical-align: middle;
}
.badge-easy { background: #C6F6D5; color: #22543D; }
.badge-medium { background: #FEEBC8; color: #744210; }
.badge-hard { background: #FED7D7; color: #822727; }
.badge-expert { background: #E9D8FD; color: #44337A; }
/* Tab 导航 */
.tab-nav { display: flex; padding: 0 20px; margin-bottom: 20px; }
.tab-item {
margin-right: 20px; font-weight: bold; color: #A0AEC0;
padding-bottom: 8px; cursor: pointer; transition: all 0.3s;
}
.tab-item.active { color: #553C9A; border-bottom: 3px solid #553C9A; font-size: 18px; }
/* 页面切换动画 */
.page-enter { animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
@keyframes slideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="intro-page" style="padding: 20px;">
<div class="intro-title">📦 变倍公因数</div>
<div class="card-base" style="padding: 25px;">
<p style="font-size: 16px; line-height: 1.8;">
想象一下,你和小伙伴分棒棒糖。你有4根柠檬味的,他有8根草莓味的。
<br><br>
这8根草莓味棒棒糖,其实是你的<strong>2倍</strong>(8 = 4 × 2)。
在计算中,我们也要学会找这样的“亲戚”关系!
</p>
<div class="concept-highlight">
<div>🕵️ <strong>伪装者</strong>:表面看没有相同的数字,但暗藏倍数关系。</div>
<div style="margin-top:10px;">🔍 <strong>照妖镜</strong>:把大数拆开,让公因数现出原形。</div>
</div>
<div style="text-align:center; font-size: 60px; margin: 20px 0;">🍭 🍬</div>
</div>
<button class="primary-btn" @click="speak('欢迎学习提取公因数进阶变倍。想象一下,你和小伙伴分棒棒糖。你有4根柠檬味的,他有8根草莓味的。但仔细一看,他的8根其实是你的4根的2倍。这节课,我们就要学会用照妖镜,让伪装起来的公因数现出原形,轻松计算!')">
🔊 听老师讲解
</button>
</div>
<div v-show="currentPage === 2">
<div class="tab-nav" style="margin-top: 20px;">
<div class="tab-item" :class="{active: demoTab === 'direct'}" @click="switchDemoTab('direct')">魔法演示</div>
<div class="tab-item" :class="{active: demoTab === 'theory'}" @click="switchDemoTab('theory')">核心原理</div>
</div>
<div v-show="demoTab === 'direct'" style="padding: 0 20px;">
<div class="card-base" style="background: #FEEBC8; padding: 15px;">
<strong style="color: #744210;">目标算式:</strong> $99 \times 22 + 33 \times 34$
<br>
<span style="font-size: 14px; color: #744210;">(99 是 33 的 3 倍,我们要拆它!)</span>
</div>
<div class="animation-area">
<div class="anim-text" style="text-align: center; margin-bottom: 20px; font-weight: bold; color: #667eea;">{{ stepText }}</div>
<div style="display: flex; align-items: center; justify-content: center; height: 150px; position: relative;">
<span class="num-display" id="num99" style="background-color: #FFF5F5; color: #E53E3E; border: 2px solid #FEB2B2; border-radius: 16px;">99</span>
<span class="num-display" id="num33" style="background-color: #E6FFFA; color: #319795; border: 2px solid #81E6D9; border-radius: 16px; position: absolute; opacity: 0;">33</span>
<span class="num-display" id="num3" style="background-color: #EBF8FF; color: #3182CE; border: 2px solid #90CDF4; border-radius: 16px; position: absolute; opacity: 0;">3</span>
<span class="operator">×</span>
<span class="num-display" id="num22" style="background-color: #FAF5FF; color: #805AD5; border: 2px solid #D6BCFA; border-radius: 16px;">22</span>
<span class="num-display" id="num66" style="background-color: #EBF8FF; color: #2B6CB0; border: 2px solid #63B3ED; border-radius: 16px; position: absolute; opacity: 0;">66</span>
</div>
<button class="step-btn prev" @click="prevStep" :disabled="directStep <= 0">◀</button>
<button class="step-btn next" @click="nextStep" :disabled="directStep >= 2">▶</button>
</div>
</div>
<div v-show="demoTab === 'theory'" style="padding: 0 20px;">
<div class="card-base" style="padding: 25px;">
<h3 style="margin-top: 0; color: #553C9A;">原理:拆分制造公因数</h3>
<div style="background: #F7FAFC; padding: 15px; border-radius: 10px; margin-bottom: 15px;">
<div>🔍 <strong>观察:</strong> 99 和 33</div>
<div style="color: #718096; font-size: 14px;">虽然不相等,但 99 是 33 的亲戚。</div>
</div>
<div style="font-size: 18px; font-weight: bold; text-align: center; margin: 20px 0;">
$99 = 33 \times 3$
</div>
<div style="background: #FFFAF0; border-left: 4px solid #ED8936; padding: 15px; border-radius: 4px;">
<span style="font-weight: bold; color: #DD6B20;">魔法口诀:</span><br>
大数拆开变小数,倍数送给好朋友。<br>
<span style="font-size:14px; color:#A0AEC0;">(3送给22,变成66)</span>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 3">
<div class="tab-nav" style="margin-top: 20px;">
<div class="tab-item" :class="{active: explainTab === 'advanced'}" @click="explainTab = 'advanced'">典型例题</div>
<div class="tab-item" :class="{active: explainTab === 'mistake'}" @click="explainTab = 'mistake'">易错警示</div>
</div>
<div style="padding: 0 20px;">
<div class="card-base" v-show="explainTab === 'advanced'" @click="toggleCard(0)">
<div style="padding: 20px; display: flex; justify-content: space-between; align-items: center;">
<strong>🎯 例题:$99 \times 22 + 33 \times 34$</strong>
<span style="color: #CBD5E0;">{{ expandedCard === 0 ? '▲' : '▼' }}</span>
</div>
<div v-if="expandedCard === 0" style="padding: 0 20px 20px; border-top: 1px solid #eee;">
<p><strong>第一步:拆</strong><br>99 变成 $33 \times 3$</p>
<p><strong>第二步:送</strong><br>3 送给 22,$3 \times 22 = 66$</p>
<p><strong>第三步:提</strong><br>$33 \times 66 + 33 \times 34 = 33 \times (66+34)$</p>
<div style="background: #F0FFF4; padding: 10px; border-radius: 8px; font-weight: bold; color: #276749;">
答案:3300
</div>
</div>
</div>
<div class="card-base" v-show="explainTab === 'mistake'" @click="toggleCard(1)">
<div style="padding: 20px; display: flex; justify-content: space-between; align-items: center;">
<strong>⚠️ 易错:$333 \times 2 + 111 \times 4$</strong>
<span style="color: #CBD5E0;">{{ expandedCard === 1 ? '▲' : '▼' }}</span>
</div>
<div v-if="expandedCard === 1" style="padding: 0 20px 20px; border-top: 1px solid #eee;">
<p style="color: #E53E3E;">❌ 错误做法:直接提取 111,忘记处理倍数。</p>
<p><strong>✅ 正确做法:</strong></p>
<p>1. 333 是 111 的 3 倍。</p>
<p>2. 原式变成 $111 \times 3 \times 2 + 111 \times 4$</p>
<p>3. 即 $111 \times 6 + 111 \times 4$</p>
<div style="background: #F0FFF4; padding: 10px; border-radius: 8px; font-weight: bold; color: #276749;">
答案:$111 \times 10 = 1110$
</div>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="practice-page">
<div v-if="!practiceCompleted">
<div class="question-card">
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<span style="font-weight: bold; color: #A0AEC0;">练习 {{currentQuestion + 1}}/8</span>
<span style="color: #ECC94B; font-weight: bold;">⭐ {{score}}</span>
</div>
<div class="question-text" v-html="practiceQuestions[currentQuestion].text"></div>
<div class="options-container">
<button v-for="(option, index) in practiceQuestions[currentQuestion].options"
:key="'opt'+index"
class="option-btn"
:class="{
'correct': answered && option.correct,
'wrong': answered && selectedOption === index && !option.correct
}"
@click="selectOption(index, option.correct)"
:disabled="answered">
{{option.text}}
</button>
</div>
<div v-if="answered" style="margin-top: 20px; padding: 15px; background: #F7FAFC; border-radius: 12px; font-size: 14px; line-height: 1.6;">
<strong>解析:</strong> {{practiceQuestions[currentQuestion].explanation}}
</div>
<button v-if="answered" class="primary-btn" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '查看成绩'}}
</button>
</div>
</div>
<div v-else class="intro-page" style="padding: 40px 20px; text-align: center;">
<div style="font-size: 80px; margin-bottom: 20px;">🎉</div>
<h2 style="color: #2D3748;">练习完成!</h2>
<div style="font-size: 40px; font-weight: 800; color: #667eea; margin: 20px 0;">{{score}}分</div>
<button class="primary-btn" @click="switchPage(5)">去挑战奥数题 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="practice-page">
<div v-if="!olympiadCompleted">
<div class="question-card" style="border-top: 5px solid #805AD5;">
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<div>
<span style="font-weight: bold; color: #A0AEC0;">挑战 {{currentOlympiad + 1}}/8</span>
<span class="difficulty-badge" :class="'badge-' + olympiadQuestions[currentOlympiad].difficulty" style="margin-left: 8px;">
{{olympiadQuestions[currentOlympiad].difficultyText}}
</span>
</div>
<span style="color: #ECC94B; font-weight: bold;">🏆 {{olympiadScore}}</span>
</div>
<div class="question-text" v-html="olympiadQuestions[currentOlympiad].text"></div>
<div class="options-container">
<button v-for="(option, index) in olympiadQuestions[currentOlympiad].options"
:key="'olopt'+index"
class="option-btn"
:class="{
'correct': olympiadAnswered && option.correct,
'wrong': olympiadAnswered && selectedOlympiadOption === index && !option.correct
}"
@click="selectOlympiadOption(index, option.correct)"
:disabled="olympiadAnswered">
{{option.text}}
</button>
</div>
<div v-if="wrongAttempts > 0 && !olympiadAnswered" style="background: #FFFFF0; padding: 10px; border-radius: 8px; margin-top: 10px; font-size: 14px; color: #D69E2E;">
💡 {{ olympiadQuestions[currentOlympiad].hint }}
</div>
<div v-if="olympiadAnswered" style="margin-top: 20px; padding: 15px; background: #F7FAFC; border-radius: 12px; font-size: 14px; line-height: 1.6;">
<strong>解析:</strong> <span v-html="olympiadQuestions[currentOlympiad].explanation"></span>
</div>
<button v-if="olympiadAnswered || wrongAttempts >= 2" class="primary-btn" @click="nextOlympiad">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '查看通关秘籍'}}
</button>
</div>
</div>
<div v-else class="intro-page" style="padding: 40px 20px; text-align: center;">
<div style="font-size: 80px; margin-bottom: 20px;">🏆</div>
<h2 style="color: #2D3748;">奥数挑战通关!</h2>
<div style="font-size: 40px; font-weight: 800; color: #805AD5; margin: 20px 0;">{{olympiadScore}}分</div>
<button class="primary-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<div v-show="currentPage === 6" style="background: #F0F4F8; height: 100%; display: flex; flex-direction: column;">
<div style="padding: 20px 0 0 20px; font-size: 20px; font-weight: bold; color: #2D3748;">通关秘籍卡片</div>
<div class="secret-container">
<div class="secret-card theme-1">
<div class="secret-title">秘籍一:火眼金睛</div>
<div class="secret-text">
看到两个数有倍数关系时,<br>
把大数拆成 <strong>小数 × 倍数</strong>。<br>
就像把长条积木切成小块!
</div>
<svg viewBox="0 0 300 150" style="width: 100%; max-width: 280px; margin-top: 20px;">
<defs>
<marker id="arrow1" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#718096" />
</marker>
</defs>
<rect x="10" y="20" width="270" height="40" rx="5" fill="#FED7D7" stroke="#E53E3E" stroke-width="2"/>
<text x="145" y="45" font-family="Arial" font-size="16" fill="#C53030" text-anchor="middle" font-weight="bold">99</text>
<line x1="145" y1="65" x2="145" y2="85" stroke="#718096" stroke-width="2" marker-end="url(#arrow1)"/>
<rect x="10" y="90" width="86" height="40" rx="5" fill="#C6F6D5" stroke="#38A169" stroke-width="2"/>
<text x="53" y="115" font-family="Arial" font-size="14" fill="#22543D" text-anchor="middle">33</text>
<rect x="102" y="90" width="86" height="40" rx="5" fill="#C6F6D5" stroke="#38A169" stroke-width="2"/>
<text x="145" y="115" font-family="Arial" font-size="14" fill="#22543D" text-anchor="middle">33</text>
<rect x="194" y="90" width="86" height="40" rx="5" fill="#C6F6D5" stroke="#38A169" stroke-width="2"/>
<text x="237" y="115" font-family="Arial" font-size="14" fill="#22543D" text-anchor="middle">33</text>
<text x="145" y="145" font-size="12" fill="#718096" text-anchor="middle">99 变成了 3 个 33</text>
</svg>
<div class="swipe-hint">← 向左滑动查看下一张</div>
</div>
<div class="secret-card theme-2">
<div class="secret-title">秘籍二:移花接木</div>
<div class="secret-text">
拆出来的倍数不要丢,<br>
马上乘给另一个因数。<br>
<span style="color:#E53E3E">“牺牲自己,成全公因数!”</span>
</div>
<svg viewBox="0 0 300 120" style="width: 100%; max-width: 280px; margin-top:20px;">
<circle cx="40" cy="60" r="30" fill="#EBF8FF" stroke="#3182CE" stroke-width="2"/>
<text x="40" y="65" font-size="20" font-weight="bold" fill="#2B6CB0" text-anchor="middle">3</text>
<text x="40" y="25" font-size="10" fill="#2B6CB0" text-anchor="middle">倍数</text>
<text x="80" y="65" font-size="24" font-weight="bold" fill="#333">×</text>
<rect x="110" y="30" width="60" height="60" rx="10" fill="#FAF5FF" stroke="#805AD5" stroke-width="2"/>
<text x="140" y="65" font-size="20" font-weight="bold" fill="#6B46C1" text-anchor="middle">22</text>
<path d="M180,60 L210,60" fill="none" stroke="#333" stroke-width="2" marker-end="url(#arrow1)" />
<rect x="225" y="30" width="65" height="60" rx="10" fill="#EBF8FF" stroke="#2B6CB0" stroke-width="2"/>
<text x="257" y="65" font-size="24" font-weight="bold" fill="#2C5282" text-anchor="middle">66</text>
</svg>
<div class="swipe-hint">← 向左滑动查看下一张</div>
</div>
<div class="secret-card theme-3">
<div class="secret-title">秘籍三:最终合体</div>
<div class="secret-text">
两个算式都有相同的公因数了,<br>
直接提取,括号里相加!<br>
$33 \times 66 + 33 \times 34 = 33 \times (66+34)$
</div>
<svg viewBox="0 0 300 100" style="width: 100%; max-width: 280px; margin-top:20px;">
<text x="20" y="60" font-size="24" font-weight="bold" fill="#38A169">33</text>
<text x="60" y="60" font-size="24" fill="#333">×</text>
<rect x="85" y="30" width="200" height="50" rx="10" fill="none" stroke="#333" stroke-width="2" stroke-dasharray="5,5"/>
<text x="110" y="60" font-size="24" font-weight="bold" fill="#2B6CB0">66</text>
<text x="150" y="60" font-size="24" fill="#333">+</text>
<text x="180" y="60" font-size="24" font-weight="bold" fill="#2B6CB0">34</text>
<text x="230" y="60" font-size="20" fill="#E53E3E" font-weight="bold">= 100</text>
</svg>
<button class="primary-btn" style="width: auto; padding: 12px 40px; margin-top: auto;" @click="switchPage(1)">再学一遍 ↺</button>
</div>
</div>
</div>
</div>
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<span class="nav-icon">🏠</span><span class="nav-text">引入</span>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<span class="nav-icon">💡</span><span class="nav-text">原理</span>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<span class="nav-icon">📖</span><span class="nav-text">例题</span>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<span class="nav-icon">✍️</span><span class="nav-text">练习</span>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<span class="nav-icon">🚀</span><span class="nav-text">挑战</span>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<span class="nav-icon">🔑</span><span class="nav-text">秘籍</span>
</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, ref, nextTick, onMounted } = Vue;
createApp({
setup() {
const currentPage = ref(1);
const demoTab = ref('direct');
const explainTab = ref('advanced');
const expandedCard = ref(null);
// 动画控制
const directStep = ref(0);
const stepText = ref("点击下一步,看 99 怎么变身!");
// 语音控制 (保持原逻辑不变)
const speak = (text) => {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
let audio = document.getElementById('tts-audio');
if (!audio) {
audio = document.createElement('audio');
audio.id = 'tts-audio';
audio.style.display = 'none';
document.body.appendChild(audio);
}
const url = `https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
audio.play().catch(err => console.log('语音播放失败:', err));
} else {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
}
};
const stopSpeak = () => {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
const audio = document.getElementById('tts-audio');
if (audio) { audio.pause(); audio.currentTime = 0; }
} else {
if (window.speechSynthesis) window.speechSynthesis.cancel();
}
};
// 页面切换
const switchPage = (page) => {
stopSpeak();
currentPage.value = page;
nextTick(() => {
// 简单的进入动画类处理
const pageEl = document.querySelector(`[v-show="currentPage === ${page}"]`);
if (pageEl) {
pageEl.classList.remove('page-enter');
void pageEl.offsetWidth; // trigger reflow
pageEl.classList.add('page-enter');
}
});
if (page === 2) {
directStep.value = 0;
runDirectAnimation();
}
};
const switchDemoTab = (tab) => {
demoTab.value = tab;
if (tab === 'direct') {
directStep.value = 0;
runDirectAnimation();
}
};
const toggleCard = (index) => {
expandedCard.value = expandedCard.value === index ? null : index;
};
// GSAP 动画逻辑 (优化版)
const runDirectAnimation = () => {
gsap.set(['#num99', '#num22', '#num33', '#num3', '#num66'], { clearProps: 'all' });
if (directStep.value === 0) {
stepText.value = "准备:99 和 22 看起来没关系...";
gsap.set('#num99', { scale: 1, opacity: 1, x:0, y:0, display:'inline-flex' });
gsap.set('#num22', { scale: 1, opacity: 1, x:0, y:0 });
gsap.set(['#num33', '#num3', '#num66'], { opacity: 0, scale: 0.5 });
} else if (directStep.value === 1) {
stepText.value = "变身!99 拆成了 33 和 3";
// 99 消失
gsap.to('#num99', { scale: 1.2, opacity: 0, duration: 0.3 });
// 33 和 3 出现
gsap.to('#num33', {
opacity: 1, scale: 1, duration: 0.5, ease: "back.out(1.7)",
x: -60
});
gsap.to('#num3', {
opacity: 1, scale: 1, duration: 0.5, delay: 0.1, ease: "back.out(1.7)",
x: 60
});
gsap.to('#num22', { x: 40, duration: 0.5 }); // 22 稍微挪一点位置
} else if (directStep.value === 2) {
stepText.value = "结合!3 和 22 变成了 66";
// 3 飞向 22
gsap.to('#num3', { x: 120, opacity: 0, duration: 0.4 });
gsap.to('#num22', { opacity: 0, duration: 0.4 });
// 66 出现
gsap.to('#num66', {
opacity: 1, scale: 1.1, duration: 0.5, delay: 0.4, ease: "elastic.out(1, 0.5)",
x: 60
});
}
};
const prevStep = () => { if(directStep.value > 0) { directStep.value--; runDirectAnimation(); } };
const nextStep = () => { if(directStep.value < 2) { directStep.value++; runDirectAnimation(); } };
// ================= 练习题数据 (补全至8题) =================
const practiceQuestions = ref([
{
text: '$44 \\times 5 + 22 \\times 90 = ?$',
options: [
{text: 'A. 2200', correct: true}, {text: 'B. 2000', correct: false},
{text: 'C. 2420', correct: false}, {text: 'D. 4400', correct: false}
],
explanation: '44 是 22 的 2 倍。变成 $22 \\times 2 \\times 5 = 22 \\times 10$。原式 = $22 \\times (10+90) = 2200$。'
},
{
text: '$12 \\times 14 + 6 \\times 72 = ?$',
options: [
{text: 'A. 600', correct: true}, {text: 'B. 720', correct: false},
{text: 'C. 1200', correct: false}, {text: 'D. 360', correct: false}
],
explanation: '12 是 6 的 2 倍。$12 \\times 14 = 6 \\times 28$。原式 = $6 \\times (28+72) = 600$。'
},
{
text: '$38 \\times 9 + 19 \\times 82 = ?$',
options: [
{text: 'A. 1900', correct: true}, {text: 'B. 3800', correct: false},
{text: 'C. 1800', correct: false}, {text: 'D. 2000', correct: false}
],
explanation: '38 是 19 的 2 倍。$38 \\times 9 = 19 \\times 18$。原式 = $19 \\times (18+82) = 1900$。'
},
{
text: '$25 \\times 32 + 5 \\times 40 = ?$',
options: [
{text: 'A. 1000', correct: true}, {text: 'B. 1200', correct: false},
{text: 'C. 800', correct: false}, {text: 'D. 1100', correct: false}
],
explanation: '25 是 5 的 5 倍。$25 \\times 32 = 5 \\times 5 \\times 32 = 5 \\times 160$。原式 = $5 \\times (160+40) = 1000$。'
},
{
text: '$11 \\times 4 + 22 \\times 3 = ?$',
options: [
{text: 'A. 110', correct: true}, {text: 'B. 100', correct: false},
{text: 'C. 121', correct: false}, {text: 'D. 220', correct: false}
],
explanation: '22 是 11 的 2 倍。$22 \\times 3 = 11 \times 6$。$11 \\times (4+6) = 110$。'
},
{
text: '$999 \\times 1 + 333 \\times 7 = ?$',
options: [
{text: 'A. 3330', correct: true}, {text: 'B. 9990', correct: false},
{text: 'C. 3000', correct: false}, {text: 'D. 6660', correct: false}
],
explanation: '999 是 333 的 3 倍。$999 \\times 1 = 333 \\times 3$。$333 \\times (3+7) = 3330$。'
},
{
text: '$50 \\times 6 + 25 \\times 8 = ?$',
options: [
{text: 'A. 500', correct: true}, {text: 'B. 400', correct: false},
{text: 'C. 600', correct: false}, {text: 'D. 300', correct: false}
],
explanation: '50 是 25 的 2 倍。$50 \\times 6 = 25 \\times 12$。$25 \\times (12+8) = 500$。'
},
{
text: '$16 \\times 4 + 32 \\times 3 = ?$',
options: [
{text: 'A. 160', correct: true}, {text: 'B. 128', correct: false},
{text: 'C. 200', correct: false}, {text: 'D. 320', correct: false}
],
explanation: '32 是 16 的 2 倍。$32 \\times 3 = 16 \\times 6$。$16 \\times (4+6) = 160$。'
}
]);
// ================= 奥数题数据 (补全至8题) =================
const olympiadQuestions = ref([
{
text: '$1998 \\times 34 + 999 \\times 32 = ?$',
options: [
{text: 'A. 99900', correct: true}, {text: 'B. 199800', correct: false},
{text: 'C. 9990', correct: false}, {text: 'D. 10000', correct: false}
],
explanation: '1998 是 999 的 2 倍。<br>$1998 \\times 34 = 999 \\times 2 \\times 34 = 999 \\times 68$。<br>$999 \\times (68+32) = 99900$。',
hint: '观察 1998 和 999,1998 = 999 × ?',
difficulty: 'medium', difficultyText: '入门'
},
{
text: '$222 \\times 3 + 111 \\times 4 = ?$',
options: [
{text: 'A. 1110', correct: true}, {text: 'B. 2220', correct: false},
{text: 'C. 666', correct: false}, {text: 'D. 888', correct: false}
],
explanation: '$222 = 111 \\times 2$。<br>$222 \\times 3 = 111 \\times 6$。<br>$111 \\times (6+4) = 1110$。',
hint: '222 是 111 的几倍?',
difficulty: 'medium', difficultyText: '基础'
},
{
text: '$123 \\times 16 + 246 \\times 42 = ?$',
options: [
{text: 'A. 12300', correct: true}, {text: 'B. 24600', correct: false},
{text: 'C. 1230', correct: false}, {text: 'D. 10000', correct: false}
],
explanation: '$246 = 123 \\times 2$。<br>$246 \\times 42 = 123 \\times 2 \\times 42 = 123 \\times 84$。<br>$123 \\times (16+84) = 12300$。',
hint: '小数是123,大数是246,看看怎么拆?',
difficulty: 'hard', difficultyText: '进阶'
},
{
text: '$3333 \\times 2 + 1111 \\times 4 = ?$',
options: [
{text: 'A. 11110', correct: true}, {text: 'B. 33330', correct: false},
{text: 'C. 22220', correct: false}, {text: 'D. 10000', correct: false}
],
explanation: '$3333 = 1111 \\times 3$。<br>$3333 \\times 2 = 1111 \\times 3 \\times 2 = 1111 \\times 6$。<br>$1111 \\times (6+4) = 11110$。',
hint: '3333 是 1111 的 3 倍哦。',
difficulty: 'hard', difficultyText: '进阶'
},
{
text: '$55 \\times 8 + 11 \\times 60 = ?$',
options: [
{text: 'A. 1100', correct: true}, {text: 'B. 5500', correct: false},
{text: 'C. 440', correct: false}, {text: 'D. 660', correct: false}
],
explanation: '$55 = 11 \\times 5$。<br>$55 \\times 8 = 11 \\times 5 \\times 8 = 11 \\times 40$。<br>$11 \\times (40+60) = 1100$。',
hint: '55 里面藏着 11 × 5。',
difficulty: 'hard', difficultyText: '挑战'
},
{
text: '$125 \\times 16 + 25 \\times 20 = ?$',
options: [
{text: 'A. 2500', correct: true}, {text: 'B. 2000', correct: false},
{text: 'C. 3000', correct: false}, {text: 'D. 12500', correct: false}
],
explanation: '$125 = 25 \\times 5$。<br>$125 \\times 16 = 25 \\times 5 \\times 16 = 25 \\times 80$。<br>$25 \\times (80+20) = 2500$。',
hint: '125 是 25 的 5 倍,先把 125 拆开。',
difficulty: 'expert', difficultyText: '专家'
},
{
text: '$66 \\times 4 + 33 \\times 2 = ?$',
options: [
{text: 'A. 330', correct: true}, {text: 'B. 660', correct: false},
{text: 'C. 300', correct: false}, {text: 'D. 264', correct: false}
],
explanation: '$66 = 33 \\times 2$。<br>$66 \\times 4 = 33 \\times 2 \\times 4 = 33 \\times 8$。<br>$33 \\times (8+2) = 330$。',
hint: '注意观察 66 和 33。',
difficulty: 'medium', difficultyText: '易错'
},
{
text: '$9999 \\times 2 + 3333 \\times 4 = ?$',
options: [
{text: 'A. 33330', correct: true}, {text: 'B. 99990', correct: false},
{text: 'C. 66660', correct: false}, {text: 'D. 30000', correct: false}
],
explanation: '$9999 = 3333 \\times 3$。<br>$9999 \\times 2 = 3333 \\times 6$。<br>$3333 \\times (6+4) = 33330$。',
hint: '最大的挑战!9999 是 3333 的 3 倍。',
difficulty: 'expert', difficultyText: '巅峰'
}
]);
// 练习题逻辑
const currentQuestion = ref(0);
const answered = ref(false);
const selectedOption = ref(null);
const score = ref(0);
const practiceCompleted = ref(false);
const selectOption = (index, correct) => {
if (answered.value) return;
selectedOption.value = index;
answered.value = true;
if (correct) score.value += 12.5; // 100/8 = 12.5
};
const nextQuestion = () => {
if (currentQuestion.value < practiceQuestions.value.length - 1) {
currentQuestion.value++; answered.value = false; selectedOption.value = null;
} else {
practiceCompleted.value = true;
}
};
// 奥数题逻辑
const currentOlympiad = ref(0);
const olympiadAnswered = ref(false);
const selectedOlympiadOption = ref(null);
const olympiadScore = ref(0);
const olympiadCompleted = ref(false);
const wrongAttempts = ref(0);
const selectOlympiadOption = (index, correct) => {
if (olympiadAnswered.value) return;
selectedOlympiadOption.value = index;
if (correct) {
olympiadAnswered.value = true;
olympiadScore.value += 12.5;
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
} else {
wrongAttempts.value++;
if (wrongAttempts.value >= 2) olympiadAnswered.value = true; // 2次机会
}
};
const nextOlympiad = () => {
if (currentOlympiad.value < olympiadQuestions.value.length - 1) {
currentOlympiad.value++; olympiadAnswered.value = false; selectedOlympiadOption.value = null; wrongAttempts.value = 0;
} else {
olympiadCompleted.value = true;
}
};
return {
currentPage, demoTab, explainTab, expandedCard,
directStep, stepText,
practiceQuestions, currentQuestion, answered, selectedOption, score, practiceCompleted,
olympiadQuestions, currentOlympiad, olympiadAnswered, selectedOlympiadOption, olympiadScore, olympiadCompleted, wrongAttempts,
speak, switchPage, switchDemoTab, toggleCard, prevStep, nextStep,
selectOption, nextQuestion, selectOlympiadOption, nextOlympiad
};
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码