可以使用p5等动画插件写一个html,然后录制动画

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正弦函数动画解释</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#FF7D00',
dark: '#1D2129',
light: '#F2F3F5'
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.text-shadow {
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.transition-all-300 {
transition: all 300ms ease-in-out;
}
}
</style>
</head>
<body class="bg-gradient-to-br from-light to-gray-100 font-inter text-dark min-h-screen flex flex-col">
<header class="bg-white shadow-md sticky top-0 z-50">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-primary">
<i class="fa fa-superscript mr-2"></i>数学可视化:正弦函数
</h1>
<div class="flex items-center space-x-4">
<button id="playPauseBtn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-lg shadow transition-all-300 flex items-center">
<i class="fa fa-play mr-2" id="playIcon"></i>
<span id="playBtnText">播放解说</span>
</button>
<button id="fullscreenBtn" class="bg-secondary hover:bg-secondary/90 text-white px-4 py-2 rounded-lg shadow transition-all-300">
<i class="fa fa-expand mr-2"></i>全屏
</button>
</div>
</div>
</header>
<main class="flex-grow container mx-auto px-4 py-8 grid grid-cols-1 lg:grid-cols-3 gap-8">
<div class="lg:col-span-2 bg-white rounded-xl shadow-lg overflow-hidden">
<div class="relative" style="padding-bottom: 56.25%;">
<div id="p5Container" class="absolute inset-0"></div>
</div>
</div>
<div class="lg:col-span-1 bg-white rounded-xl shadow-lg p-6 flex flex-col">
<h2 class="text-2xl font-bold mb-4 text-primary border-b pb-2">正弦函数解说</h2>
<div id="transcript" class="flex-grow overflow-y-auto mb-6">
<div class="mb-4">
<p class="text-lg font-semibold text-primary">1. 基本定义</p>
<p class="text-dark/80">正弦函数(sin)是三角函数的一种,表示直角三角形中对边与斜边的比值。在单位圆上,正弦值等于点的y坐标。</p>
</div>
<div class="mb-4">
<p class="text-lg font-semibold text-primary">2. 波形特征</p>
<p class="text-dark/80">正弦波是周期性的,其形状在时间或空间上重复。它的周期是2π,意味着每经过2π的间隔,波形就会重复一次。</p>
</div>
<div class="mb-4">
<p class="text-lg font-semibold text-primary">3. 关键参数</p>
<p class="text-dark/80">振幅(Amplitude)决定了波形的高度,频率(Frequency)决定了波形的密集程度,相位(Phase)决定了波形的起始位置。</p>
</div>
<div class="mb-4">
<p class="text-lg font-semibold text-primary">4. 应用领域</p>
<p class="text-dark/80">正弦函数在物理学、工程学、信号处理、音乐等领域有广泛应用,例如描述振动、交流电、声波等。</p>
</div>
</div>
<div class="space-y-4">
<div class="bg-light rounded-lg p-4">
<h3 class="font-semibold mb-2">控制参数</h3>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-dark/70 mb-1">振幅 (A)</label>
<input type="range" id="amplitudeSlider" min="0.1" max="2" step="0.1" value="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
<div class="flex justify-between text-xs text-dark/60">
<span>0.1</span>
<span id="amplitudeValue">1.0</span>
<span>2.0</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-dark/70 mb-1">频率 (ω)</label>
<input type="range" id="frequencySlider" min="0.1" max="2" step="0.1" value="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
<div class="flex justify-between text-xs text-dark/60">
<span>0.1</span>
<span id="frequencyValue">1.0</span>
<span>2.0</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-dark/70 mb-1">相位 (φ)</label>
<input type="range" id="phaseSlider" min="0" max="6.28" step="0.1" value="0" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
<div class="flex justify-between text-xs text-dark/60">
<span>0</span>
<span id="phaseValue">0.0</span>
<span>2π</span>
</div>
</div>
</div>
</div>
<div class="text-center">
<button id="resetBtn" class="bg-gray-200 hover:bg-gray-300 text-dark px-4 py-2 rounded-lg shadow transition-all-300">
<i class="fa fa-refresh mr-2"></i>重置参数
</button>
</div>
</div>
</div>
</main>
<footer class="bg-dark text-white py-6 mt-8">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<h2 class="text-xl font-bold">数学可视化项目</h2>
<p class="text-gray-400 mt-1">让抽象概念变得直观易懂</p>
</div>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fa fa-github text-xl"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fa fa-twitter text-xl"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<i class="fa fa-envelope text-xl"></i>
</a>
</div>
</div>
<div class="border-t border-gray-700 mt-6 pt-6 text-center text-gray-400">
© 2025 数学可视化团队. 保留所有权利.
</div>
</div>
</footer>
<script>
// 音频解说 - 使用Web Speech API合成
const synth = window.speechSynthesis;
let isSpeaking = false;
let utterance = null;
// 正弦函数参数
let amplitude = 1;
let frequency = 1;
let phase = 0;
// 音频解说文本
const script = [
"欢迎来到正弦函数的可视化讲解。正弦函数是数学中最基本的函数之一,在科学和工程领域有广泛应用。",
"正弦函数可以用单位圆来定义。当一个点在单位圆上运动时,其y坐标随角度的变化就是正弦函数。",
"正弦波的形状是一条平滑的周期性曲线。它的基本形式是y等于sin x,其中x是角度。",
"正弦函数有三个重要参数:振幅、频率和相位。让我们依次了解它们的影响。",
"振幅决定了波形的高度。增大振幅会使波形更高,减小振幅则会使波形更矮。",
"频率决定了波形的密集程度。频率越高,波形在单位长度内重复的次数就越多。",
"相位决定了波形的起始位置。改变相位会使波形左右移动,但不会改变其形状和大小。",
"正弦函数在现实生活中有许多应用,包括交流电、声波、电磁波等周期性现象的描述。",
"通过调整这些参数,我们可以创建出各种各样的正弦波形,从而模拟不同的物理现象。",
"感谢观看这个正弦函数的可视化讲解。希望这能帮助你更好地理解这个重要的数学概念。"
];
// 播放/暂停解说
document.getElementById('playPauseBtn').addEventListener('click', () => {
if (isSpeaking) {
synth.cancel();
updatePlayButton(false);
} else {
speakScript();
updatePlayButton(true);
}
});
// 全屏切换
document.getElementById('fullscreenBtn').addEventListener('click', () => {
const container = document.getElementById('p5Container').parentElement;
if (!document.fullscreenElement) {
container.requestFullscreen().catch(err => {
console.error(`全屏请求错误: ${err.message}`);
});
} else {
document.exitFullscreen();
}
});
// 重置参数
document.getElementById('resetBtn').addEventListener('click', () => {
document.getElementById('amplitudeSlider').value = 1;
document.getElementById('frequencySlider').value = 1;
document.getElementById('phaseSlider').value = 0;
updateParameters(1, 1, 0);
});
// 更新播放按钮状态
function updatePlayButton(isPlaying) {
isSpeaking = isPlaying;
const playIcon = document.getElementById('playIcon');
const playBtnText = document.getElementById('playBtnText');
if (isPlaying) {
playIcon.className = 'fa fa-pause mr-2';
playBtnText.textContent = '暂停解说';
} else {
playIcon.className = 'fa fa-play mr-2';
playBtnText.textContent = '播放解说';
}
}
// 播放解说脚本
function speakScript() {
if (synth.speaking) return;
utterance = new SpeechSynthesisUtterance(script.join(' '));
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
utterance.pitch = 1;
utterance.onend = () => {
updatePlayButton(false);
};
utterance.onerror = (event) => {
console.error('语音合成错误:', event.error);
updatePlayButton(false);
};
synth.speak(utterance);
updatePlayButton(true);
}
// 参数滑块事件监听
document.getElementById('amplitudeSlider').addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
document.getElementById('amplitudeValue').textContent = value.toFixed(1);
updateParameters(value, frequency, phase);
});
document.getElementById('frequencySlider').addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
document.getElementById('frequencyValue').textContent = value.toFixed(1);
updateParameters(amplitude, value, phase);
});
document.getElementById('phaseSlider').addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
document.getElementById('phaseValue').textContent = value.toFixed(1);
updateParameters(amplitude, frequency, value);
});
// 更新正弦函数参数
function updateParameters(amp, freq, ph) {
amplitude = amp;
frequency = freq;
phase = ph;
if (window.drawSineWave) {
window.drawSineWave(amplitude, frequency, phase);
}
}
// P5.js 动画
const sketch = (p) => {
let angle = 0;
let pointOnCircle;
let wavePoints = [];
let waveX = 300;
let waveY = 200;
let waveAmplitude = 1;
let waveFrequency = 1;
let wavePhase = 0;
p.setup = () => {
const container = document.getElementById('p5Container');
const canvas = p.createCanvas(container.offsetWidth, container.offsetHeight);
canvas.parent(container);
// 初始化波形绘制函数
window.drawSineWave = (amp, freq, ph) => {
waveAmplitude = amp;
waveFrequency = freq;
wavePhase = ph;
};
};
p.windowResized = () => {
const container = document.getElementById('p5Container');
p.resizeCanvas(container.offsetWidth, container.offsetHeight);
waveX = p.width * 0.4;
waveY = p.height / 2;
};
p.draw = () => {
p.background(240);
p.translate(p.width * 0.25, p.height / 2);
// 绘制坐标系
drawAxes();
// 绘制单位圆
const radius = 100 * waveAmplitude;
p.stroke(20, 93, 255, 150);
p.strokeWeight(2);
p.noFill();
p.ellipse(0, 0, radius * 2);
// 计算圆上点的位置
const x = radius * p.cos(angle * waveFrequency + wavePhase);
const y = radius * p.sin(angle * waveFrequency + wavePhase);
pointOnCircle = { x, y };
// 绘制圆上的点
p.fill(255, 125, 0);
p.stroke(255, 125, 0);
p.strokeWeight(2);
p.ellipse(x, y, 12);
// 绘制从圆心到点的线
p.stroke(255, 125, 0);
p.strokeWeight(2);
p.line(0, 0, x, y);
// 绘制水平线到波形
p.stroke(20, 93, 255);
p.strokeWeight(2);
p.line(x, y, waveX - radius, y);
// 更新波形点
wavePoints.unshift(y);
if (wavePoints.length > 400) {
wavePoints.pop();
}
// 绘制波形
p.push();
p.translate(waveX, 0);
// 绘制波形坐标系
drawWaveAxes();
// 绘制波形
p.beginShape();
p.noFill();
p.stroke(20, 93, 255);
p.strokeWeight(3);
for (let i = 0; i < wavePoints.length; i++) {
p.vertex(i, wavePoints[i]);
}
p.endShape();
// 绘制当前点在波形上的位置
p.fill(255, 125, 0);
p.stroke(255, 125, 0);
p.strokeWeight(2);
p.ellipse(0, wavePoints[0], 12);
// 绘制函数表达式
p.fill(30);
p.noStroke();
p.textSize(18);
p.textAlign(p.LEFT, p.TOP);
p.text(`y = ${waveAmplitude.toFixed(1)} sin(${waveFrequency.toFixed(1)}x + ${wavePhase.toFixed(1)})`, -radius, -radius - 30);
p.pop();
// 更新角度
angle += 0.02;
};
// 绘制坐标系
function drawAxes() {
p.stroke(150);
p.strokeWeight(1);
// x轴
p.line(-150, 0, p.width * 0.75, 0);
// y轴
p.line(0, -200, 0, 200);
// 标记原点
p.fill(50);
p.noStroke();
p.textSize(14);
p.textAlign(p.CENTER, p.TOP);
p.text("O", -10, 5);
// 标记单位圆
p.textAlign(p.LEFT, p.CENTER);
p.text("1", 105 * waveAmplitude, 5);
p.textAlign(p.CENTER, p.BOTTOM);
p.text("1", 5, -105 * waveAmplitude);
}
// 绘制波形坐标系
function drawWaveAxes() {
p.stroke(150);
p.strokeWeight(1);
// x轴
p.line(-100, 0, 400, 0);
// y轴
p.line(0, -200, 0, 200);
// 标记刻度
p.fill(50);
p.noStroke();
p.textSize(14);
p.textAlign(p.CENTER, p.TOP);
// x轴刻度 (π的倍数)
for (let i = 1; i < 4; i++) {
const xPos = i * 100;
p.line(xPos, -5, xPos, 5);
p.text(`${i}π`, xPos, 10);
}
for (let i = -1; i >= -1; i--) {
const xPos = i * 100;
p.line(xPos, -5, xPos, 5);
p.text(`${i}π`, xPos, 10);
}
// y轴刻度
p.textAlign(p.RIGHT, p.CENTER);
p.line(-5, 100 * waveAmplitude, 5, 100 * waveAmplitude);
p.text(`${waveAmplitude.toFixed(1)}`, -10, 100 * waveAmplitude);
p.line(-5, -100 * waveAmplitude, 5, -100 * waveAmplitude);
p.text(`-${waveAmplitude.toFixed(1)}`, -10, -100 * waveAmplitude);
}
};
// 创建p5实例
new p5(sketch);
</script>
</body>
</html>
网友回复


