<!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>
/* ====================
1. 基础架构与背景
==================== */
:root {
--primary-color: #6a11cb;
--accent-pink: #ff9a9e;
--accent-blue: #a18cd1;
--bg-color: #fdfbf7;
--card-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.08);
--card-radius: 24px;
}
* { 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-color: var(--bg-color);
/* 升级后的柔和点阵背景 */
background-image:
radial-gradient(#e0e0e0 1.5px, transparent 1.5px),
radial-gradient(#e0e0e0 1.5px, transparent 1.5px);
background-size: 24px 24px;
background-position: 0 0, 12px 12px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden; height: 100vh;
color: #4a4a4a;
}
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: rgba(255,255,255,0.85); backdrop-filter: blur(10px); display: flex; flex-direction: column; box-shadow: 0 0 50px rgba(0,0,0,0.05); }
.content-area { flex: 1; overflow-y: auto; padding-bottom: 90px; scroll-behavior: smooth; position: relative; }
/* ====================
2. 标题栏与通用组件
==================== */
.page-header {
padding: 30px 20px 40px;
text-align: center;
background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);
color: white;
border-bottom-left-radius: 40px;
border-bottom-right-radius: 40px;
box-shadow: 0 10px 20px -5px rgba(161, 140, 209, 0.4);
margin-bottom: -20px; /* 让内容卡片上浮遮盖一点 */
position: relative;
z-index: 1;
}
.page-header h1 { margin: 0; font-size: 26px; font-weight: 800; text-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.page-header p { margin: 8px 0 0 0; font-size: 14px; opacity: 0.9; }
/* 页面过渡 */
.page-enter { animation: slideUpFade 0.5s cubic-bezier(0.2, 0.8, 0.2, 1); }
@keyframes slideUpFade { from { opacity: 0; transform: translateY(20px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
/* 按钮通用样式 */
button { font-family: inherit; }
.btn-press-anim:active { transform: scale(0.95); transition: transform 0.1s; }
/* ====================
3. 演示动画 SVG 区域 (升级版)
==================== */
.animation-area {
background: #ffffff;
border-radius: var(--card-radius);
padding: 20px;
height: 340px; /* 固定高度 */
position: relative;
margin: 20px;
box-shadow: var(--card-shadow);
border: 1px solid rgba(0,0,0,0.03);
display: flex; flex-direction: column; align-items: center; justify-content: center;
overflow: hidden;
}
.svg-container { width: 100%; height: 100%; overflow: visible; }
/* 演示控制按钮 */
.controls-wrapper {
position: absolute; bottom: 20px; width: 100%; padding: 0 30px;
display: flex; justify-content: space-between; pointer-events: none;
}
.step-btn {
pointer-events: auto;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none;
width: 48px; height: 48px; border-radius: 50%;
font-size: 20px; display: flex; align-items: center; justify-content: center;
box-shadow: 0 6px 15px rgba(118, 75, 162, 0.3);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer;
}
.step-btn:active { transform: scale(0.9); }
.step-btn:disabled { background: #e0e0e0; box-shadow: none; cursor: not-allowed; opacity: 0.6; }
.step-indicator {
position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
background: rgba(106, 17, 203, 0.1); color: #6a11cb;
padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: bold;
}
/* ====================
4. 讲解页 & 选项卡
==================== */
.tab-nav {
display: flex; background: #f0f2f5; border-radius: 50px;
margin: 30px 20px 20px; padding: 6px;
position: relative; z-index: 2;
}
.tab-item {
flex: 1; text-align: center; padding: 10px; border-radius: 40px;
cursor: pointer; font-weight: 600; font-size: 14px; color: #8898aa;
transition: all 0.3s ease;
}
.tab-item.active { background: white; color: #6a11cb; box-shadow: 0 4px 10px rgba(0,0,0,0.05); }
.concept-card, .expand-card {
background: white; border-radius: var(--card-radius);
margin-bottom: 20px; overflow: hidden;
box-shadow: var(--card-shadow);
transition: transform 0.3s;
}
.concept-header { padding: 18px 24px; color: white; font-weight: bold; font-size: 17px; display: flex; justify-content: space-between; }
.bg-pink { background: linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%); }
.bg-blue { background: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%); }
.bg-purple { background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); }
.highlight-box {
background: #fff8e1; border-left: 5px solid #ffc107;
padding: 16px; border-radius: 8px; margin: 15px 0;
color: #d35400; font-size: 15px; line-height: 1.6;
}
.formula-text { display: block; margin-top: 8px; font-size: 18px; font-weight: bold; color: #e67e22; font-family: "Courier New", monospace; }
/* 折叠卡片样式 */
.card-header { padding: 20px 24px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; }
.card-title { font-weight: 700; color: #333; }
.toggle-icon { transition: transform 0.3s; color: #ccc; }
.expand-card.active .toggle-icon { transform: rotate(180deg); color: #6a11cb; }
.card-content { padding: 0 24px 24px; border-top: 1px dashed #eee; display: none; color: #555; line-height: 1.6; }
.expand-card.active .card-content { display: block; animation: fadeIn 0.3s; }
/* ====================
5. 练习题 & 选项
==================== */
.practice-page { padding: 20px; }
.question-card {
background: white; border-radius: var(--card-radius); padding: 30px 24px;
box-shadow: var(--card-shadow);
}
.question-text { font-size: 18px; line-height: 1.6; margin: 20px 0; font-weight: 600; color: #2d3436; }
.option-btn {
width: 100%; padding: 18px 20px; margin-bottom: 12px;
border: 2px solid #f0f2f5; border-radius: 16px;
background: white; font-size: 16px; text-align: left;
transition: all 0.2s; color: #636e72; position: relative;
}
.option-btn:active { transform: scale(0.98); }
.option-btn.correct { background: #e3f9e5; border-color: #2ecc71; color: #27ae60; font-weight: bold; }
.option-btn.correct::after { content: '✔'; position: absolute; right: 20px; }
.option-btn.wrong { background: #ffebee; border-color: #ff7675; color: #d63031; }
.option-btn.wrong::after { content: '✘'; position: absolute; right: 20px; }
.feedback-area { margin-top: 20px; padding: 15px; border-radius: 12px; font-size: 15px; line-height: 1.5; }
.feedback-correct { background: #e8f5e9; color: #2e7d32; }
.feedback-wrong { background: #ffebee; color: #c62828; }
.next-btn {
width: 100%; padding: 18px; margin-top: 25px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none; border-radius: 50px;
font-size: 16px; font-weight: bold; cursor: pointer;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
/* ====================
6. 秘籍页面 (核心修改:左右滑动)
==================== */
.secret-page { overflow: hidden; }
.secret-scroll-wrapper {
position: relative;
width: 100%;
padding-bottom: 20px;
}
.secret-container {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 20px;
padding: 20px 20px 40px 20px; /* 增加底部padding给阴影留空间 */
width: 100%;
/* 隐藏滚动条 */
-webkit-overflow-scrolling: touch;
}
.secret-container::-webkit-scrollbar { display: none; }
.secret-card {
min-width: 100%; /* 关键:占满容器宽度 */
flex-shrink: 0;
scroll-snap-align: center;
height: 500px; /* 固定高度 */
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: 24px;
padding: 30px;
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.15);
display: flex;
flex-direction: column;
justify-content: center; align-items: center;
position: relative;
transform-style: preserve-3d;
}
.secret-card h3 {
font-size: 28px; color: #6a11cb; margin-bottom: 20px;
background: rgba(255,255,255,0.6); padding: 5px 20px; border-radius: 20px;
}
.secret-card p { font-size: 20px; line-height: 1.8; color: #555; text-align: center; font-weight: 500; }
/* 滑动提示 */
.swipe-hint {
text-align: center; color: #999; font-size: 12px; margin-top: -30px;
animation: bounce 2s infinite;
}
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 40% {transform: translateY(-5px);} 60% {transform: translateY(-3px);} }
/* ====================
7. 底部导航
==================== */
.bottom-nav {
position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
width: 100%; max-width: 480px; height: 80px;
background: rgba(255,255,255,0.95);
backdrop-filter: blur(20px);
border-top: 1px solid rgba(0,0,0,0.05);
display: flex; justify-content: space-around; align-items: flex-start;
padding-top: 12px;
z-index: 9999;
box-shadow: 0 -5px 20px rgba(0,0,0,0.03);
border-top-left-radius: 24px; border-top-right-radius: 24px;
}
.nav-item {
flex: 1; display: flex; flex-direction: column; align-items: center;
cursor: pointer; color: #b2bec3; font-size: 11px; transition: all 0.3s;
}
.nav-item i { font-size: 22px; margin-bottom: 4px; transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
.nav-item.active { color: #6a11cb; font-weight: 700; }
.nav-item.active i { transform: translateY(-5px); color: #6a11cb; }
/* ====================
8. 引入页
==================== */
.intro-page { padding: 40px 20px; text-align: center; }
.intro-emoji { font-size: 80px; margin-bottom: 20px; filter: drop-shadow(0 5px 15px rgba(0,0,0,0.1)); }
.intro-story {
background: white; padding: 30px; border-radius: 24px;
box-shadow: var(--card-shadow); text-align: left;
font-size: 16px; line-height: 1.8; color: #555;
}
.listen-btn {
margin-top: 30px; padding: 15px 40px; border-radius: 50px;
background: linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%);
color: white; font-weight: bold; border: none; box-shadow: 0 5px 15px rgba(102, 166, 255, 0.4);
font-size: 16px; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
}
/* 振动动画 */
@keyframes shake { 0%, 100% {transform: translateX(0);} 25% {transform: translateX(-5px);} 75% {transform: translateX(5px);} }
.shake { animation: shake 0.4s ease-in-out; }
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="intro-page page-enter">
<div class="intro-emoji">🧩</div>
<div class="intro-story">
<h2 style="margin-top:0; color:#6a11cb;">乐高积木的秘密</h2>
<p>小明有一盒乐高积木,他想知道能拼出多少种不同的形状。</p>
<p>如果乱数一通,不是数重复,就是数漏掉,这可怎么办?</p>
<div style="background:#f0f2f5; padding:15px; border-radius:12px; margin-top:15px;">
<i class="fas fa-lightbulb" style="color:#f1c40f"></i>
<strong>关键点:</strong>就像数大楼的窗户,我们需要<strong>有序、不重不漏</strong>的方法!
</div>
</div>
<button class="listen-btn btn-press-anim" @click="speak('欢迎来到巧数图形课堂!我们将学习如何有序、不重复、不遗漏地数清楚线段、三角形和长方形。')">
<i class="fas fa-volume-up"></i> 听老师讲解
</button>
</div>
<div v-show="currentPage === 2" class="demo-page page-enter">
<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 v-show="demoTab === 'direct'">
<div class="animation-area">
<div class="step-indicator">步骤 {{directStep + 1}} / 4</div>
<svg class="svg-container" viewBox="0 0 400 250" id="line-demo-svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#ff9a9e;stop-opacity:1" />
<stop offset="100%" style="stop-color:#fecfef;stop-opacity:1" />
</linearGradient>
<marker id="dot" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6">
<circle cx="5" cy="5" r="5" fill="#6a11cb" />
</marker>
</defs>
<line x1="50" y1="125" x2="350" y2="125" stroke="#e0e0e0" stroke-width="4" stroke-linecap="round" id="base-line"/>
<circle cx="150" cy="125" r="6" fill="#6a11cb" id="dot1" style="opacity:0"/>
<circle cx="250" cy="125" r="6" fill="#6a11cb" id="dot2" style="opacity:0"/>
<g id="group-basic" style="opacity:0">
<path d="M50,110 Q100,80 150,110" fill="none" stroke="#ff9a9e" stroke-width="3" />
<text x="100" y="75" text-anchor="middle" fill="#ff9a9e" font-weight="bold">1</text>
<path d="M150,110 Q200,80 250,110" fill="none" stroke="#ff9a9e" stroke-width="3" />
<text x="200" y="75" text-anchor="middle" fill="#ff9a9e" font-weight="bold">2</text>
<path d="M250,110 Q300,80 350,110" fill="none" stroke="#ff9a9e" stroke-width="3" />
<text x="300" y="75" text-anchor="middle" fill="#ff9a9e" font-weight="bold">3</text>
</g>
<g id="group-combo" style="opacity:0">
<path d="M50,135 Q150,165 250,135" fill="none" stroke="#a18cd1" stroke-width="3" stroke-dasharray="5,5"/>
<path d="M150,135 Q250,165 350,135" fill="none" stroke="#a18cd1" stroke-width="3" stroke-dasharray="5,5"/>
</g>
<g id="group-whole" style="opacity:0">
<path d="M50,150 Q200,200 350,150" fill="none" stroke="#4facfe" stroke-width="4"/>
</g>
<text x="200" y="220" text-anchor="middle" fill="#333" font-size="20" font-weight="bold" id="formula-text" opacity="0"></text>
</svg>
<div class="controls-wrapper">
<button class="step-btn prev" @click="prevDirectStep" :disabled="directStep === 0"><i class="fas fa-chevron-left"></i></button>
<button class="step-btn next" @click="nextDirectStep" :disabled="directStep >= 3"><i class="fas fa-chevron-right"></i></button>
</div>
</div>
<div class="demo-section" style="padding: 0 20px;">
<div class="concept-card">
<div class="concept-header bg-pink">
<span><i class="fas fa-magic"></i> 规律总结</span>
</div>
<div class="concept-body" style="padding:20px;">
<div class="highlight-box">
一条线上有3段基本线段。<br>
<span class="formula-text">总数 = 3 + 2 + 1 = 6</span>
</div>
</div>
</div>
</div>
</div>
<div v-show="demoTab === 'theory'" style="padding: 20px;">
<div class="concept-card">
<div class="concept-header bg-purple">
<span>🚀 核心原理:加法原理</span>
</div>
<div class="concept-body" style="padding:20px;">
<p style="margin-top:0; color:#666;">这就像堆积木,从底层开始数:</p>
<ul style="padding-left:20px; color:#555; line-height:1.8;">
<li>第一层(数单个):<strong style="color:#ff9a9e">3个</strong></li>
<li>第二层(数两个拼):<strong style="color:#a18cd1">2个</strong></li>
<li>第三层(数三个拼):<strong style="color:#4facfe">1个</strong></li>
</ul>
<div class="formula-text" style="text-align:center; border-top:1px dashed #ccc; padding-top:10px;">
公式:n + (n-1) + ... + 1
</div>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 3" class="explain-page page-enter">
<div style="padding: 20px;">
<div class="expand-card" :class="{active: expandedCard === 0}" @click="toggleCard(0)">
<div class="card-header">
<span class="card-title"><span style="color:#4facfe; margin-right:8px;">●</span>基础题:数线段</span>
<span class="toggle-icon"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="card-content" @click.stop>
<p><strong>题目:</strong>一条线上有4个点,共有几条线段?</p>
<p><strong>答案:</strong>6条</p>
<div class="highlight-box">
解析:4个点之间有3段间隔。<br>
也就是3条基本线段。<br>
公式:3 + 2 + 1 = 6。
</div>
</div>
</div>
<div class="expand-card" :class="{active: expandedCard === 1}" @click="toggleCard(1)">
<div class="card-header">
<span class="card-title"><span style="color:#a18cd1; margin-right:8px;">●</span>进阶题:数三角形</span>
<span class="toggle-icon"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="card-content" @click.stop>
<p><strong>题目:</strong>大三角形内部画2条线(从顶点到底边),共几个三角形?</p>
<p><strong>答案:</strong>6个</p>
<div class="highlight-box">
解析:看底边!底边被分成了3小段。<br>
和数线段一样:3 + 2 + 1 = 6。
</div>
</div>
</div>
<div class="expand-card" :class="{active: expandedCard === 2}" @click="toggleCard(2)">
<div class="card-header">
<span class="card-title"><span style="color:#ff9a9e; margin-right:8px;">●</span>易错题:数正方形</span>
<span class="toggle-icon"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="card-content" @click.stop>
<p><strong>题目:</strong>田字格(2x2)里有几个正方形?</p>
<p><strong>答案:</strong>5个</p>
<div class="highlight-box">
解析:<br>
1. 小正方形(1x1):4个<br>
2. 大正方形(2x2):1个<br>
总数:4 + 1 = 5个。<br>
<small style="color:#999">*别忘了外面最大的那个!</small>
</div>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="practice-page page-enter">
<div v-if="!practiceCompleted" style="margin-top: 20px;">
<div class="question-card">
<div style="display:flex; justify-content:space-between; margin-bottom:15px; color:#888; font-size:14px;">
<span>题目 {{currentQuestion + 1}} / {{practiceQuestions.length}}</span>
<span style="color:#ff9f43; font-weight:bold;"><i class="fas fa-star"></i> {{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,
'shake': showShake && 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> {{practiceQuestions[currentQuestion].explanation}}</div>
<div v-else><strong>💡 解析:</strong> {{practiceQuestions[currentQuestion].explanation}}</div>
</div>
<button v-if="answered" class="next-btn btn-press-anim" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '查看成绩 🏁'}}
</button>
</div>
</div>
<div v-else class="intro-page">
<div class="intro-emoji">🏆</div>
<h2 style="color:#6a11cb;">练习完成!</h2>
<p style="font-size:24px; color:#ff9f43; font-weight:bold;">最终得分:{{score}}分</p>
<button class="listen-btn btn-press-anim" @click="switchPage(5)">前往奥数挑战 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="practice-page page-enter">
<div v-if="!olympiadCompleted" style="margin-top: 20px;">
<div class="question-card" style="border-top: 5px solid #fda085;">
<div style="display:flex; justify-content:space-between; margin-bottom:15px; color:#888;">
<span style="background:#fff3e0; color:#e67e22; padding:2px 8px; border-radius:4px; font-size:12px;">
{{olympiadQuestions[currentOlympiad].difficultyText}}
</span>
<span style="color:#e67e22; font-weight:bold;"><i class="fas fa-crown"></i> {{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,
'shake': showOlympiadShake && selectedOlympiadOption === index && !option.correct
}"
@click="selectOlympiadOption(index, option.correct)"
:disabled="olympiadAnswered">
{{option.text}}
</button>
</div>
<div v-if="wrongAttempts > 0 && !olympiadAnswered" class="highlight-box" style="background:#e3f2fd; border-color:#2196f3; color:#1565c0;">
<i class="fas fa-info-circle"></i>
<span v-if="wrongAttempts === 1">提示1: {{olympiadQuestions[currentOlympiad].hint1}}</span>
<span v-if="wrongAttempts === 2">提示2: {{olympiadQuestions[currentOlympiad].hint2}}</span>
</div>
<div v-if="olympiadAnswered" class="feedback-area" :class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'">
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
<button v-if="olympiadAnswered || wrongAttempts >= 3" class="next-btn btn-press-anim" @click="nextOlympiad" style="background: linear-gradient(135deg, #f6d365 0%, #fda085 100%);">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '领取奖励 🎁'}}
</button>
</div>
</div>
<div v-else class="intro-page">
<div class="intro-emoji">👑</div>
<h2 style="color:#e67e22;">挑战通关!</h2>
<p style="font-size:24px; color:#e67e22; font-weight:bold;">奥数得分:{{olympiadScore}}分</p>
<button class="listen-btn btn-press-anim" @click="switchPage(6)">查看通关秘籍 🗝️</button>
</div>
</div>
<div v-show="currentPage === 6" class="secret-page page-enter">
<div class="secret-scroll-wrapper">
<div class="secret-container">
<div class="secret-card">
<div style="font-size:60px; margin-bottom:20px;">1️⃣</div>
<h3>标号码法</h3>
<p>给每一个基本图形<br>标上1、2、3...</p>
<p style="font-size:16px; color:#6a11cb;">(按顺序数,绝不乱套)</p>
<svg width="100" height="40" viewBox="0 0 100 40" style="margin-top:20px;">
<circle cx="20" cy="20" r="15" fill="white" opacity="0.5"/>
<text x="20" y="25" text-anchor="middle" fill="#6a11cb" font-weight="bold">1</text>
<circle cx="50" cy="20" r="15" fill="white" opacity="0.5"/>
<text x="50" y="25" text-anchor="middle" fill="#6a11cb" font-weight="bold">2</text>
<circle cx="80" cy="20" r="15" fill="white" opacity="0.5"/>
<text x="80" y="25" text-anchor="middle" fill="#6a11cb" font-weight="bold">3</text>
</svg>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);">
<div style="font-size:60px; margin-bottom:20px;">2️⃣</div>
<h3>加法原理</h3>
<p>数线段、数角、数三角形</p>
<p class="formula-text" style="background:rgba(255,255,255,0.3); padding:10px; border-radius:10px;">n + (n-1) + ... + 1</p>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%);">
<div style="font-size:60px; margin-bottom:20px;">3️⃣</div>
<h3>乘法口诀</h3>
<p>数长方形最简单</p>
<p>长边段数 × 宽边段数</p>
<p style="font-size:16px; color:#2d3436; margin-top:20px;">(正方形要单独数哦!)</p>
</div>
</div>
<div class="swipe-hint">
<i class="fas fa-hand-point-up"></i> 左右滑动
</div>
</div>
<button class="restart-btn" @click="switchPage(1)" style="display:block; margin: 0 auto; padding: 15px 30px; border:none; background:white; color:#6a11cb; border-radius:30px; font-weight:bold; box-shadow:0 5px 15px rgba(0,0,0,0.1);">
<i class="fas fa-undo"></i> 再学一遍
</button>
</div>
</div>
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<i class="fas fa-home"></i>引入
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<i class="fas fa-chalkboard-teacher"></i>演示
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<i class="fas fa-book-open"></i>例题
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<i class="fas fa-pencil-alt"></i>练习
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<i class="fas fa-trophy"></i>挑战
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<i class="fas fa-key"></i>秘籍
</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, computed, nextTick } = Vue;
createApp({
setup() {
// --- 1. 语音 TTS 逻辑 (完全保持原样) ---
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(); }
}
};
// --- 2. 状态管理 ---
const currentPage = ref(1);
const switchPage = (page) => {
stopSpeak();
currentPage.value = page;
if (page === 2) {
directStep.value = 0;
runDirectAnimation();
}
};
// --- 3. 演示动画逻辑 (配合 SVG 升级) ---
const demoTab = ref('direct');
const switchDemoTab = (tab) => {
demoTab.value = tab;
if (tab === 'theory') {
nextTick(() => { runDirectAnimation(); });
}
};
const directStep = ref(0);
const directAnimationTimeline = ref(null);
const nextDirectStep = () => {
if (directStep.value < 3) {
directStep.value++;
runDirectAnimation();
}
};
const prevDirectStep = () => {
if (directStep.value > 0) {
directStep.value--;
runDirectAnimation();
}
};
const runDirectAnimation = () => {
if (directAnimationTimeline.value) {
directAnimationTimeline.value.kill();
}
const tl = gsap.timeline();
directAnimationTimeline.value = tl;
// 初始状态重置
tl.set(['#dot1', '#dot2'], { opacity: 0, scale: 0 });
tl.set(['#group-basic', '#group-combo', '#group-whole'], { opacity: 0 });
tl.set('#formula-text', { opacity: 0, text: "" });
if (directStep.value >= 0) {
// 步骤0: 显示点 (弹跳效果)
tl.to(['#dot1', '#dot2'], { duration: 0.5, opacity: 1, scale: 1, ease: "back.out(1.7)" });
}
if (directStep.value >= 1) {
// 步骤1: 显示基本线段
tl.to('#group-basic', { duration: 0.5, opacity: 1 });
tl.fromTo('#group-basic path',
{ drawSVG: "0%" },
{ duration: 0.5, drawSVG: "100%", stagger: 0.2, ease: "power2.out" }
, "<");
tl.to('#formula-text', { duration: 0.3, opacity: 1, text: "3" });
}
if (directStep.value >= 2) {
// 步骤2: 显示组合线段
tl.to('#group-combo', { duration: 0.5, opacity: 1 });
tl.to('#formula-text', { duration: 0.3, text: "3 + 2" });
}
if (directStep.value >= 3) {
// 步骤3: 显示整体线段
tl.to('#group-whole', { duration: 0.5, opacity: 1 });
tl.to('#formula-text', { duration: 0.3, text: "3 + 2 + 1 = 6" });
// 庆祝彩带
confetti({ particleCount: 50, spread: 50, origin: { y: 0.5, x: 0.5 }, disableForReducedMotion: true });
}
};
// --- 4. 讲解页逻辑 ---
const explainTab = ref('basic');
const expandedCard = ref(null);
const toggleCard = (index) => {
expandedCard.value = expandedCard.value === index ? null : index;
};
// --- 5. 题目数据 (保持原样) ---
const practiceQuestions = [
{
text: "一条线上有5个点,共有几条线段?",
options: [ { text: "A. 5条", correct: false }, { text: "B. 10条", correct: true }, { text: "C. 4条", correct: false }, { text: "D. 8条", correct: false } ],
explanation: "5个点产生4个间隔(基本线段)。总数 = 4 + 3 + 2 + 1 = 10。"
},
{
text: "数三角形,底边有4个小段,共有几个三角形?",
options: [ { text: "A. 4个", correct: false }, { text: "B. 10个", correct: true }, { text: "C. 8个", correct: false }, { text: "D. 6个", correct: false } ],
explanation: "底边有4小段,和数线段原理一样:4 + 3 + 2 + 1 = 10。"
},
{
text: "2x2的方格图中,有多少个长方形(包括正方形)?",
options: [ { text: "A. 4个", correct: false }, { text: "B. 5个", correct: false }, { text: "C. 9个", correct: true }, { text: "D. 8个", correct: false } ],
explanation: "横边线段数(2+1=3) × 竖边线段数(2+1=3) = 9个。"
},
{
text: "从一点引出4条射线,组成几个角?",
options: [ { text: "A. 3个", correct: false }, { text: "B. 4个", correct: false }, { text: "C. 6个", correct: true }, { text: "D. 10个", correct: false } ],
explanation: "4条射线形成3个基本角。总数 = 3 + 2 + 1 = 6。"
},
{
text: "3x3的九宫格里有几个正方形?",
options: [ { text: "A. 9个", correct: false }, { text: "B. 10个", correct: false }, { text: "C. 14个", correct: true }, { text: "D. 13个", correct: false } ],
explanation: "1x1(9个) + 2x2(4个) + 3x3(1个) = 14个。"
}
];
const olympiadQuestions = [
{
text: "数一数,图中有多少个长方形?(图:长边4格,宽边3格)",
options: [ { text: "A. 12", correct: false }, { text: "B. 60", correct: true }, { text: "C. 30", correct: false }, { text: "D. 18", correct: false } ],
explanation: "长边线段:4+3+2+1=10。宽边线段:3+2+1=6。总数:10 × 6 = 60个。",
hint1: "先算长边有多少条线段 (4+...)", hint2: "再算宽边有多少条线段 (3+...)",
difficultyText: "中等"
},
{
text: "一个三角形内部画了2条横线,2条竖线,共几个三角形?",
options: [ { text: "A. 6", correct: false }, { text: "B. 12", correct: false }, { text: "C. 18", correct: true }, { text: "D. 24", correct: false } ],
explanation: "先数单层:底边3段 -> 3+2+1=6个。共有3层,所以 6 × 3 = 18个。",
hint1: "先只看最下面那一层,有几个?", hint2: "数数看一共有几层?",
difficultyText: "困难"
}
];
// --- 6. 答题逻辑 (通用) ---
const currentQuestion = ref(0);
const selectedOption = ref(null);
const answered = ref(false);
const isCorrect = ref(false);
const showShake = ref(false);
const score = ref(0);
const practiceCompleted = ref(false);
const selectOption = (index, correct) => {
if (answered.value) return;
selectedOption.value = index;
answered.value = true;
isCorrect.value = correct;
if (correct) {
score.value += 20;
speak('回答正确!太棒了!');
} else {
showShake.value = true;
setTimeout(() => showShake.value = false, 500);
speak('这道题答错了,来看看解析吧。');
}
};
const nextQuestion = () => {
selectedOption.value = null;
answered.value = false;
if (currentQuestion.value < practiceQuestions.length - 1) {
currentQuestion.value++;
} else {
practiceCompleted.value = true;
speak(`练习结束,你得了${score.value}分!`);
}
};
// --- 7. 奥数逻辑 ---
const currentOlympiad = ref(0);
const selectedOlympiadOption = ref(null);
const olympiadAnswered = ref(false);
const isOlympiadCorrect = ref(false);
const showOlympiadShake = ref(false);
const wrongAttempts = ref(0);
const olympiadScore = ref(0);
const olympiadCompleted = ref(false);
const selectOlympiadOption = (index, correct) => {
if (olympiadAnswered.value) return;
selectedOlympiadOption.value = index;
if (correct) {
olympiadAnswered.value = true;
isOlympiadCorrect.value = true;
olympiadScore.value += 50;
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
speak('太厉害了!奥数题也难不倒你!');
} else {
wrongAttempts.value++;
showOlympiadShake.value = true;
setTimeout(() => showOlympiadShake.value = false, 500);
if (wrongAttempts.value >= 3) {
olympiadAnswered.value = true;
isOlympiadCorrect.value = false;
speak('没关系,我们来看看详细解析。');
} else {
speak('再试一次,我有提示给你。');
}
}
};
const nextOlympiad = () => {
selectedOlympiadOption.value = null;
olympiadAnswered.value = false;
wrongAttempts.value = 0;
if (currentOlympiad.value < olympiadQuestions.length - 1) {
currentOlympiad.value++;
} else {
olympiadCompleted.value = true;
speak(`挑战完成!奥数得分${olympiadScore.value}分!`);
}
};
// 初始化动画
nextTick(() => { runDirectAnimation(); });
return {
speak, stopSpeak,
currentPage, switchPage,
demoTab, switchDemoTab, directStep, nextDirectStep, prevDirectStep,
explainTab, expandedCard, toggleCard,
practiceQuestions, currentQuestion, selectedOption, answered, isCorrect, showShake, score, practiceCompleted, selectOption, nextQuestion,
olympiadQuestions, currentOlympiad, selectedOlympiadOption, olympiadAnswered, isOlympiadCorrect, showOlympiadShake, wrongAttempts, olympiadScore, olympiadCompleted, selectOlympiadOption, nextOlympiad
};
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码