<!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>第05讲: 找规律(进阶:斐波那契数列初步)</title>
<style>
/* ================= 基础重置与全局背景 ================= */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; outline: none; }
body, html {
margin: 0; padding: 0; height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Heiti SC', sans-serif;
/* 全局细腻渐变背景 */
background: linear-gradient(160deg, #fdfbfb 0%, #ebedee 100%);
color: #333;
}
#app {
height: 100vh;
max-width: 480px;
margin: 0 auto;
display: flex;
flex-direction: column;
overflow: hidden;
background: transparent; /* 让body背景透出来 */
}
.content-area {
flex: 1;
overflow-y: auto;
padding-bottom: 90px; /* 增加底部留白 */
-webkit-overflow-scrolling: touch;
}
/* 隐藏滚动条 */
.content-area::-webkit-scrollbar,
.secrets-container::-webkit-scrollbar { display: none; width: 0 !important; height: 0 !important; }
.content-area, .secrets-container { -ms-overflow-style: none; scrollbar-width: none; }
/* ================= 卡片与视觉风格 (圆润 + 微投影) ================= */
.card, .question-card {
background: rgba(255, 255, 255, 0.95); /* 轻微毛玻璃感 */
border-radius: 24px; /* 更圆润 */
padding: 28px;
margin: 20px;
/* 高级微投影阴影 */
box-shadow:
0 10px 20px -5px rgba(102, 126, 234, 0.15),
0 6px 10px -6px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(255,255,255,0.8);
transition: transform 0.3s ease;
}
.title { font-size: 26px; font-weight: 800; color: #2d3748; margin-bottom: 18px; text-align: center; letter-spacing: 0.5px; }
.subtitle { font-size: 19px; color: #667eea; font-weight: 700; margin-bottom: 12px; display: flex; align-items: center; }
.text { font-size: 17px; line-height: 1.7; color: #4a5568; margin-bottom: 15px; text-align: justify; }
.emoji { font-size: 60px; text-align: center; margin: 10px 0 20px; filter: drop-shadow(0 4px 6px rgba(0,0,0,0.1)); }
/* 按钮升级 */
.btn, .next-btn, .restart-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none;
padding: 16px 32px;
border-radius: 50px;
font-size: 17px; font-weight: 700;
cursor: pointer; display: block; margin: 30px auto;
width: 85%;
box-shadow: 0 8px 20px rgba(118, 75, 162, 0.3);
transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
position: relative; overflow: hidden;
}
.btn:active, .next-btn:active, .restart-btn:active { transform: scale(0.96); box-shadow: 0 4px 10px rgba(118, 75, 162, 0.2); }
/* ================= 底部导航 (悬浮感) ================= */
.bottom-nav {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
width: 90%; max-width: 440px; height: 65px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 35px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
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; padding: 5px 0; cursor: pointer; transition: all 0.3s; color: #a0aec0; }
.nav-item.active { color: #667eea; transform: translateY(-3px); }
.nav-icon { font-size: 20px; margin-bottom: 3px; }
.nav-label { font-size: 11px; font-weight: 600; }
/* ================= 页面2: 演示动画 ================= */
.demo-page { padding: 10px 0; }
.tab-nav { display: flex; background: #fff; margin: 0 20px 20px; border-radius: 15px; padding: 5px; box-shadow: 0 4px 10px rgba(0,0,0,0.03); }
.tab-item { flex: 1; padding: 10px; text-align: center; cursor: pointer; border-radius: 12px; font-weight: 600; color: #718096; transition: all 0.3s; }
.tab-item.active { background: #eef2ff; color: #667eea; }
.animation-area {
background: #fff; /* 纯白背景突出 SVG */
border-radius: 24px; padding: 10px; margin: 0 20px;
min-height: 320px; position: relative;
box-shadow: inset 0 0 20px rgba(0,0,0,0.02); /* 内部阴影增加层次 */
border: 1px solid #f0f0f0;
}
.svg-container { width: 100%; height: 100%; min-height: 300px; }
.control-bar {
position: absolute; top: 50%; left: -10px; right: -10px;
transform: translateY(-50%); display: flex; justify-content: space-between;
pointer-events: none; z-index: 100;
}
.step-btn {
pointer-events: auto; background: white; color: #667eea;
border: 2px solid #eef2ff;
width: 44px; height: 44px; border-radius: 50%;
font-size: 18px; cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
display: flex; align-items: center; justify-content: center;
transition: all 0.2s;
}
.step-btn:active { transform: scale(0.9); background: #f7fafc; }
.step-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.math-area {
background: linear-gradient(120deg, #fff 0%, #f7fafc 100%);
border: none;
border-left: 4px solid #667eea;
border-radius: 12px; padding: 15px; margin: 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
}
.math-formula { font-size: 22px; font-weight: 800; color: #2d3748; text-align: center; font-family: 'Courier New', monospace; }
.step-explanation { background: rgba(102, 126, 234, 0.1); padding: 12px; border-radius: 12px; color: #4c51bf; font-weight: 500; font-size: 15px; text-align: center; margin: 0 20px 20px; line-height: 1.5; }
/* ================= 页面3: 讲解页 ================= */
.example-card { background: #f8fafc; border-radius: 16px; padding: 15px; margin-bottom: 15px; border: 1px solid #edf2f7; }
.collapsible-header { background: #ebf4ff; border-radius: 12px; padding: 18px; margin-bottom: 12px; color: #2b6cb0; font-weight: 600; box-shadow: 0 2px 5px rgba(102,126,234,0.1); }
.collapsible-content { background: #fff; padding: 20px; border-radius: 12px; border: 1px solid #e2e8f0; margin-bottom: 15px; }
/* ================= 页面4 & 5: 练习与奥数 ================= */
.option-btn {
background: #fff; border: 2px solid #edf2f7;
border-radius: 16px; padding: 18px; text-align: left;
font-size: 16px; margin-bottom: 12px;
box-shadow: 0 2px 5px rgba(0,0,0,0.02);
position: relative; overflow: hidden;
}
.option-btn.correct { background: #f0fff4; border-color: #48bb78; color: #2f855a; }
.option-btn.wrong { background: #fff5f5; border-color: #f56565; color: #c53030; }
.feedback-area { padding: 20px; border-radius: 16px; margin-top: 25px; font-size: 15px; line-height: 1.6; }
.feedback-correct { background: #f0fff4; color: #276749; border: 1px solid #c6f6d5; }
.feedback-wrong { background: #fff5f5; color: #9b2c2c; border: 1px solid #feb2b2; }
/* ================= 页面6: 通关秘籍 (全屏卡片滑动) ================= */
.secrets-container {
/* 关键布局:Flex + Scroll Snap */
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory; /* 强制对齐 */
gap: 20px;
padding: 20px 20px 100px 20px; /* 底部预留导航高度 */
height: 100%;
align-items: center; /* 垂直居中 */
}
.secret-card {
/* 关键样式:占满宽度,禁止压缩 */
flex: 0 0 100%;
width: 100%;
scroll-snap-align: center; /* 停在中间 */
min-height: 420px; /* 增加高度 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* 视觉 */
border-radius: 30px;
box-shadow: 0 15px 35px rgba(0,0,0,0.15), inset 0 0 0 1px rgba(255,255,255,0.3);
margin-bottom: 0; /* 在这里不需要底部边距,因为是横向 */
position: relative;
}
/* 个性化渐变背景 */
.secret-bg-1 { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); }
.secret-bg-2 { background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%); }
.secret-bg-3 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.secret-emoji { font-size: 80px; margin-bottom: 30px; filter: drop-shadow(0 8px 16px rgba(0,0,0,0.15)); }
.secret-title { font-size: 28px; font-weight: 800; color: #fff; margin-bottom: 20px; text-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.secret-text { font-size: 18px; line-height: 1.8; text-align: center; color: rgba(255,255,255,0.95); padding: 0 20px; font-weight: 500; }
/* 滑动提示圆点 */
.swipe-hint {
position: absolute; bottom: 20px; left: 0; right: 0; text-align: center;
color: rgba(255,255,255,0.6); font-size: 12px;
}
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="intro-page" style="padding: 20px;">
<div class="card">
<div class="emoji">🐇</div>
<div class="title">神奇的兔子数列</div>
<div class="text">
我们来玩一个“兔子接龙”游戏!假设有一对刚出生的小兔子🐰。<br><br>
规则是:<strong>兔子宝宝要长大一个月才能生宝宝,而且每个月每对成年兔子都生一对新兔子。</strong>
</div>
<div class="text" style="background: #f7fafc; padding: 15px; border-radius: 12px;">
第1个月:1对小兔子<br>
第2个月:1对 (长大了)<br>
第3个月:2对 (生宝宝啦)<br>
第4个月:3对 (又生啦)...
</div>
<div class="text">
数量变化:1, 1, 2, 3, 5, 8... <strong>后面一个数总是前面两个数加起来!</strong>
这就是大名鼎鼎的“斐波那契数列”。
</div>
<button class="btn" @click="switchPage(2)">🎬 看兔子怎么变多</button>
</div>
</div>
<div v-show="currentPage === 2" class="demo-page">
<div class="tab-nav">
<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 class="demo-section" v-show="demoTab === 'direct'">
<div class="animation-area">
<svg class="svg-container" viewBox="0 0 400 330">
<defs>
<filter id="shadow">
<feDropShadow dx="2" dy="4" stdDeviation="3" flood-color="rgba(0,0,0,0.15)"/>
</filter>
</defs>
<rect x="10" y="270" width="380" height="40" rx="20" fill="#e2e8f0" opacity="0.5"/>
<g id="rabbits-container"></g>
<g id="numbers-container"></g>
<text id="month-label" x="200" y="300" text-anchor="middle" font-size="16" font-weight="bold" fill="#718096">第 1 个月</text>
</svg>
<div class="control-bar">
<button class="step-btn" @click="prevDirectStep" :disabled="directStep === 0">◀</button>
<button class="step-btn" @click="nextDirectStep" :disabled="directStep === directSteps.length - 1">▶</button>
</div>
</div>
<div class="math-area">
<div class="math-formula" id="current-formula">{{directFormulas[directStep]}}</div>
</div>
<div class="step-explanation">{{directSteps[directStep]}}</div>
</div>
<div class="demo-section" v-show="demoTab === 'theory'">
<div class="animation-area">
<svg class="svg-container" viewBox="0 0 400 330">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#667eea"/>
</marker>
</defs>
<g id="theory-numbers"></g>
<text x="200" y="320" text-anchor="middle" font-size="14" fill="#a0aec0">前两数相加得到后一个数</text>
</svg>
<div class="control-bar">
<button class="step-btn" @click="prevTheoryStep" :disabled="theoryStep === 0">◀</button>
<button class="step-btn" @click="nextTheoryStep" :disabled="theoryStep === theorySteps.length - 1">▶</button>
</div>
</div>
<div class="math-area">
<div class="math-formula" id="theory-formula">{{theoryFormulas[theoryStep]}}</div>
</div>
<div class="step-explanation">{{theorySteps[theoryStep]}}</div>
</div>
</div>
<div v-show="currentPage === 3" class="explain-page" style="padding: 20px;">
<div class="tab-nav" style="margin: 0 0 20px 0;">
<div class="tab-item" :class="{active: explainTab === 'direct'}" @click="explainTab = 'direct'">直接判断</div>
<div class="tab-item" :class="{active: explainTab === 'theory'}" @click="explainTab = 'theory'">核心原理</div>
<div class="tab-item" :class="{active: explainTab === 'example'}" @click="explainTab = 'example'">典型例题</div>
</div>
<div v-if="explainTab === 'direct'" class="card">
<div class="subtitle">🔍 怎么判断是“兔子数列”?</div>
<div class="text">1. <strong>看趋势</strong>:数字越来越大,而且后面比前面大得越来越明显。</div>
<div class="text">2. <strong>算加法</strong>:从第三个数开始,试着把前面两个数加起来:1+1=2,1+2=3... 看看是不是等于后面的数。</div>
<div class="text">3. <strong>记开头</strong>:最常见是 1, 1, 2... 或 0, 1, 1, 2...</div>
</div>
<div v-if="explainTab === 'theory'" class="card">
<div class="subtitle">🎯 核心原理</div>
<div class="math-area" style="min-height:auto; margin:15px 0; padding:20px; background:#f0f4ff; border-left-color: #667eea;">
<div class="math-formula" style="font-size:20px;">F(n) = F(n-1) + F(n-2)</div>
</div>
<div class="text">翻译一下:<strong>第n个数 = 前一个数 + 再前一个数</strong>。</div>
<div class="text">只要有两个“种子”数字,整个数列就能像推倒多米诺骨牌一样算出来。</div>
</div>
<div v-if="explainTab === 'example'" class="card">
<div class="subtitle">📚 典型例题</div>
<div>
<div class="collapsible-header" @click="toggleExample(0)">
<span class="example-title">例题1:1, 1, 2, 3, 5, ( )</span>
<span>{{exampleOpen[0] ? '▲' : '▼'}}</span>
</div>
<div class="collapsible-content" v-show="exampleOpen[0]">
<div class="example-answer">答案:8</div>
<div class="example-explanation">解析:3 + 5 = 8。</div>
</div>
<div class="collapsible-header" @click="toggleExample(1)">
<span class="example-title">例题2:2, 3, 5, 8, 13, ( )</span>
<span>{{exampleOpen[1] ? '▲' : '▼'}}</span>
</div>
<div class="collapsible-content" v-show="exampleOpen[1]">
<div class="example-answer">答案:21</div>
<div class="example-explanation">解析:前两数之和。8 + 13 = 21。</div>
</div>
<div class="collapsible-header" @click="toggleExample(2)">
<span class="example-title">例题3(易错):1, 2, 4, 7, 11...</span>
<span>{{exampleOpen[2] ? '▲' : '▼'}}</span>
</div>
<div class="collapsible-content" v-show="exampleOpen[2]">
<div class="example-answer">答案:16</div>
<div class="example-explanation">解析:【注意】这不是兔子数列!看差值:+1, +2, +3, +4... 下一个加5。11 + 5 = 16。</div>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="practice-page" style="padding: 20px;">
<div v-if="!practiceCompleted">
<div class="question-card">
<div class="question-header" style="display:flex; justify-content:space-between; margin-bottom:20px; border-bottom:1px dashed #eee; padding-bottom:10px;">
<div class="question-number" style="color:#a0aec0; font-weight:600;">第 {{currentQuestion + 1}}/{{practiceQuestions.length}} 题</div>
<div class="score-display" style="color:#ed8936; font-weight:800;">⭐ {{score}}</div>
</div>
<div class="question-text" style="font-size:18px; font-weight:600; margin-bottom:25px;">{{practiceQuestions[currentQuestion].text}}</div>
<div class="options-container">
<button v-for="(option, index) in practiceQuestions[currentQuestion].options"
:key="'opt'+index"
class="option-btn"
:class="{ 'correct': answered && option.correct, 'wrong': answered && selectedOption === index && !option.correct }"
@click="selectOption(index, option.correct)"
:disabled="answered">
{{option.text}}
</button>
</div>
<div v-if="answered" class="feedback-area" :class="isCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isCorrect"><strong>🎉 太棒了!</strong><br>{{practiceQuestions[currentQuestion].explanation}}</div>
<div v-else><strong>💡 分析:</strong><br>{{practiceQuestions[currentQuestion].explanation}}</div>
</div>
<button v-if="answered" class="next-btn" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '完成练习 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page" style="text-align:center; padding-top:50px;">
<div class="emoji">🎊</div>
<div class="title">练习完成!</div>
<div class="final-score" style="font-size:40px; color:#ed8936; font-weight:800; margin:20px 0;">{{score}}分</div>
<button class="restart-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="practice-page" style="padding: 20px;">
<div v-if="!olympiadCompleted">
<div class="question-card">
<div class="question-header" style="display:flex; justify-content:space-between; margin-bottom:20px;">
<div>
<span class="question-number">第 {{currentOlympiad + 1}}/{{olympiadQuestions.length}} 题</span>
<span class="difficulty-badge" :style="{background: '#e6fffa', color: '#38b2ac', padding:'2px 8px', borderRadius:'8px', fontSize:'12px', marginLeft:'5px'}">
{{olympiadQuestions[currentOlympiad].difficultyText}}
</span>
</div>
<div class="score-display">⭐ {{olympiadScore}}</div>
</div>
<div class="question-text" style="font-size:18px; font-weight:600; margin-bottom:25px;">{{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="margin-top:15px;">
<div class="hint-box" style="background:#fffaf0; border-left:4px solid #ed8936; padding:10px; color:#c05621; font-size:14px; margin-bottom:5px;" v-if="wrongAttempts >= 1">
<strong>💡 提示1:</strong> {{olympiadQuestions[currentOlympiad].hint1}}
</div>
<div class="hint-box" style="background:#fffaf0; border-left:4px solid #ed8936; padding:10px; color:#c05621; font-size:14px;" v-if="wrongAttempts >= 2">
<strong>💡 提示2:</strong> {{olympiadQuestions[currentOlympiad].hint2}}
</div>
</div>
<div v-if="olympiadAnswered" class="feedback-area" :class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div><strong>{{isOlympiadCorrect ? '🏆 厉害!' : '📖 详细解析:'}}</strong><br>{{olympiadQuestions[currentOlympiad].explanation}}</div>
</div>
<button v-if="olympiadAnswered || wrongAttempts >= 3" class="next-btn" @click="nextOlympiad">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '完成挑战 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page" style="text-align:center; padding-top:50px;">
<div class="emoji">🏆</div>
<div class="title">奥数挑战完成!</div>
<div class="final-score" style="font-size:40px; color:#ed8936; font-weight:800; margin:20px 0;">{{olympiadScore}}分</div>
<button class="restart-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<div v-show="currentPage === 6" class="secrets-container">
<div class="secret-card secret-bg-1">
<div class="secret-emoji">🧮</div>
<div class="secret-title">秘籍 1:前加后</div>
<div class="secret-text">
看到数字忽大忽小乱跳,试试把前面两个加起来。
<br><br>
F(n) = F(n-1) + F(n-2)
</div>
<div class="swipe-hint">← 向左滑动查看更多</div>
</div>
<div class="secret-card secret-bg-2">
<div class="secret-emoji">📏</div>
<div class="secret-title">秘籍 2:看差值</div>
<div class="secret-text">
如果不是前加后,就算算相邻两个数相差多少。
<br><br>
看看差有没有规律 (比如 +1, +2, +3...)
</div>
<div class="swipe-hint">← 向左滑动查看更多</div>
</div>
<div class="secret-card secret-bg-3">
<div class="secret-emoji">🔢</div>
<div class="secret-title">秘籍 3:奇偶排队</div>
<div class="secret-text">
如果问第几百个数是奇数还是偶数,千万别硬算!
<br><br>
找找“奇奇偶”这样的循环规律。
</div>
</div>
</div>
</div>
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<div class="nav-icon">💡</div><div class="nav-label">概念</div>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<div class="nav-icon">🎬</div><div class="nav-label">演示</div>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<div class="nav-icon">📝</div><div class="nav-label">讲解</div>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<div class="nav-icon">✏️</div><div class="nav-label">练习</div>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<div class="nav-icon">🏆</div><div class="nav-label">奥数</div>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<div class="nav-icon">🎁</div><div class="nav-label">秘籍</div>
</div>
</div>
</div>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/vue.global.prod.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/gsap.min.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/confetti.browser.min.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
currentPage: 1,
// 演示页状态
demoTab: 'direct',
directStep: 0,
theoryStep: 0,
directSteps: [
'第1个月:有一对刚出生的小兔子(用白色表示)。',
'第2个月:小兔子长大了(变成粉色),但还不能生宝宝,所以还是1对。',
'第3个月:这对成年兔子生了1对新兔子(白色)。现在总共有2对兔子。',
'第4个月:最初的成年兔子又生了1对(白色),上个月出生的小兔子长大了(粉色)。现在总共3对。',
'第5个月:现在有2对成年兔子(粉色),它们各生1对(白色),加上原来的小兔子长大...看,兔子越来越多啦!',
'看,数字的轨迹连起来,像不像一个旋转的贝壳?这就是神奇的斐波那契螺旋线!'
],
directFormulas: [
'兔子对数 = 1',
'兔子对数 = 1',
'1 + 1 = 2',
'1 + 2 = 3',
'2 + 3 = 5',
'数列:1, 1, 2, 3, 5, 8...'
],
theorySteps: [
'我们来看数列:1, 1。这是开始的两个数。',
'第三个数:把前两个数相加。1 + 1 = 2。',
'第四个数:把第二、三个数相加。1 + 2 = 3。',
'第五个数:把第三、四个数相加。2 + 3 = 5。',
'第六个数:把第四、五个数相加。3 + 5 = 8。',
'看到了吗?每个新数字都是前面两个数字的“和”。这就是规律!'
],
theoryFormulas: [
'开始:1, 1',
'1 + 1 = 2',
'1 + 2 = 3',
'2 + 3 = 5',
'3 + 5 = 8',
'规律:F(n) = F(n-1) + F(n-2)'
],
// 讲解页状态
explainTab: 'direct',
exampleOpen: [false, false, false],
// 练习题数据与状态
practiceQuestions: [
{
text: "1, 1, 2, 3, 5, 8, ( )",
options: [
{text: "A. 11", correct: false},
{text: "B. 12", correct: false},
{text: "C. 13", correct: true},
{text: "D. 14", correct: false}
],
explanation: "提示3:13。因为5 + 8 = 13,符合前两数之和的规律。"
},
{
text: "2, 2, 4, 6, 10, ( )",
options: [
{text: "A. 14", correct: false},
{text: "B. 16", correct: true},
{text: "C. 18", correct: false},
{text: "D. 12", correct: false}
],
explanation: "提示3:16。因为6 + 10 = 16,符合前两数之和的规律。"
},
{
text: "1, 3, 4, 7, 11, ( )",
options: [
{text: "A. 15", correct: false},
{text: "B. 16", correct: false},
{text: "C. 18", correct: true},
{text: "D. 19", correct: false}
],
explanation: "提示3:18。因为7 + 11 = 18,符合前两数之和的规律。"
},
{
text: "1, 2, 3, 6, 11, 20, ( )",
options: [
{text: "A. 31", correct: false},
{text: "B. 37", correct: true},
{text: "C. 30", correct: false},
{text: "D. 25", correct: false}
],
explanation: "提示3:6+11+20=37。这是前三个数相加的规律,要仔细辨别。"
},
{
text: "5, 10, 15, 25, 40, ( )",
options: [
{text: "A. 60", correct: false},
{text: "B. 65", correct: true},
{text: "C. 55", correct: false},
{text: "D. 50", correct: false}
],
explanation: "提示3:25+40=65。观察发现从第三项开始,每一项是前两项之和。"
},
{
text: "3, 6, 9, 15, 24, ( )",
options: [
{text: "A. 30", correct: false},
{text: "B. 33", correct: false},
{text: "C. 39", correct: true},
{text: "D. 36", correct: false}
],
explanation: "提示3:15+24=39。符合前两数之和的规律。"
},
{
text: "1, 2, 4, 8, 16, ( )",
options: [
{text: "A. 20", correct: false},
{text: "B. 24", correct: false},
{text: "C. 32", correct: true},
{text: "D. 30", correct: false}
],
explanation: "提示3:16×2=32。这是倍数规律(每一项是前一项的2倍),不是加法规律,注意区分。"
},
{
text: "0, 1, 1, 2, 3, 5, ( ), 13",
options: [
{text: "A. 6", correct: false},
{text: "B. 7", correct: false},
{text: "C. 8", correct: true},
{text: "D. 9", correct: false}
],
explanation: "提示3:8。因为3+5=8,是最标准的斐波那契数列。"
}
],
currentQuestion: 0,
answered: false,
selectedOption: null,
isCorrect: false,
score: 0,
practiceCompleted: false,
// 奥数题数据与状态
olympiadQuestions: [
{
text: "有一列数:1, 2, 4, 7, 11, 16... 第 8 个数是多少?",
options: [
{text: "A. 22", correct: false},
{text: "B. 29", correct: true},
{text: "C. 25", correct: false},
{text: "D. 30", correct: false}
],
hint1: "差是 1, 2, 3, 4, 5...",
hint2: "第7个数加6,第8个数加7。",
explanation: "完整解析:二级等差数列。差是 1, 2, 3, 4, 5... 第6个数16,第7个数是16+6=22,第8个数是22+7=29。",
difficulty: 2,
difficultyText: "⭐⭐"
},
{
text: "观察规律:1, 4, 9, 16, 25, ( )",
options: [
{text: "A. 30", correct: false},
{text: "B. 36", correct: true},
{text: "C. 40", correct: false},
{text: "D. 49", correct: false}
],
hint1: "1x1, 2x2, 3x3...",
hint2: "5x5=25。",
explanation: "完整解析:平方数。规律是1²=1, 2²=4, 3²=9, 4²=16, 5²=25,所以下一个是6²=36。",
difficulty: 1,
difficultyText: "⭐"
},
{
text: "有一列数:1, 1, 2, 3, 5, 8, 13, 21... 问第 20 个数是奇数还是偶数?",
options: [
{text: "A. 奇数", correct: true},
{text: "B. 偶数", correct: false},
{text: "C. 不确定", correct: false},
{text: "D. 无法计算", correct: false}
],
hint1: "奇, 奇, 偶, 奇, 奇, 偶...",
hint2: "每3个一组循环。",
explanation: "完整解析:奇偶性规律。数列奇偶性为:奇, 奇, 偶, 奇, 奇, 偶, 奇, 奇, 偶... 每3个数(奇奇偶)一组循环。20÷3=6...2,余数是2,对应循环里的第二个数,是奇数。",
difficulty: 3,
difficultyText: "⭐⭐⭐"
},
{
text: "1, 2, 5, 12, 27, ( )",
options: [
{text: "A. 50", correct: false},
{text: "B. 54", correct: false},
{text: "C. 58", correct: true},
{text: "D. 60", correct: false}
],
hint1: "1×2+0=2。",
hint2: "2×2+1=5, 5×2+2=12, 12×2+3=27。",
explanation: "完整解析:倍数加减。规律是:前一项×2,再加上一个递增的数(0,1,2,3...)。所以27×2+4=58。",
difficulty: 3,
difficultyText: "⭐⭐⭐"
},
{
text: "括号里填什么? 2, 3, 5, 9, 17, ( )",
options: [
{text: "A. 30", correct: false},
{text: "B. 33", correct: true},
{text: "C. 35", correct: false},
{text: "D. 32", correct: false}
],
hint1: "2×2-1=3。",
hint2: "3×2-1=5...",
explanation: "完整解析:前数×2-1。规律是:后一项 = 前一项 × 2 - 1。所以17×2-1=33。",
difficulty: 2,
difficultyText: "⭐⭐"
},
{
text: "数列 1, 1, 2, 3, 5, 8, 13... 请问前 10 个数的和是多少?",
options: [
{text: "A. 88", correct: false},
{text: "B. 143", correct: true},
{text: "C. 231", correct: false},
{text: "D. 100", correct: false}
],
hint1: "1+1+2+3+5+8+13+21+34+55。",
hint2: "列竖式计算。",
explanation: "完整解析:直接加或利用性质(性质太难,直接加)。前10个数为:1,1,2,3,5,8,13,21,34,55。它们的和是143。有一个有趣的性质:前n项和 = 第(n+2)项 - 1。这里第12项是144,144-1=143。",
difficulty: 2,
difficultyText: "⭐⭐"
},
{
text: "3, 6, 18, 72, ( )",
options: [
{text: "A. 144", correct: false},
{text: "B. 216", correct: false},
{text: "C. 360", correct: true},
{text: "D. 288", correct: false}
],
hint1: "3×2=6。",
hint2: "6×3=18, 18×4=72。",
explanation: "完整解析:倍数递增。规律是:后一项 = 前一项 × (2,3,4,5...),乘数依次递增。所以72×5=360。",
difficulty: 2,
difficultyText: "⭐⭐"
},
{
text: "小明爬楼梯,一次可以爬 1 级或 2 级。爬到 3 楼(2层楼梯)有 2 种方法。爬到 5 楼(4层楼梯)有几种方法?",
options: [
{text: "A. 3", correct: false},
{text: "B. 5", correct: true},
{text: "C. 8", correct: false},
{text: "D. 4", correct: false}
],
hint1: "1级:1种;2级:2种。",
hint2: "3级:1+2=3种;4级:2+3=5种。",
explanation: "完整解析:这是典型的斐波那契应用。爬n级台阶的方法数:F(1)=1, F(2)=2, F(3)=3, F(4)=5, F(5)=8... 所以爬4级(到5楼)有5种方法。",
difficulty: 3,
difficultyText: "⭐⭐⭐"
}
],
currentOlympiad: 0,
olympiadAnswered: false,
selectedOlympiadOption: null,
isOlympiadCorrect: false,
wrongAttempts: 0,
olympiadScore: 0,
olympiadCompleted: false
};
},
mounted() {
// 初始化时,运行第一次动画
this.$nextTick(() => {
this.runDirectAnimation();
this.runTheoryAnimation();
});
},
methods: {
switchPage(page) {
this.stopSpeak();
this.currentPage = page;
if (page === 2) {
this.directStep = 0;
this.theoryStep = 0;
this.$nextTick(() => {
this.runDirectAnimation();
this.runTheoryAnimation();
});
}
// 切换到练习或奥数页时,重置相关状态(如果未完成)
if (page === 4 && !this.practiceCompleted) {
this.currentQuestion = 0;
this.answered = false;
this.score = 0;
}
if (page === 5 && !this.olympiadCompleted) {
this.currentOlympiad = 0;
this.olympiadAnswered = false;
this.wrongAttempts = 0;
this.olympiadScore = 0;
}
},
// 停止语音
stopSpeak() {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
const audio = document.getElementById('tts-audio');
if (audio) {
audio.pause();
audio.currentTime = 0;
}
} else {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
}
},
switchDemoTab(tab) {
this.stopSpeak();
this.demoTab = tab;
// 切换到对应Tab时,重置步骤并运行动画
if (tab === 'direct') {
this.directStep = 0;
this.$nextTick(() => this.runDirectAnimation());
} else {
this.theoryStep = 0;
this.$nextTick(() => this.runTheoryAnimation());
}
},
// 语音合成
speak(text) {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
let audio = document.getElementById('tts-audio');
if (!audio) {
audio = document.createElement('audio');
audio.id = 'tts-audio';
audio.style.display = 'none';
document.body.appendChild(audio);
}
const url = `https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
audio.play().catch(err => { console.log('语音播放失败:', err); });
} else {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
},
// 讲解页折叠面板
toggleExample(index) {
this.exampleOpen[index] = !this.exampleOpen[index];
},
// GSAP 动画逻辑 - 兔子生长
runDirectAnimation() {
if (typeof gsap === 'undefined') return;
const tl = gsap.timeline();
const svgNS = "http://www.w3.org/2000/svg";
const rabbitsContainer = document.querySelector('#rabbits-container');
const numbersContainer = document.querySelector('#numbers-container');
const monthLabel = document.querySelector('#month-label');
// 清除旧内容
while (rabbitsContainer.firstChild) rabbitsContainer.firstChild.remove();
while (numbersContainer.firstChild) numbersContainer.firstChild.remove();
// 根据当前步骤绘制
const rabbitPairs = this.directStep + 1; // 简化演示
const numbers = [1, 1, 2, 3, 5, 8];
const spiralPoints = [
{x:200, y:150},
{x:230, y:150},
{x:260, y:170},
{x:270, y:200},
{x:260, y:230},
{x:230, y:250}
];
// 更新月份标签
monthLabel.textContent = `第 ${Math.min(this.directStep + 1, 6)} 个月`;
// 步骤0-4:绘制兔子
if (this.directStep < 5) {
for (let i = 0; i < rabbitPairs; i++) {
const x = 80 + (i % 4) * 70;
const y = 60 + Math.floor(i / 4) * 80;
// 成年兔子(粉色)
const adult = document.createElementNS(svgNS, 'circle');
adult.setAttribute('cx', x);
adult.setAttribute('cy', y);
adult.setAttribute('r', 12);
adult.setAttribute('fill', '#ffb6c1');
adult.setAttribute('filter', 'url(#shadow)');
rabbitsContainer.appendChild(adult);
// 如果i>0,添加小兔子(白色)
if (this.directStep >= 2 && i < rabbitPairs - 1) {
const baby = document.createElementNS(svgNS, 'circle');
baby.setAttribute('cx', x + 25);
baby.setAttribute('cy', y);
baby.setAttribute('r', 10);
baby.setAttribute('fill', 'white');
baby.setAttribute('stroke', '#ccc');
baby.setAttribute('stroke-width', '1');
rabbitsContainer.appendChild(baby);
}
}
}
// 步骤5:绘制数字螺旋线
if (this.directStep === 5) {
// 绘制数字
for (let i = 0; i <= this.directStep; i++) {
const point = spiralPoints[i];
const num = document.createElementNS(svgNS, 'text');
num.setAttribute('x', point.x);
num.setAttribute('y', point.y);
num.setAttribute('text-anchor', 'middle');
num.setAttribute('font-size', '20');
num.setAttribute('font-weight', 'bold');
num.setAttribute('fill', '#667eea');
num.textContent = numbers[i];
numbersContainer.appendChild(num);
// 绘制螺旋线(用圆弧模拟)
if (i > 0) {
const prevPoint = spiralPoints[i-1];
const path = document.createElementNS(svgNS, 'path');
const rx = Math.abs(point.x - prevPoint.x) / 2;
const ry = Math.abs(point.y - prevPoint.y) / 2;
path.setAttribute('d', `M ${prevPoint.x} ${prevPoint.y} A ${rx} ${ry} 0 0 1 ${point.x} ${point.y}`);
path.setAttribute('stroke', '#667eea');
path.setAttribute('stroke-width', '2');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-dasharray', '5,5');
numbersContainer.appendChild(path);
}
}
// 添加一个螺旋形图案
const spiral = document.createElementNS(svgNS, 'path');
spiral.setAttribute('d', 'M200,150 Q230,150 260,170 T270,200 T260,230 T230,250');
spiral.setAttribute('stroke', '#ff9800');
spiral.setAttribute('stroke-width', '3');
spiral.setAttribute('fill', 'none');
numbersContainer.appendChild(spiral);
}
// 应用动画效果
tl.fromTo(rabbitsContainer.children, {opacity:0, scale:0}, {opacity:1, scale:1, duration:0.6, stagger:0.15, ease:"bounce.out"})
.fromTo(numbersContainer.children, {opacity:0}, {opacity:1, duration:0.5, stagger:0.2}, "-=0.3");
},
// GSAP 动画逻辑 - 数列规律
runTheoryAnimation() {
if (typeof gsap === 'undefined') return;
const tl = gsap.timeline();
const svgNS = "http://www.w3.org/2000/svg";
const numbersContainer = document.querySelector('#theory-numbers');
// 清除旧内容
while (numbersContainer.firstChild) numbersContainer.firstChild.remove();
const numbers = [1, 1, 2, 3, 5, 8];
const startX = 80;
const yPos = 150;
const spacing = 60;
// 绘制当前步骤可见的数字
for (let i = 0; i <= Math.min(this.theoryStep + 1, numbers.length - 1); i++) {
const x = startX + i * spacing;
// 数字背景
const bg = document.createElementNS(svgNS, 'circle');
bg.setAttribute('cx', x);
bg.setAttribute('cy', yPos);
bg.setAttribute('r', 20);
bg.setAttribute('fill', i <= this.theoryStep ? '#e0e7ff' : '#f0f4ff');
bg.setAttribute('stroke', i <= this.theoryStep ? '#667eea' : '#ccc');
bg.setAttribute('stroke-width', '2');
numbersContainer.appendChild(bg);
// 数字文本
const num = document.createElementNS(svgNS, 'text');
num.setAttribute('x', x);
num.setAttribute('y', yPos + 5);
num.setAttribute('text-anchor', 'middle');
num.setAttribute('font-size', '18');
num.setAttribute('font-weight', 'bold');
num.setAttribute('fill', i <= this.theoryStep ? '#667eea' : '#999');
num.textContent = numbers[i];
numbersContainer.appendChild(num);
// 下标
const sub = document.createElementNS(svgNS, 'text');
sub.setAttribute('x', x + 15);
sub.setAttribute('y', yPos - 15);
sub.setAttribute('text-anchor', 'middle');
sub.setAttribute('font-size', '12');
sub.setAttribute('fill', '#888');
sub.textContent = i+1;
numbersContainer.appendChild(sub);
}
// 步骤1以上:绘制加号和箭头
if (this.theoryStep >= 1 && this.theoryStep <= 4) {
const x1 = startX + (this.theoryStep - 1) * spacing;
const x2 = startX + this.theoryStep * spacing;
const x3 = startX + (this.theoryStep + 1) * spacing;
// 加号
const plus = document.createElementNS(svgNS, 'text');
plus.setAttribute('x', (x1 + x2) / 2);
plus.setAttribute('y', yPos - 40);
plus.setAttribute('text-anchor', 'middle');
plus.setAttribute('font-size', '24');
plus.setAttribute('font-weight', 'bold');
plus.setAttribute('fill', '#ff9800');
plus.textContent = '+';
numbersContainer.appendChild(plus);
// 等号
const equals = document.createElementNS(svgNS, 'text');
equals.setAttribute('x', (x2 + x3) / 2);
equals.setAttribute('y', yPos - 40);
equals.setAttribute('text-anchor', 'middle');
equals.setAttribute('font-size', '24');
equals.setAttribute('font-weight', 'bold');
equals.setAttribute('fill', '#ff9800');
equals.textContent = '=';
numbersContainer.appendChild(equals);
// 箭头
const arrow = document.createElementNS(svgNS, 'line');
arrow.setAttribute('x1', x1);
arrow.setAttribute('y1', yPos - 25);
arrow.setAttribute('x2', x2);
arrow.setAttribute('y2', yPos - 25);
arrow.setAttribute('stroke', '#667eea');
arrow.setAttribute('stroke-width', '2');
arrow.setAttribute('marker-end', 'url(#arrowhead)');
numbersContainer.appendChild(arrow);
const arrow2 = document.createElementNS(svgNS, 'line');
arrow2.setAttribute('x1', x2);
arrow2.setAttribute('y1', yPos - 25);
arrow2.setAttribute('x2', x3);
arrow2.setAttribute('y2', yPos - 25);
arrow2.setAttribute('stroke', '#667eea');
arrow2.setAttribute('stroke-width', '2');
numbersContainer.appendChild(arrow2);
}
// 应用动画效果
tl.fromTo(numbersContainer.querySelectorAll('circle, text'), {opacity:0, y: -20}, {opacity:1, y:0, duration:0.5, stagger:0.1, ease:"back.out(1.7)"});
if (this.theoryStep >= 1) {
tl.fromTo(numbersContainer.querySelectorAll('line'), {opacity:0, scaleX:0}, {opacity:1, scaleX:1, duration:0.5, ease:"power2.out"}, "-=0.2");
}
},
// 步骤导航
nextDirectStep() {
if (this.directStep < this.directSteps.length - 1) {
this.directStep++;
this.runDirectAnimation();
// 注意:根据要求,此处不自动播放语音
// this.speak(this.directSteps[this.directStep]);
}
},
prevDirectStep() {
if (this.directStep > 0) {
this.directStep--;
this.runDirectAnimation();
// 注意:根据要求,此处不自动播放语音
// this.speak(this.directSteps[this.directStep]);
}
},
nextTheoryStep() {
if (this.theoryStep < this.theorySteps.length - 1) {
this.theoryStep++;
this.runTheoryAnimation();
}
},
prevTheoryStep() {
if (this.theoryStep > 0) {
this.theoryStep--;
this.runTheoryAnimation();
}
},
// 练习题逻辑
selectOption(index, correct) {
if (this.answered) return;
this.answered = true;
this.selectedOption = index;
this.isCorrect = correct;
if (correct) {
this.score += 20;
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
}
}
},
nextQuestion() {
if (this.currentQuestion < this.practiceQuestions.length - 1) {
this.currentQuestion++;
this.answered = false;
this.selectedOption = null;
} else {
this.practiceCompleted = true;
}
},
// 奥数题逻辑
selectOlympiadOption(index, correct) {
if (this.olympiadAnswered) return;
this.selectedOlympiadOption = index;
if (correct) {
this.olympiadAnswered = true;
this.isOlympiadCorrect = true;
// 得分:第一次答对30分,使用提示后答对分数递减
const baseScore = 30;
const deduction = this.wrongAttempts * 10;
this.olympiadScore += Math.max(baseScore - deduction, 10);
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 150, spread: 80, origin: { y: 0.6 } });
}
} else {
this.wrongAttempts++;
// 错误反馈动画
const optionBtn = document.querySelectorAll('.practice-page .option-btn')[index];
if (optionBtn && this.wrongAttempts < 3) {
gsap.to(optionBtn, { x: -5, duration: 0.1, yoyo: true, repeat: 5 });
}
// 错误3次后自动显示答案
if (this.wrongAttempts >= 3) {
this.olympiadAnswered = true;
this.isOlympiadCorrect = false;
}
}
},
nextOlympiad() {
if (this.currentOlympiad < this.olympiadQuestions.length - 1) {
this.currentOlympiad++;
this.olympiadAnswered = false;
this.selectedOlympiadOption = null;
this.wrongAttempts = 0;
} else {
this.olympiadCompleted = true;
}
}
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码