可以使用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>
网友回复
为啥所有的照片分辨率提升工具都会修改照片上的图案细节?
js如何在浏览器中将webm视频的声音分离为单独音频?
微信小程序如何播放第三方域名url的mp4视频?
ai多模态大模型能实时识别视频中的手语为文字吗?
如何远程调试别人的chrome浏览器获取调试信息?
为啥js打开新网页window.open设置窗口宽高无效?
浏览器中js的navigator.mediaDevices.getDisplayMedia屏幕录像无法录制SpeechSynthesisUtterance产生的说话声音?
js中mediaRecorder如何录制window.speechSynthesis声音音频并下载?
python如何直接获取抖音短视频的音频文件url?
js在浏览器中如何使用MediaStream与MediaRecorder实现声音音频多轨道混流?