<!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>
<link rel="stylesheet" href="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/katex/katex.min.css">
<script defer src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/katex/katex.min.js"></script>
<script defer src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/katex/auto-render.min.js"></script>
<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>
<style>
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
html,
body {
margin: 0;
padding: 0;
background: #F0F4F8;
overflow: hidden;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
-webkit-text-size-adjust: 100%;
}
#app {
height: 100vh;
width: 100%;
max-width: 480px;
margin: 0 auto;
background: #fff;
display: flex;
flex-direction: column;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05);
}
.content-area {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 80px;
-webkit-overflow-scrolling: touch;
}
/* 顶部标签栏 */
.top-tabs {
display: flex;
background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
padding: 10px 10px 0 10px;
gap: 5px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.top-tab {
flex-shrink: 0;
padding: 10px 12px;
border-radius: 12px 12px 0 0;
font-size: 12px;
font-weight: bold;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.3s;
background: rgba(255, 255, 255, 0.1);
min-width: 80px;
text-align: center;
}
.top-tab.active {
background: white;
color: #D97706;
transform: translateY(-2px);
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
}
/* ========================================
悬浮导航按钮样式
======================================== */
/* 例题动画舞台特殊样式 */
.example-anim-stage {
padding: 25px 40px !important;
overflow: visible !important;
}
/* 悬浮导航按钮(左右) */
.float-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
color: white;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(245, 158, 11, 0.4);
transition: all 0.3s ease;
z-index: 100;
}
.float-nav-btn:hover:not(:disabled) {
transform: translateY(-50%) scale(1.15);
box-shadow: 0 6px 20px rgba(245, 158, 11, 0.6);
background: linear-gradient(135deg, #FBBF24 0%, #F59E0B 100%);
}
.float-nav-btn:active:not(:disabled) {
transform: translateY(-50%) scale(0.95);
}
.float-nav-btn:disabled {
cursor: not-allowed;
background: linear-gradient(135deg, #CBD5E1 0%, #94A3B8 100%);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.float-nav-left {
left: -25px;
}
.float-nav-right {
right: -25px;
}
/* 底部悬浮按钮组 */
.float-bottom-btns {
position: absolute;
bottom: 75px;
right: 15px;
display: flex;
gap: 10px;
z-index: 100;
}
.float-action-btn {
width: 45px;
height: 45px;
border-radius: 50%;
border: none;
background: rgba(100, 116, 139, 0.9);
backdrop-filter: blur(10px);
color: white;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.float-action-btn:hover {
background: rgba(71, 85, 105, 1);
transform: translateY(-3px) scale(1.1);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
}
.float-action-btn:active {
transform: translateY(-1px) scale(0.95);
}
/* 移动端适配 */
@media (max-width: 768px) {
.example-anim-stage {
padding: 25px 15px !important;
}
.float-nav-left {
left: 5px;
}
.float-nav-right {
right: 5px;
}
.float-nav-btn {
width: 45px;
height: 45px;
font-size: 18px;
}
.float-bottom-btns {
bottom: 70px;
right: 10px;
gap: 8px;
}
.float-action-btn {
width: 40px;
height: 40px;
font-size: 18px;
}
}
/* 超小屏幕 */
@media (max-width: 480px) {
.float-nav-btn {
width: 40px;
height: 40px;
font-size: 16px;
}
.float-nav-left {
left: 2px;
}
.float-nav-right {
right: 2px;
}
.float-action-btn {
width: 36px;
height: 36px;
font-size: 16px;
}
.float-bottom-btns {
bottom: 65px;
right: 8px;
}
}
/* 按钮脉冲动画(提示用户可以点击) */
@keyframes pulse-hint {
0%,
100% {
box-shadow: 0 4px 15px rgba(245, 158, 11, 0.4);
}
50% {
box-shadow: 0 4px 25px rgba(245, 158, 11, 0.7);
}
}
.float-nav-btn:not(:disabled) {
animation: pulse-hint 2s infinite;
}
.float-nav-btn:hover:not(:disabled) {
animation: none;
}
/* 底部导航 */
.bottom-nav {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 480px;
height: 70px;
background: white;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-around;
align-items: center;
z-index: 1000;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 8px;
transition: all 0.3s;
color: #64748B;
}
.nav-item.active {
color: #F59E0B;
}
.nav-icon {
font-size: 24px;
margin-bottom: 4px;
}
.nav-label {
font-size: 12px;
font-weight: 500;
}
/* 通用样式 */
.page-container {
padding: 20px;
}
.section-title {
font-size: 20px;
font-weight: 800;
color: #1E293B;
margin: 20px 0 15px 0;
display: flex;
align-items: center;
gap: 8px;
}
.card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
border: 1px solid #E2E8F0;
}
.tag {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
margin-bottom: 8px;
}
.tag-blue {
background: #E0F2FE;
color: #0284C7;
}
.tag-green {
background: #D1FAE5;
color: #065F46;
}
.tag-orange {
background: #FFEDD5;
color: #C2410C;
}
.tag-purple {
background: #F3E8FF;
color: #7C3AED;
}
.tag-amber {
background: #FEF3C7;
color: #D97706;
}
.formula-box {
background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
padding: 20px;
border-radius: 12px;
border: 3px solid #F59E0B;
margin: 15px 0;
text-align: center;
}
.formula-title {
font-size: 14px;
font-weight: bold;
color: #D97706;
margin-bottom: 10px;
}
.formula-content {
font-size: 18px;
font-weight: bold;
color: #1E293B;
}
.speak-btn {
background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
color: white;
border: none;
padding: 12px 20px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
box-shadow: 0 4px 10px rgba(245, 158, 11, 0.3);
width: 100%;
}
/* 动画舞台 */
.anim-stage {
background: #1E293B;
border-radius: 16px;
padding: 25px 15px;
min-height: 400px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid #334155;
margin: 15px 0;
}
.anim-text {
color: white;
margin-top: auto;
font-size: 14px;
font-weight: bold;
text-align: center;
min-height: 60px;
background: rgba(0, 0, 0, 0.4);
padding: 12px 15px;
border-radius: 20px;
backdrop-filter: blur(4px);
width: 100%;
display: flex;
align-items: center;
justify-content: center;
line-height: 1.5;
}
.step-controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
justify-content: center;
flex-wrap: wrap;
}
.step-btn {
background: #F59E0B;
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
font-weight: bold;
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.step-btn:disabled {
background: #CBD5E1;
cursor: not-allowed;
}
.step-btn:active:not(:disabled) {
transform: scale(0.95);
}
/* 计算演示 */
.calc-demo {
color: white;
width: 100%;
margin-top: 20px;
}
.calc-step {
background: rgba(255, 255, 255, 0.1);
padding: 15px;
border-radius: 10px;
margin-bottom: 15px;
opacity: 0;
border: 2px solid transparent;
}
.calc-step.active {
border-color: #F59E0B;
background: rgba(245, 158, 11, 0.2);
}
.calc-equation {
font-size: 16px;
text-align: center;
margin-bottom: 8px;
}
.calc-highlight {
color: #FCD34D;
font-size: 20px;
font-weight: bold;
}
.calc-explanation {
font-size: 12px;
color: #CBD5E1;
text-align: center;
margin-top: 5px;
}
/* 视觉演示 */
.visual-demo {
width: 100%;
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.divisor-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
max-width: 100%;
}
.divisor-box {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
color: white;
background: #3B82F6;
opacity: 0;
}
.factor-tree {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
}
/* 左右分栏布局 */
.theory-layout {
display: flex;
gap: 15px;
}
.anim-section {
flex: 0 0 45%;
min-width: 200px;
}
.content-section {
flex: 1;
}
/* 移动端响应式 */
@media (max-width: 768px) {
.theory-layout {
flex-direction: column;
}
.anim-section {
flex: 1;
margin-bottom: 15px;
}
}
/* 按钮激活状态 */
.step-btn.active {
background: #7C3AED !important;
}
.factor-row {
display: flex;
gap: 10px;
align-items: center;
}
.factor-box {
padding: 8px 15px;
background: #F59E0B;
color: white;
border-radius: 8px;
font-weight: bold;
opacity: 0;
}
/* 练习题样式 */
.question-card {
background: white;
border-radius: 16px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
margin-bottom: 15px;
}
.question-text {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
line-height: 1.6;
color: #1E293B;
}
.answer-input {
width: 100%;
padding: 12px;
border: 2px solid #E2E8F0;
border-radius: 8px;
font-size: 16px;
margin-bottom: 10px;
}
.submit-btn {
background: #F59E0B;
color: white;
border: none;
padding: 12px;
border-radius: 8px;
width: 100%;
font-weight: bold;
font-size: 16px;
cursor: pointer;
}
.feedback-box {
margin-top: 10px;
padding: 12px;
border-radius: 8px;
font-size: 13px;
line-height: 1.6;
}
.feedback-correct {
background: #ECFDF5;
color: #047857;
border: 2px solid #10B981;
}
.feedback-wrong {
background: #FEF2F2;
color: #B91C1C;
border: 2px solid #EF4444;
}
.example-item {
border: 2px solid #E2E8F0;
border-radius: 10px;
margin-bottom: 12px;
overflow: hidden;
}
.example-header {
padding: 15px;
background: #F8FAFC;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
font-size: 14px;
}
.example-content {
padding: 15px;
border-top: 1px solid #E2E8F0;
background: white;
line-height: 1.8;
font-size: 13px;
color: #334155;
display: none;
}
.example-item.active .example-content {
display: block;
}
.property-card {
background: linear-gradient(135deg, #DBEAFE 0%, #BFDBFE 100%);
border: 2px solid #3B82F6;
border-radius: 12px;
padding: 15px;
margin: 15px 0;
}
.property-title {
font-size: 15px;
font-weight: bold;
color: #1E40AF;
margin-bottom: 10px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.08);
}
}
@keyframes slideIn {
from {
transform: translateX(-20px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "\\[", right: "\\]", display: true },
{ left: "$", right: "$", display: false },
{ left: "\\(", right: "\\)", display: false }
],
throwOnError: false
});
});
</script>
</head>
<body>
<div id="app">
<div class="content-area">
<!-- Page 1: 核心原理 -->
<div v-show="currentPage === 1">
<div class="top-tabs">
<div class="top-tab" :class="{active: theoryTab === 'count'}" @click="theoryTab = 'count'">
约数个数
</div>
<div class="top-tab" :class="{active: theoryTab === 'properties'}"
@click="theoryTab = 'properties'">
约数性质
</div>
<div class="top-tab" :class="{active: theoryTab === 'sum'}" @click="theoryTab = 'sum'">
约数和
</div>
<div class="top-tab" :class="{active: theoryTab === 'special'}" @click="theoryTab = 'special'">
特殊数
</div>
</div>
<div class="page-container">
<!-- 约数个数 -->
<!-- 约数个数 -->
<div v-show="theoryTab === 'count'">
<div class="section-title">🔢 约数个数的求法</div>
<!-- 左右分栏布局 -->
<div class="theory-layout">
<!-- 左侧:动画演示区 -->
<div class="anim-section">
<div class="card">
<div class="tag tag-purple">🎬 理解0次幂</div>
<p style="font-size: 13px; color: #64748B; margin: 10px 0;">
在学习约数个数公式前,先理解"为什么指数可以是0"
</p>
<!-- 4个按钮 -->
<div class="step-controls"
style="flex-direction: column; gap: 8px; margin-bottom: 15px;">
<button class="step-btn" @click="currentAnimation = 'origin'; animateOrigin()"
:style="{background: currentAnimation === 'origin' ? '#7C3AED' : '#F59E0B'}"
style="width: 100%; justify-content: flex-start; padding-left: 15px;">
🌟 什么是"起点"?
</button>
<button class="step-btn" @click="currentAnimation = 'whyA'; animateWhyZeroA()"
:style="{background: currentAnimation === 'whyA' ? '#7C3AED' : '#F59E0B'}"
style="width: 100%; justify-content: flex-start; padding-left: 15px;">
💡 为什么2⁰=1?
</button>
<button class="step-btn" @click="currentAnimation = 'whyB'; animateWhyZeroB()"
:style="{background: currentAnimation === 'whyB' ? '#7C3AED' : '#F59E0B'}"
style="width: 100%; justify-content: flex-start; padding-left: 15px;">
📐 数学证明
</button>
<button class="step-btn" @click="currentAnimation = 'complete'; animateCount()"
:style="{background: currentAnimation === 'complete' ? '#7C3AED' : '#F59E0B'}"
style="width: 100%; justify-content: flex-start; padding-left: 15px;">
▶️ 完整演示:72的约数
</button>
</div>
<!-- 演示舞台 -->
<div class="anim-stage" style="min-height: 400px;">
<div class="visual-demo" id="count-all-demo" style="width: 100%; height: 100%;">
<!-- 动画内容将在这里生成 -->
</div>
<div class="anim-text" id="count-all-text">点击上方按钮开始学习</div>
</div>
</div>
</div>
<!-- 右侧:原有理论内容 -->
<div class="content-section">
<div class="formula-box">
<div class="formula-title">★ 核心公式 ★</div>
<div class="formula-content">
若 $N = p_1^{a_1} \cdot p_2^{a_2} \cdots p_k^{a_k}$<br>
则 $\tau(N) = (a_1+1)(a_2+1)\cdots(a_k+1)$
</div>
</div>
<div class="card">
<div class="tag tag-blue">公式原理</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
每个约数都可以表示为 $p_1^{i_1} \cdot p_2^{i_2} \cdots p_k^{i_k}$ 的形式,其中:
</p>
<div
style="background: #EFF6FF; padding: 15px; border-radius: 8px; margin: 10px 0;">
• $i_1$ 可以从 $0$ 到 $a_1$ 取值,共 <strong>$(a_1+1)$ 种</strong>选择<br>
• $i_2$ 可以从 $0$ 到 $a_2$ 取值,共 <strong>$(a_2+1)$ 种</strong>选择<br>
• ··· 依此类推<br><br>
根据<strong>乘法原理</strong>,总约数个数 = 各选择数相乘
</div>
</div>
<button class="speak-btn" @click="speakCount">
🔊 听讲解
</button>
</div>
</div>
</div>
<!-- 约数性质 -->
<div v-show="theoryTab === 'properties'">
<div class="section-title">⚡ 约数的性质</div>
<div class="property-card">
<div class="property-title">性质1:约数个数为奇数</div>
<div style="font-size: 14px; color: #1E40AF; line-height: 1.8;">
一个数的约数个数是<strong>奇数</strong>,当且仅当这个数是<strong>完全平方数</strong>。
</div>
<div
style="background: white; padding: 12px; border-radius: 8px; margin-top: 10px; font-size: 13px;">
<strong>原理:</strong>约数通常成对出现(如 $4 \times 9 = 36$)。<br>
只有完全平方数有一个约数等于 $\sqrt{N}$,无法配对。
</div>
</div>
<div class="property-card">
<div class="property-title">性质2:奇数约数与偶数约数</div>
<div style="font-size: 14px; color: #1E40AF; line-height: 1.8; margin-bottom: 10px;">
若 $N = 2^a \times M$($M$ 为奇数部分):
</div>
<div
style="background: white; padding: 12px; border-radius: 8px; margin-top: 10px; font-size: 13px;">
<strong>奇数约数个数:</strong>$\tau_{\text{奇}}(N) = \tau(M)$<br>
(与 $2^a$ 无关,只看奇数部分)<br><br>
<strong>偶数约数个数:</strong>$\tau_{\text{偶}}(N) = a \times \tau(M)$<br>
(必须至少含一个因子2)
</div>
</div>
<div class="card">
<div class="tag tag-amber">动画演示:180的奇偶约数</div>
<p style="font-size: 13px; color: #64748B; margin: 10px 0;">
$180 = 2^2 \times 3^2 \times 5$
</p>
<div class="step-controls">
<button class="step-btn" @click="animateProperties">▶️ 演示分类过程</button>
</div>
<div class="anim-stage">
<div class="visual-demo" id="properties-demo">
<!-- 动画将在这里生成 -->
</div>
<div class="anim-text" id="properties-text">点击按钮开始演示</div>
</div>
</div>
<button class="speak-btn" @click="speakProperties">
🔊 听讲解
</button>
</div>
<!-- 约数和 -->
<div v-show="theoryTab === 'sum'">
<div class="section-title">➕ 所有约数的和</div>
<div class="formula-box">
<div class="formula-title">★ 核心公式 ★</div>
<div class="formula-content" style="font-size: 14px;">
$\sigma(N) = (1+p_1+\cdots+p_1^{a_1}) \times$<br>
$(1+p_2+\cdots+p_2^{a_2}) \times \cdots$
</div>
</div>
<div class="card">
<div class="tag tag-blue">等比数列求和形式</div>
<div
style="background: #F0FDF4; padding: 15px; border-radius: 8px; margin: 10px 0; text-align: center;">
$\sigma(N) = \frac{p_1^{a_1+1}-1}{p_1-1} \times \frac{p_2^{a_2+1}-1}{p_2-1} \times
\cdots$
</div>
</div>
<div class="card">
<div class="tag tag-purple">公式原理</div>
<p style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
展开多项式乘积:
</p>
<div
style="background: #FEF3C7; padding: 15px; border-radius: 8px; margin: 10px 0; font-size: 13px;">
$(1+p_1+p_1^2) \times (1+p_2+p_2^2)$<br>
$= 1 + p_1 + p_1^2 + p_2 + p_1p_2 + p_1^2p_2 + \cdots$<br><br>
每一项都是 $N$ 的一个约数!
</div>
</div>
<div class="card">
<div class="tag tag-amber">动画演示:72的约数和</div>
<p style="font-size: 13px; color: #64748B; margin: 10px 0;">
$72 = 2^3 \times 3^2$
</p>
<div class="step-controls">
<button class="step-btn" @click="animateSum">▶️ 演示计算过程</button>
</div>
<div class="anim-stage">
<div class="calc-demo" id="sum-demo">
<!-- 动画将在这里生成 -->
</div>
<div class="anim-text" id="sum-text">点击按钮开始演示</div>
</div>
</div>
<button class="speak-btn" @click="speakSum">
🔊 听讲解
</button>
</div>
<!-- 特殊数 -->
<div v-show="theoryTab === 'special'">
<div class="section-title">⭐ 特殊数与概念</div>
<div class="card">
<div class="tag tag-green">完全数 (Perfect Number)</div>
<div style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>定义:</strong>一个数的所有<strong>真约数</strong>(除了它本身的约数)之和等于它自身。
</div>
<div style="background: #ECFDF5; padding: 15px; border-radius: 8px; margin: 10px 0;">
<strong>公式:</strong>$\sigma(N) - N = N$ 或 $\sigma(N) = 2N$
</div>
<div
style="background: #FEF3C7; padding: 12px; border-radius: 8px; margin-top: 10px; font-size: 13px;">
<strong>例子1:</strong>$6$ 的约数:$1, 2, 3, 6$<br>
真约数和:$1+2+3 = 6$ ✓<br><br>
<strong>例子2:</strong>$28$ 的约数:$1, 2, 4, 7, 14, 28$<br>
真约数和:$1+2+4+7+14 = 28$ ✓
</div>
</div>
<div class="card">
<div class="tag tag-purple">亲和数 (Amicable Numbers)</div>
<div style="font-size: 14px; color: #334155; line-height: 1.8; margin-top: 10px;">
<strong>概念:</strong>两个数 $A$ 和 $B$,满足 $A$ 的真约数之和等于 $B$,且 $B$ 的真约数之和等于 $A$。
</div>
<div
style="background: #F3E8FF; padding: 15px; border-radius: 8px; margin: 10px 0; font-size: 13px;">
<strong>经典例子:220 和 284</strong><br>
• $220$ 的真约数和 = $284$<br>
• $284$ 的真约数和 = $220$<br>
它们互为亲和数!
</div>
</div>
<div class="card">
<div class="tag tag-amber">动画演示:验证6是完全数</div>
<div class="step-controls">
<button class="step-btn" @click="animateSpecial">▶️ 演示验证过程</button>
</div>
<div class="anim-stage">
<div class="visual-demo" id="special-demo">
<!-- 动画将在这里生成 -->
</div>
<div class="anim-text" id="special-text">点击按钮开始演示</div>
</div>
</div>
<button class="speak-btn" @click="speakSpecial">
🔊 听讲解
</button>
</div>
</div>
</div>
<!-- Page 2: 5道经典例题 -->
<div v-show="currentPage === 2">
<div class="top-tabs">
<div class="top-tab" :class="{active: exampleIndex === index}"
v-for="(ex, index) in classicExamples" :key="'tab-' + index" @click="switchExample(index)">
例题{{ index + 1 }}
</div>
</div>
<div class="page-container">
<div v-for="(ex, index) in classicExamples" :key="'example-' + index"
v-show="exampleIndex === index">
<div class="section-title">{{ ex.title }}</div>
<div class="card">
<div class="tag tag-blue">题目</div>
<div class="question-text" v-html="ex.question"></div>
</div>
<div class="card">
<div class="tag tag-amber">分步解析(动画演示)</div>
<!-- 新的悬浮按钮设计 -->
<div class="anim-stage example-anim-stage" style="position: relative;">
<!-- 左侧悬浮:上一步 -->
<button class="float-nav-btn float-nav-left" @click="prevExampleStep(index)"
:disabled="exampleSteps[index] === 0"
:style="{opacity: exampleSteps[index] === 0 ? 0.3 : 1}">
<span style="font-size: 24px;">◀</span>
</button>
<!-- 右侧悬浮:下一步 -->
<button class="float-nav-btn float-nav-right" @click="nextExampleStep(index)"
:disabled="exampleSteps[index] >= ex.steps.length - 1"
:style="{opacity: exampleSteps[index] >= ex.steps.length - 1 ? 0.3 : 1}">
<span style="font-size: 24px;">▶</span>
</button>
<!-- 底部悬浮按钮组 -->
<div class="float-bottom-btns">
<button class="float-action-btn" @click="resetExampleStep(index)" title="重置">
<span style="font-size: 20px;">🔄</span>
</button>
<button class="float-action-btn" @click="speakExample(index)" title="语音讲解">
<span style="font-size: 20px;">🔊</span>
</button>
</div>
<!-- 动画内容区域 -->
<div class="calc-demo">
<div v-for="(step, sIndex) in ex.steps" :key="'step-' + sIndex" class="calc-step"
:class="{active: exampleSteps[index] === sIndex}"
:style="{opacity: exampleSteps[index] >= sIndex ? 1 : 0}">
<div class="calc-equation" v-html="step.equation"></div>
<div class="calc-explanation">{{ step.explanation }}</div>
</div>
</div>
<div class="anim-text">
{{ exampleSteps[index] < ex.steps.length ? ex.steps[exampleSteps[index]].text
: '已完成所有步骤' }} </div>
</div>
</div>
<div class="card">
<div class="tag tag-green">答案</div>
<div style="background: #ECFDF5; padding: 15px; border-radius: 8px; border-left: 3px solid #10B981;"
v-html="ex.answer"></div>
</div>
</div>
</div>
</div>
<!-- Page 3: 10道练习题 -->
<div v-show="currentPage === 3" class="page-container">
<div class="section-title">✏️ 10道课程练习</div>
<div v-if="!practiceDone">
<div class="question-card">
<div class="tag tag-blue">练习 {{ currentPracticeIndex + 1 }}/10</div>
<div class="question-text" v-html="practiceQuestions[currentPracticeIndex].question"></div>
<input type="text" class="answer-input" v-model="userAnswer" placeholder="输入答案"
@keyup.enter="checkPracticeAnswer" :disabled="practiceAnswered">
<button class="submit-btn" @click="checkPracticeAnswer" v-if="!practiceAnswered">
提交答案
</button>
<div v-if="practiceAnswered" class="feedback-box"
:class="isPracticeCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isPracticeCorrect" style="font-weight: bold; margin-bottom: 5px;">🎉 正确!
</div>
<div v-else style="font-weight: bold; margin-bottom: 5px;">💡 答案:{{
practiceQuestions[currentPracticeIndex].answer }}</div>
<div v-html="practiceQuestions[currentPracticeIndex].explanation"></div>
</div>
<button v-if="practiceAnswered" class="submit-btn" @click="nextPractice"
style="margin-top: 10px;">
{{ currentPracticeIndex < 9 ? '下一题 →' : '完成练习 🎯' }} </button>
</div>
</div>
<div v-else class="card" style="text-align: center; padding: 40px 20px;">
<div style="font-size: 60px; margin-bottom: 20px;">🏆</div>
<h2 style="color: #1E293B; margin-bottom: 10px;">练习完成!</h2>
<p style="font-size: 18px; color: #64748B; margin-bottom: 30px;">得分:{{ practiceScore }}/100</p>
<button class="submit-btn" @click="switchPage(4)">挑战杯赛真题 →</button>
</div>
</div>
<!-- Page 4: 10道杯赛真题 -->
<div v-show="currentPage === 4" class="page-container">
<div class="section-title">🏆 10道杯赛真题</div>
<div class="card" style="background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);">
<div style="font-size: 14px; color: #92400E; line-height: 1.6;">
<strong>💡 提示:</strong>这些题目来自华杯赛、希望杯、迎春杯等真题。<br>
需要灵活运用约数个数、约数和的各种性质!
</div>
</div>
<div v-if="!olympiadDone">
<div class="question-card">
<div class="tag tag-amber">🏆 杯赛真题 {{ currentOlympiadIndex + 1 }}/10</div>
<div class="question-text" v-html="olympiadQuestions[currentOlympiadIndex].question"></div>
<div v-if="olympiadQuestions[currentOlympiadIndex].hint"
style="background: #FEF3C7; padding: 12px; border-radius: 6px; margin-bottom: 10px; font-size: 13px;">
<strong>💡 提示:</strong>{{ olympiadQuestions[currentOlympiadIndex].hint }}
</div>
<input type="text" class="answer-input" v-model="userOlympiadAnswer" placeholder="输入答案"
@keyup.enter="checkOlympiadAnswer" :disabled="olympiadAnswered">
<button class="submit-btn" @click="checkOlympiadAnswer" v-if="!olympiadAnswered">
提交答案
</button>
<div v-if="olympiadAnswered" class="feedback-box"
:class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isOlympiadCorrect" style="font-weight: bold; margin-bottom: 5px;">🎉 正确!太棒了!
</div>
<div v-else style="font-weight: bold; margin-bottom: 5px;">💡 正确答案:{{
olympiadQuestions[currentOlympiadIndex].answer }}</div>
<div style="margin-top: 10px;"><strong>详细解析:</strong></div>
<div v-html="olympiadQuestions[currentOlympiadIndex].explanation"></div>
</div>
<button v-if="olympiadAnswered" class="submit-btn" @click="nextOlympiad"
style="margin-top: 10px;">
{{ currentOlympiadIndex < 9 ? '下一题 →' : '查看成绩 📚' }} </button>
</div>
</div>
<div v-else class="card" style="text-align: center; padding: 40px 20px;">
<div style="font-size: 60px; margin-bottom: 20px;">🥇</div>
<h2 style="color: #1E293B; margin-bottom: 10px;">杯赛真题通关!</h2>
<p style="font-size: 18px; color: #64748B; margin-bottom: 30px;">得分:{{ olympiadScore }}/100</p>
<p style="font-size: 14px; color: #64748B;">你已经掌握了约数个数与约数和!</p>
</div>
</div>
</div>
<!-- 底部导航 -->
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<div class="nav-icon">📖</div>
<div class="nav-label">原理</div>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<div class="nav-icon">📚</div>
<div class="nav-label">例题</div>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<div class="nav-icon">✏️</div>
<div class="nav-label">练习</div>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<div class="nav-icon">🏆</div>
<div class="nav-label">杯赛</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
currentPage: 1,
theoryTab: 'count',
exampleIndex: 0,
currentAnimation: '', // 新增:当前激活的动画
// 例题步骤
exampleSteps: [0, 0, 0, 0, 0],
// 5道经典例题
classicExamples: [
{
title: '例题1:约数个数基础计算',
question: '求 $1440$ 共有多少个约数。',
steps: [
{ equation: '第1步:分解质因数', explanation: '从小到大分解', text: '步骤1:开始质因数分解' },
{ equation: '$1440 = 144 \\times 10$', explanation: '拆分成熟悉的数', text: '步骤2:拆分数字' },
{ equation: '$= (12^2) \\times 10 = (2^2 \\times 3)^2 \\times (2 \\times 5)$', explanation: '继续分解', text: '步骤3:继续分解' },
{ equation: '$= 2^4 \\times 3^2 \\times 2 \\times 5 =$ <span class="calc-highlight">$2^5 \\times 3^2 \\times 5^1$</span>', explanation: '合并同类项', text: '步骤4:得到标准形式' },
{ equation: '指数:$a_1=5, a_2=2, a_3=1$', explanation: '提取指数', text: '步骤5:提取各质因数指数' },
{ equation: '$\\tau(1440) = (5+1)(2+1)(1+1)$', explanation: '套用公式', text: '步骤6:套用约数个数公式' },
{ equation: '$= 6 \\times 3 \\times 2 =$ <span class="calc-highlight">$36$</span>', explanation: '计算结果', text: '步骤7:计算得出答案' }
],
answer: '<strong style="color: #F59E0B; font-size: 18px;">答案:36个约数</strong>',
voice: '求1440的约数个数。第一步,分解质因数。1440等于144乘以10,等于2的4次方乘以3的平方,再乘以2乘以5,合并后得到2的5次方乘以3的平方乘以5。第二步,套用公式。指数分别是5、2、1,所以约数个数等于括号5加1括号,乘以括号2加1括号,乘以括号1加1括号,等于6乘以3乘以2,等于36。答案是36个约数。'
},
{
title: '例题2:约数和的计算',
question: '求 $72$ 的所有约数之和 $\\sigma(72)$。',
steps: [
{ equation: '第1步:分解质因数', explanation: '标准分解', text: '步骤1:质因数分解' },
{ equation: '$72 = 8 \\times 9 = 2^3 \\times 3^2$', explanation: '得到标准形式', text: '步骤2:得到2³×3²' },
{ equation: '套用约数和公式:', explanation: '展开各质因数的幂', text: '步骤3:准备套用公式' },
{ equation: '$\\sigma(72) = (1+2+2^2+2^3) \\times (1+3+3^2)$', explanation: '列出所有幂次', text: '步骤4:列出所有项' },
{ equation: '$= (1+2+4+8) \\times (1+3+9)$', explanation: '计算各项值', text: '步骤5:计算各项' },
{ equation: '$= 15 \\times 13$', explanation: '求和', text: '步骤6:求和' },
{ equation: '$=$ <span class="calc-highlight">$195$</span>', explanation: '最终结果', text: '步骤7:得出答案' }
],
answer: '<strong style="color: #F59E0B; font-size: 18px;">答案:195</strong>',
voice: '求72的所有约数之和。第一步,分解质因数,72等于2的3次方乘以3的平方。第二步,套用约数和公式。等于括号1加2加2的平方加2的3次方括号,乘以括号1加3加3的平方括号。第三步,计算各项。等于括号1加2加4加8括号,乘以括号1加3加9括号,等于15乘以13,等于195。'
},
{
title: '例题3:奇数约数的应用',
question: '求 $1800$ 的所有约数中,有多少个是奇数?',
steps: [
{ equation: '第1步:分解质因数', explanation: '标准分解', text: '步骤1:分解质因数' },
{ equation: '$1800 = 18 \\times 100$', explanation: '拆分', text: '步骤2:拆分数字' },
{ equation: '$= (2 \\times 3^2) \\times (2^2 \\times 5^2)$', explanation: '分别分解', text: '步骤3:分别分解' },
{ equation: '$=$ <span class="calc-highlight">$2^3 \\times 3^2 \\times 5^2$</span>', explanation: '合并', text: '步骤4:合并同类项' },
{ equation: '识别奇数部分 $M$:', explanation: '去掉2的幂次', text: '步骤5:提取奇数部分' },
{ equation: '$M = 3^2 \\times 5^2$', explanation: '只看奇质因数', text: '步骤6:奇数部分M' },
{ equation: '$\\tau_{\\text{奇}}(1800) = (2+1)(2+1)$', explanation: '只用M的指数', text: '步骤7:套用公式' },
{ equation: '$= 3 \\times 3 =$ <span class="calc-highlight">$9$</span>', explanation: '计算结果', text: '步骤8:得出答案' }
],
answer: '<strong style="color: #F59E0B; font-size: 18px;">答案:9个奇数约数</strong>',
voice: '求1800的所有约数中有多少个是奇数。第一步,分解质因数,1800等于2的3次方乘以3的平方乘以5的平方。第二步,识别奇数部分M。去掉2的幂次,M等于3的平方乘以5的平方。第三步,计算奇数约数个数。只用M的指数,等于括号2加1括号乘以括号2加1括号,等于3乘以3,等于9个。'
},
{
title: '例题4:完全平方数判定',
question: '在 $1$ 到 $100$ 之间,有多少个正整数的约数个数是奇数?',
steps: [
{ equation: '应用性质:', explanation: '核心定理', text: '步骤1:应用核心性质' },
{ equation: '约数个数为<span class="calc-highlight">奇数</span> ⟺ 数是<span class="calc-highlight">完全平方数</span>', explanation: '充要条件', text: '步骤2:等价转化' },
{ equation: '寻找范围内的完全平方数:', explanation: '1到100之间', text: '步骤3:寻找完全平方数' },
{ equation: '$1^2=1, 2^2=4, 3^2=9, \\ldots$', explanation: '从小到大列举', text: '步骤4:从小到大列举' },
{ equation: '$\\ldots, 9^2=81, 10^2=100$', explanation: '直到100', text: '步骤5:一直到100' },
{ equation: '计数:从 $1$ 到 $10$', explanation: '共10个数', text: '步骤6:计数' },
{ equation: '答案:<span class="calc-highlight">10</span> 个', explanation: '最终答案', text: '步骤7:得出答案' }
],
answer: '<strong style="color: #F59E0B; font-size: 18px;">答案:10个</strong><br>它们是:1, 4, 9, 16, 25, 36, 49, 64, 81, 100',
voice: '在1到100之间,有多少个正整数的约数个数是奇数。应用性质,约数个数为奇数,当且仅当这个数是完全平方数。所以问题转化为,1到100之间有多少个完全平方数。从1的平方等于1,到10的平方等于100,一共有10个。答案是10个。'
},
{
title: '例题5:逆向构造问题',
question: '一个数 $N$ 只有 $6$ 个约数,求满足条件的最小数 $N$。',
steps: [
{ equation: '拆分约数个数 $6$:', explanation: '寻找可能形式', text: '步骤1:拆分6' },
{ equation: '情况A:$6 = 5+1$', explanation: '一个质因数', text: '步骤2:情况A分析' },
{ equation: '$N = p^5 \\rightarrow 2^5 = 32$', explanation: '最小取p=2', text: '步骤3:情况A结果' },
{ equation: '情况B:$6 = (2+1)(1+1)$', explanation: '两个质因数', text: '步骤4:情况B分析' },
{ equation: '$N = p_1^2 \\times p_2^1$', explanation: '指数为2和1', text: '步骤5:情况B形式' },
{ equation: '最小化原则:', explanation: '大指数配小质数', text: '步骤6:最小化原则' },
{ equation: '$N = 2^2 \\times 3^1 = 4 \\times 3 = 12$', explanation: '计算结果', text: '步骤7:情况B结果' },
{ equation: '比较:$12 < 32$', explanation: '选较小的', text: '步骤8:比较得出答案' },
{ equation: '答案:<span class="calc-highlight">12</span>', explanation: '最终答案', text: '步骤9:确定答案' }
],
answer: '<strong style="color: #F59E0B; font-size: 18px;">答案:12</strong><br>$12 = 2^2 \\times 3$ 的约数:1, 2, 3, 4, 6, 12(共6个)',
voice: '一个数N只有6个约数,求满足条件的最小数N。第一步,拆分6。情况A,6等于5加1,N等于p的5次方,最小是2的5次方等于32。情况B,6等于括号2加1括号乘以括号1加1括号,N等于p1的平方乘以p2。最小化原则,大指数配小质数,所以N等于2的平方乘以3,等于12。比较,12小于32,答案是12。'
}
],
// 练习题
currentPracticeIndex: 0,
practiceScore: 0,
practiceAnswered: false,
isPracticeCorrect: false,
userAnswer: '',
practiceDone: false,
practiceQuestions: [
{ question: '求 $120$ 的约数个数 $\\tau(120)$', answer: '16', explanation: '$120 = 2^3 \\times 3^1 \\times 5^1$<br>$\\tau(120) = (3+1)(1+1)(1+1) = 4 \\times 2 \\times 2 = <strong>16</strong>$' },
{ question: '求 $\\sigma(99)$', answer: '156', explanation: '$99 = 3^2 \\times 11^1$<br>$\\sigma(99) = (1+3+9) \\times (1+11) = 13 \\times 12 = <strong>156</strong>$' },
{ question: '求 $180$ 的约数中,有多少个是偶数?', answer: '12', explanation: '$180 = 2^2 \\times 3^2 \\times 5^1$<br>$\\tau(180) = 18$,$\\tau_{\\text{奇}} = (2+1)(1+1)=6$<br>$\\tau_{\\text{偶}} = 18 - 6 = <strong>12</strong>$' },
{ question: '已知一个数 $N$ 的约数个数是 $3$,且 $N < 100$,求 $N$ 的个数', answer: '4', explanation: '约数个数 $3 = 2+1$,所以 $N = p^2$<br>$2^2=4, 3^2=9, 5^2=25, 7^2=49$<br>共<strong>4</strong>个' },
{ question: '求 $2^5 \\times 5^2 \\times 7^1$ 的约数个数', answer: '36', explanation: '$\\tau(N) = (5+1)(2+1)(1+1) = 6 \\times 3 \\times 2 = <strong>36</strong>$' },
{ question: '求 $\\sigma(200)$', answer: '465', explanation: '$200 = 2^3 \\times 5^2$<br>$\\sigma(200) = (1+2+4+8) \\times (1+5+25) = 15 \\times 31 = <strong>465</strong>$' },
{ question: '一个数 $N$ 只有 $4$ 个约数,求满足条件的最小数 $N$', answer: '6', explanation: '$4=3+1$ ($2^3=8$) 或 $4=(1+1)(1+1)$ ($2^1 \\times 3^1 = 6$)<br>最小为 <strong>6</strong>' },
{ question: '求 $60$ 的约数中,能被 $6$ 整除的约数个数', answer: '4', explanation: '$60 = 2^2 \\times 3^1 \\times 5^1$<br>约数必须包含 $6=2^1 \\times 3^1$<br>$a \\ge 1, b \\ge 1$:$(2,1)(1,1)(2,1) = <strong>4</strong>$' },
{ question: '求 $96$ 的所有约数中,有多少个是 $4$ 的倍数?', answer: '8', explanation: '$96 = 2^5 \\times 3^1$,要能被 $4=2^2$ 整除<br>$a \\ge 2$:可取 $2,3,4,5$ (4种),$b$ 可取 $0,1$ (2种)<br>$4 \\times 2 = <strong>8</strong>$' },
{ question: '证明 $28$ 是完全数(输入 $\\sigma(28)$ 的值)', answer: '56', explanation: '$\\sigma(28) = \\sigma(2^2 \\times 7^1) = (1+2+4) \\times (1+7) = 7 \\times 8 = <strong>56</strong>$<br>$2 \\times 28 = 56$,故 $28$ 是完全数' }
],
// 杯赛真题
currentOlympiadIndex: 0,
olympiadScore: 0,
olympiadAnswered: false,
isOlympiadCorrect: false,
userOlympiadAnswer: '',
olympiadDone: false,
olympiadQuestions: [
{
question: '(华杯赛)一个自然数 $N$ 有 $10$ 个约数,求满足条件的最小数 $N$。',
answer: '48',
hint: '拆分10,比较各种情况',
explanation: '拆分:$10 = 9+1$ 或 $10 = (4+1)(1+1)$<br>情况A:$p^9 \\rightarrow 2^9 = 512$<br>情况B:$p_1^4 \\times p_2^1 \\rightarrow 2^4 \\times 3^1 = <strong>48</strong>$<br>比较:$48 < 512$'
},
{
question: '(希望杯)设 $N = 2^{10} \\times 3^{10}$,求 $N$ 的所有约数中,是 $6$ 的倍数的约数个数。',
answer: '100',
hint: '约数必须包含 6 = 2×3',
explanation: '要求包含 $6 = 2^1 \\times 3^1$<br>$2$ 的指数:$1 \\le a \\le 10$ (10种)<br>$3$ 的指数:$1 \\le b \\le 10$ (10种)<br>$10 \\times 10 = <strong>100</strong>$'
},
{
question: '(迎春杯)求 $300$ 的所有约数的和 $\\sigma(300)$。',
answer: '868',
hint: '分解后套用约数和公式',
explanation: '$300 = 2^2 \\times 3^1 \\times 5^2$<br>$\\sigma(300) = (1+2+4) \\times (1+3) \\times (1+5+25)$<br>$= 7 \\times 4 \\times 31 = <strong>868</strong>$'
},
{
question: '(走美杯)一个数 $N$ 的约数个数是 $8$,且 $N < 30$,求 $N$ 的最小值。',
answer: '24',
hint: '拆分8,找小于30的',
explanation: '拆分:$8=(3+1)(1+1)$ 或 $8=(1+1)(1+1)(1+1)$<br>情况B:$p_1^3 \\times p_2^1 \\rightarrow 2^3 \\times 3^1 = <strong>24</strong>$<br>情况C:$2 \\times 3 \\times 5 = 30$ (不满足$<30$)'
},
{
question: '(中环杯)已知 $N$ 包含质因数 $2$ 和 $3$,约数个数是 $12$。求 $N$ 的最小值。',
answer: '60',
hint: '必须同时包含2和3',
explanation: '拆分:$12=2 \\times 2 \\times 3$<br>$p_1^2 \\times p_2^1 \\times p_3^1$<br>$2^2 \\times 3^1 \\times 5^1 = <strong>60</strong>$'
},
{
question: '已知一个数 $M$ 的约数个数是 $30$,且 $M$ 只有两个质因数 $p$ 和 $q$。求 $M$ 的最小值。',
answer: '2592',
hint: '拆分30为两个因子的乘积',
explanation: '拆分:$30 = (4+1)(5+1)$<br>$M = p^5 \\times q^4$ 或 $p^4 \\times q^5$<br>$2^5 \\times 3^4 = 32 \\times 81 = <strong>2592</strong>$<br>$2^4 \\times 3^5 = 16 \\times 243 = 3888$'
},
{
question: '已知 $N$ 是一个完全数,且所有真约数之和 $s(N) = N$,求 $\\sigma(N)$(用N表示)。',
answer: '2N',
hint: 'σ(N) = N + s(N)',
explanation: '定义:$\\sigma(N) = N + s(N)$<br>完全数性质:$s(N) = N$<br>代入:$\\sigma(N) = N + N = <strong>2N</strong>$'
},
{
question: '求 $144$ 的所有约数中,奇数约数有多少个?',
answer: '3',
hint: '144 = 2⁴ × 3²',
explanation: '$144 = 2^4 \\times 3^2$<br>奇数部分:$M = 3^2$<br>$\\tau_{\\text{奇}} = 2+1 = <strong>3</strong>$ 个<br>(即 1, 3, 9)'
},
{
question: '设 $N=2^{10}$,求 $N$ 的所有约数的个数。',
answer: '11',
hint: 'N只有一个质因数',
explanation: '$N = 2^{10}$<br>$\\tau(N) = 10+1 = <strong>11</strong>$ 个<br>约数为:$1, 2, 2^2, \\ldots, 2^{10}$'
},
{
question: '已知两位数 $N$ 的真约数之和是 $15$,求 $N$。',
answer: '16',
hint: 's(N) = σ(N) - N = 15',
explanation: '试探:$N=16 = 2^4$<br>约数:$1, 2, 4, 8, 16$<br>真约数和:$1+2+4+8 = 15$ ✓<br>答案:<strong>16</strong>'
}
]
}
},
methods: {
// 语音播报
speak(text) {
if (window.speechSynthesis) window.speechSynthesis.cancel();
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
let audio = document.getElementById('tts-audio');
if (!audio) {
audio = document.createElement('audio'); audio.id = 'tts-audio'; audio.style.display = 'none';
document.body.appendChild(audio);
}
const url = `https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
audio.play().catch(e => { if (window.WeixinJSBridge) window.WeixinJSBridge.invoke('getNetworkType', {}, () => audio.play()); });
} else {
if (window.speechSynthesis) {
const u = new SpeechSynthesisUtterance(text); u.lang = 'zh-CN'; u.rate = 0.9;
window.speechSynthesis.speak(u);
}
}
},
speakCount() {
this.speak('约数个数公式是最重要的公式。如果一个数N分解质因数为p1的a1次方乘以p2的a2次方,一直到pk的ak次方,那么N的约数个数等于括号a1加1括号,乘以括号a2加1括号,一直到括号ak加1括号。原理是,每个约数都是从各个质因数的不同幂次中选择组合而成,根据乘法原理,总数等于各选择数相乘。');
},
speakProperties() {
this.speak('约数有两个重要性质。第一,约数个数为奇数的数,一定是完全平方数。因为约数通常成对出现,只有完全平方数有一个约数等于根号N,无法配对。第二,要求奇数约数个数,只需要看奇数部分M的指数,与2的幂次无关。偶数约数个数等于a乘以M的约数个数,其中a是2的指数。');
},
speakSum() {
this.speak('约数和公式。如果N分解质因数为p1的a1次方乘以p2的a2次方,那么所有约数之和等于括号1加p1加p1的平方,一直到p1的a1次方括号,乘以括号1加p2加p2的平方,一直到p2的a2次方括号。展开这个多项式,每一项都是N的一个约数。也可以用等比数列求和公式简化计算。');
},
speakSpecial() {
this.speak('完全数是一个神奇的数,它的所有真约数之和等于它本身。最小的完全数是6,约数有1、2、3、6,真约数1加2加3等于6。第二个完全数是28,真约数和也等于28。完全数非常稀少,前四个是6、28、496、8128。亲和数是两个数,它们的真约数之和互相等于对方,最经典的一对是220和284。');
},
speakExample(index) {
const voice = this.classicExamples[index].voice;
if (voice) {
this.speak(voice);
}
},
// 页面切换
switchPage(page) {
if (window.speechSynthesis) window.speechSynthesis.cancel();
this.currentPage = page;
window.scrollTo(0, 0);
this.$nextTick(() => this.renderMath());
},
// 渲染数学公式
renderMath() {
if (typeof renderMathInElement !== 'undefined') {
setTimeout(() => {
renderMathInElement(document.body, {
delimiters: [
{ left: '$$', right: '$$', display: true },
{ left: '$', right: '$', display: false }
],
throwOnError: false
});
}, 100);
}
},
// 动画1:什么是起点?
animateOrigin() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('count-all-demo');
container.innerHTML = '';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.justifyContent = 'flex-start';
container.style.padding = '20px 10px';
// 标题
const title = document.createElement('div');
title.style.cssText = 'color: #FCD34D; font-size: 16px; font-weight: bold; margin-bottom: 20px; text-align: center;';
title.textContent = '什么是"起点"?';
container.appendChild(title);
gsap.fromTo(title, { opacity: 0, y: -20 }, { opacity: 1, y: 0, duration: 0.5 });
// 创建左右分屏
const splitContainer = document.createElement('div');
splitContainer.style.cssText = 'display: flex; gap: 15px; width: 100%; margin-top: 10px;';
container.appendChild(splitContainer);
// 左侧:加法世界
setTimeout(() => {
const leftBox = document.createElement('div');
leftBox.style.cssText = 'flex: 1; background: rgba(59, 130, 246, 0.15); padding: 15px; border-radius: 10px; border: 2px solid #3B82F6;';
leftBox.innerHTML = `
<div style="color: #60A5FA; font-weight: bold; text-align: center; margin-bottom: 10px; font-size: 13px;">加法世界 🟦</div>
<div style="color: #CBD5E1; font-size: 12px; line-height: 1.8;">
5 + 3 = 8<br>
5 + 2 = 7<br>
5 + 1 = 6<br>
<strong style="color: #FCD34D; font-size: 14px;">5 + 0 = 5</strong><br>
<div style="margin-top: 10px; background: rgba(59, 130, 246, 0.2); padding: 8px; border-radius: 6px; font-size: 11px;">
💡 任何数 + 0 = 它自己<br>
加法的起点 = <strong style="color: #FCD34D;">0</strong>
</div>
</div>
`;
splitContainer.appendChild(leftBox);
gsap.fromTo(leftBox, { opacity: 0, x: -30 }, { opacity: 1, x: 0, duration: 0.6 });
}, 500);
// 右侧:乘法世界
setTimeout(() => {
const rightBox = document.createElement('div');
rightBox.style.cssText = 'flex: 1; background: rgba(245, 158, 11, 0.15); padding: 15px; border-radius: 10px; border: 2px solid #F59E0B;';
rightBox.innerHTML = `
<div style="color: #FBBF24; font-weight: bold; text-align: center; margin-bottom: 10px; font-size: 13px;">乘法世界 🟨</div>
<div style="color: #CBD5E1; font-size: 12px; line-height: 1.8;">
5 × 3 = 15<br>
5 × 2 = 10<br>
<strong style="color: #FCD34D; font-size: 14px;">5 × 1 = 5</strong><br>
5 × ? = 5<br>
<div style="margin-top: 10px; background: rgba(245, 158, 11, 0.2); padding: 8px; border-radius: 6px; font-size: 11px;">
💡 任何数 × 1 = 它自己<br>
乘法的起点 = <strong style="color: #FCD34D;">1</strong>
</div>
</div>
`;
splitContainer.appendChild(rightBox);
gsap.fromTo(rightBox, { opacity: 0, x: 30 }, { opacity: 1, x: 0, duration: 0.6 });
}, 1000);
// 底部总结
setTimeout(() => {
const summary = document.createElement('div');
summary.style.cssText = 'width: 100%; background: rgba(16, 185, 129, 0.15); padding: 15px; border-radius: 10px; border: 2px solid #10B981; margin-top: 15px;';
summary.innerHTML = `
<div style="color: #34D399; font-weight: bold; text-align: center; margin-bottom: 8px; font-size: 13px;">🔑 核心概念</div>
<div style="color: #CBD5E1; font-size: 12px; text-align: center; line-height: 1.6;">
<strong>加法的起点 = 0</strong>(什么都不加)<br>
<strong>乘法的起点 = 1</strong>(什么都不乘)<br>
<div style="margin-top: 8px; font-size: 11px; color: #93C5FD;">
这叫做"单位元"(Identity Element)
</div>
</div>
`;
container.appendChild(summary);
gsap.fromTo(summary, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6 });
document.getElementById('count-all-text').textContent = '记住:加法起点是0,乘法起点是1!';
}, 1800);
},
// 动画2:为什么2⁰=1?(直观理解)
animateWhyZeroA() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('count-all-demo');
container.innerHTML = '';
container.style.display = 'block';
container.style.padding = '15px 10px';
// 标题
const title = document.createElement('div');
title.style.cssText = 'color: #FCD34D; font-size: 16px; font-weight: bold; margin-bottom: 15px; text-align: center;';
title.textContent = '为什么 2⁰ = 1?';
container.appendChild(title);
gsap.fromTo(title, { opacity: 0 }, { opacity: 1, duration: 0.5 });
// Part 1: 递减演示
const steps = [
{ text: '取3个2:', formula: '2 × 2 × 2 = 8', boxes: 3, delay: 0.5 },
{ text: '取2个2:', formula: '2 × 2 = 4', boxes: 2, delay: 1.5 },
{ text: '取1个2:', formula: '2 = 2', boxes: 1, delay: 2.5 },
{ text: '不取2:', formula: '? = ?', boxes: 0, delay: 3.5 }
];
steps.forEach((step, index) => {
setTimeout(() => {
const row = document.createElement('div');
row.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; padding: 10px; background: rgba(255,255,255,0.1); border-radius: 8px;';
const label = document.createElement('div');
label.style.cssText = 'color: #CBD5E1; font-size: 13px; font-weight: bold; min-width: 80px;';
label.textContent = step.text;
row.appendChild(label);
const boxesContainer = document.createElement('div');
boxesContainer.style.cssText = 'display: flex; gap: 5px;';
for (let i = 0; i < step.boxes; i++) {
const box = document.createElement('div');
box.style.cssText = 'width: 30px; height: 30px; background: #F59E0B; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 13px;';
box.textContent = '2';
boxesContainer.appendChild(box);
gsap.fromTo(box, { scale: 0 }, { scale: 1, duration: 0.3, delay: i * 0.1 });
}
row.appendChild(boxesContainer);
const formula = document.createElement('div');
formula.style.cssText = 'color: ' + (index === 3 ? '#FCD34D' : '#10B981') + '; font-size: 13px; font-weight: bold; min-width: 80px; text-align: right;';
formula.textContent = step.formula;
row.appendChild(formula);
container.appendChild(row);
gsap.fromTo(row, { opacity: 0, x: -20 }, { opacity: 1, x: 0, duration: 0.4 });
if (index === 3) {
setTimeout(() => {
formula.textContent = '? = 1';
formula.style.fontSize = '16px';
gsap.to(formula, { scale: [1, 1.2, 1], duration: 0.5 });
document.getElementById('count-all-text').textContent = '规律:8→4→2→1(每次÷2)';
}, 800);
}
}, step.delay * 1000);
});
// Part 2: 解释
setTimeout(() => {
const explanation = document.createElement('div');
explanation.style.cssText = 'width: 100%; background: rgba(16, 185, 129, 0.15); padding: 12px; border-radius: 10px; border: 2px solid #10B981; margin-top: 15px;';
explanation.innerHTML = `
<div style="color: #34D399; font-weight: bold; margin-bottom: 8px; font-size: 13px;">💡 为什么是1?</div>
<div style="color: #CBD5E1; font-size: 11px; line-height: 1.6;">
"不取任何因子"<br>
= 什么都不乘<br>
= 乘法的起点<br>
= <strong style="color: #FCD34D; font-size: 14px;">1</strong>
</div>
`;
container.appendChild(explanation);
gsap.fromTo(explanation, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6 });
setTimeout(() => {
document.getElementById('count-all-text').textContent = '所以 2⁰=1, 3⁰=1, 任何数⁰=1';
}, 600);
}, 5500);
},
// 动画3:数学证明(指数律)
animateWhyZeroB() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('count-all-demo');
container.innerHTML = '';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.padding = '20px 10px';
// 标题
const title = document.createElement('div');
title.style.cssText = 'color: #FCD34D; font-size: 16px; font-weight: bold; margin-bottom: 20px;';
title.textContent = '数学证明';
container.appendChild(title);
gsap.fromTo(title, { opacity: 0 }, { opacity: 1, duration: 0.5 });
// 指数法则
setTimeout(() => {
const rule = document.createElement('div');
rule.style.cssText = 'width: 100%; background: rgba(139, 92, 246, 0.15); padding: 15px; border-radius: 10px; border: 2px solid #8B5CF6; margin-bottom: 15px;';
rule.innerHTML = `
<div style="color: #A78BFA; font-weight: bold; text-align: center; margin-bottom: 10px; font-size: 13px;">指数运算法则</div>
<div style="color: #CBD5E1; font-size: 14px; text-align: center; line-height: 2;">
a<sup>n</sup> ÷ a<sup>n</sup> = a<sup>n-n</sup>
</div>
`;
container.appendChild(rule);
gsap.fromTo(rule, { opacity: 0, y: -20 }, { opacity: 1, y: 0, duration: 0.6 });
document.getElementById('count-all-text').textContent = '用指数律来证明...';
}, 500);
// 左右分屏推导
setTimeout(() => {
const splitContainer = document.createElement('div');
splitContainer.style.cssText = 'display: flex; gap: 15px; width: 100%;';
container.appendChild(splitContainer);
// 左侧:公式推导
const leftBox = document.createElement('div');
leftBox.style.cssText = 'flex: 1; background: rgba(139, 92, 246, 0.15); padding: 12px; border-radius: 10px; border: 2px solid #8B5CF6;';
leftBox.innerHTML = `
<div style="color: #A78BFA; font-weight: bold; text-align: center; margin-bottom: 8px; font-size: 12px;">用公式</div>
<div style="color: #CBD5E1; font-size: 12px; line-height: 1.8; text-align: center;">
2³ ÷ 2³<br>
↓<br>
= 2<sup>3-3</sup><br>
↓<br>
= 2<sup>0</sup>
</div>
`;
splitContainer.appendChild(leftBox);
gsap.fromTo(leftBox, { opacity: 0, x: -30 }, { opacity: 1, x: 0, duration: 0.6 });
// 右侧:直接计算
setTimeout(() => {
const rightBox = document.createElement('div');
rightBox.style.cssText = 'flex: 1; background: rgba(16, 185, 129, 0.15); padding: 12px; border-radius: 10px; border: 2px solid #10B981;';
rightBox.innerHTML = `
<div style="color: #34D399; font-weight: bold; text-align: center; margin-bottom: 8px; font-size: 12px;">直接算</div>
<div style="color: #CBD5E1; font-size: 12px; line-height: 1.8; text-align: center;">
2³ ÷ 2³<br>
↓<br>
= 8 ÷ 8<br>
↓<br>
= 1
</div>
`;
splitContainer.appendChild(rightBox);
gsap.fromTo(rightBox, { opacity: 0, x: 30 }, { opacity: 1, x: 0, duration: 0.6 });
}, 400);
document.getElementById('count-all-text').textContent = '两边必须相等...';
}, 1500);
// 结论
setTimeout(() => {
const conclusion = document.createElement('div');
conclusion.style.cssText = 'width: 100%; background: rgba(252, 211, 77, 0.2); padding: 15px; border-radius: 10px; border: 3px solid #FCD34D; margin-top: 15px;';
conclusion.innerHTML = `
<div style="color: #FCD34D; font-weight: bold; text-align: center; font-size: 18px; margin-bottom: 8px;">
2⁰ = 1 ✓
</div>
<div style="color: #CBD5E1; font-size: 11px; text-align: center; line-height: 1.6;">
为了保持指数律的一致性<br>
必须定义 a⁰ = 1
</div>
`;
container.appendChild(conclusion);
gsap.fromTo(conclusion, { opacity: 0, scale: 0.8 }, { opacity: 1, scale: 1, duration: 0.8 });
setTimeout(() => {
gsap.to(conclusion, { scale: [1, 1.05, 1], duration: 0.5, repeat: 1 });
document.getElementById('count-all-text').textContent = '✨ 证明完成!这是数学规律的必然要求';
}, 300);
}, 3500);
},
// 约数个数动画
animateCount() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('count-all-demo'); // 改这里
container.innerHTML = '';
// 创建因子树
const tree = document.createElement('div');
tree.className = 'factor-tree';
container.appendChild(tree);
// 72 = 2³ × 3²
const steps = [
{ text: '72 = 2³ × 3²', delay: 0 },
{ text: '2的选择:2⁰, 2¹, 2², 2³ (4种)', delay: 1 },
{ text: '3的选择:3⁰, 3¹, 3² (3种)', delay: 2 },
{ text: '约数个数 = 4 × 3 = 12', delay: 3 }
];
steps.forEach((step, i) => {
const row = document.createElement('div');
row.className = 'factor-row';
const box = document.createElement('div');
box.className = 'factor-box';
box.textContent = step.text;
row.appendChild(box);
tree.appendChild(row);
gsap.to(box, {
opacity: 1,
scale: [0.8, 1],
duration: 0.5,
delay: step.delay,
onStart: () => {
document.getElementById('count-text').textContent = step.text;
}
});
});
// 显示所有12个约数
setTimeout(() => {
const divisors = [1, 2, 3, 4, 6, 8, 9, 12, 18, 24, 36, 72];
const grid = document.createElement('div');
grid.className = 'divisor-grid';
container.appendChild(grid);
divisors.forEach((d, i) => {
const box = document.createElement('div');
box.className = 'divisor-box';
box.textContent = d;
grid.appendChild(box);
gsap.to(box, { opacity: 1, scale: [0, 1], duration: 0.3, delay: 4 + i * 0.1 });
});
setTimeout(() => {
document.getElementById('count-text').textContent = '72共有12个约数!';
}, 5200);
}, 4000);
},
// 约数性质动画
animateProperties() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('properties-demo');
container.innerHTML = '';
// 180 = 2² × 3² × 5
const oddDivisors = [1, 3, 5, 9, 15, 45];
const evenDivisors = [2, 4, 6, 10, 12, 18, 20, 30, 36, 60, 90, 180];
// 奇数约数
const oddGrid = document.createElement('div');
oddGrid.className = 'divisor-grid';
oddGrid.style.marginBottom = '20px';
container.appendChild(oddGrid);
const oddLabel = document.createElement('div');
oddLabel.style.cssText = 'color: #FCD34D; font-size: 14px; font-weight: bold; text-align: center; margin-bottom: 10px; opacity: 0;';
oddLabel.textContent = '奇数约数(6个)';
container.insertBefore(oddLabel, oddGrid);
gsap.to(oddLabel, { opacity: 1, duration: 0.5 });
oddDivisors.forEach((d, i) => {
const box = document.createElement('div');
box.className = 'divisor-box';
box.style.background = '#F59E0B';
box.textContent = d;
oddGrid.appendChild(box);
// 修复点:使用 fromTo
gsap.fromTo(box, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.3, delay: i * 0.15 });
});
// 偶数约数
setTimeout(() => {
const evenLabel = document.createElement('div');
evenLabel.style.cssText = 'color: #10B981; font-size: 14px; font-weight: bold; text-align: center; margin-bottom: 10px; opacity: 0;';
evenLabel.textContent = '偶数约数(12个)';
container.appendChild(evenLabel);
gsap.to(evenLabel, { opacity: 1, duration: 0.5 });
const evenGrid = document.createElement('div');
evenGrid.className = 'divisor-grid';
container.appendChild(evenGrid);
evenDivisors.forEach((d, i) => {
const box = document.createElement('div');
box.className = 'divisor-box';
box.style.background = '#10B981';
box.textContent = d;
evenGrid.appendChild(box);
// 修复点:使用 fromTo
gsap.fromTo(box, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.3, delay: i * 0.08 });
});
setTimeout(() => {
document.getElementById('properties-text').textContent = '180共有18个约数:6个奇数,12个偶数';
}, 1500);
}, 1500);
document.getElementById('properties-text').textContent = '开始分类展示...';
},
// 约数和动画
animateSum() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('sum-demo');
container.innerHTML = '';
const steps = [
{ eq: '$72 = 2^3 \\times 3^2$', exp: '分解质因数' },
{ eq: '$\\sigma(72) = (1+2+4+8) \\times (1+3+9)$', exp: '列出各项' },
{ eq: '$= 15 \\times 13$', exp: '计算和' },
{ eq: '$=$ <span class="calc-highlight">$195$</span>', exp: '最终答案' }
];
steps.forEach((step, i) => {
const div = document.createElement('div');
div.className = 'calc-step';
div.innerHTML = `
<div class="calc-equation">${step.eq}</div>
<div class="calc-explanation">${step.exp}</div>
`;
container.appendChild(div);
gsap.to(div, {
opacity: 1,
duration: 0.5,
delay: i * 1,
onStart: () => {
div.classList.add('active');
document.getElementById('sum-text').textContent = `步骤${i + 1}:${step.exp}`;
}
});
});
setTimeout(() => this.renderMath(), 100);
},
// 特殊数动画
animateSpecial() {
if (typeof gsap === 'undefined') return;
const container = document.getElementById('special-demo');
container.innerHTML = '';
const divisors = [1, 2, 3, 6];
const grid = document.createElement('div');
grid.className = 'divisor-grid';
container.appendChild(grid);
divisors.forEach((d, i) => {
const box = document.createElement('div');
box.className = 'divisor-box';
box.textContent = d;
box.style.background = d === 6 ? '#EF4444' : '#3B82F6';
grid.appendChild(box);
// 修复点:这里必须用 fromTo,否则方块会看不见
gsap.fromTo(box, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.4, delay: i * 0.3 });
});
setTimeout(() => {
const sum = document.createElement('div');
sum.style.cssText = 'color: #FCD34D; font-size: 18px; font-weight: bold; text-align: center; margin-top: 20px; opacity: 0;';
sum.innerHTML = '真约数:1 + 2 + 3 = <span style="color: #10B981; font-size: 24px;">6</span>';
container.appendChild(sum);
gsap.to(sum, { opacity: 1, y: [-20, 0], duration: 0.5 });
document.getElementById('special-text').textContent = '6是完全数!真约数和等于自身';
}, 1500);
},
// 例题切换
switchExample(index) {
this.exampleIndex = index;
this.$nextTick(() => this.renderMath());
},
nextExampleStep(index) {
if (this.exampleSteps[index] < this.classicExamples[index].steps.length - 1) {
this.exampleSteps[index]++;
this.$nextTick(() => this.renderMath());
}
},
prevExampleStep(index) {
if (this.exampleSteps[index] > 0) {
this.exampleSteps[index]--;
}
},
resetExampleStep(index) {
this.exampleSteps[index] = 0;
},
// 练习题
checkPracticeAnswer() {
if (this.practiceAnswered) return;
const correctAnswer = this.practiceQuestions[this.currentPracticeIndex].answer.toLowerCase().trim();
const userAnswer = this.userAnswer.toLowerCase().trim().replace(/\s+/g, '');
const correct = userAnswer === correctAnswer;
this.practiceAnswered = true;
this.isPracticeCorrect = correct;
if (correct) {
this.practiceScore += 10;
}
this.$nextTick(() => this.renderMath());
},
nextPractice() {
if (this.currentPracticeIndex < 9) {
this.currentPracticeIndex++;
this.practiceAnswered = false;
this.userAnswer = '';
this.$nextTick(() => this.renderMath());
} else {
this.practiceDone = true;
}
},
// 杯赛真题
checkOlympiadAnswer() {
if (this.olympiadAnswered) return;
const correctAnswer = this.olympiadQuestions[this.currentOlympiadIndex].answer.toLowerCase().trim();
const userAnswer = this.userOlympiadAnswer.toLowerCase().trim().replace(/\s+/g, '');
const correct = userAnswer === correctAnswer;
this.olympiadAnswered = true;
this.isOlympiadCorrect = correct;
if (correct) {
this.olympiadScore += 10;
}
this.$nextTick(() => this.renderMath());
},
nextOlympiad() {
if (this.currentOlympiadIndex < 9) {
this.currentOlympiadIndex++;
this.olympiadAnswered = false;
this.userOlympiadAnswer = '';
this.$nextTick(() => this.renderMath());
} else {
this.olympiadDone = true;
}
}
},
mounted() {
this.renderMath();
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码