html5+css3实现傅里叶变换的动态展示效果(仅供参考)
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>傅里叶变换的动态展示效果</title><style>body {font-family: 'Microsoft YaHei', Arial, sans-serif;margin: 0;padding: 0;background: #f8f9fa;color: #333;}.container {max-width: 1200px;margin: 0 auto;padding: 20px;}h1 {text-align: center;color: #2c3e50;margin-bottom: 30px;}.controls {display: flex;flex-wrap: wrap;gap: 20px;justify-content: center;margin-bottom: 30px;background: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);}.control-group {display: flex;flex-direction: column;min-width: 200px;}label {margin-bottom: 5px;font-weight: bold;}input[type="range"], select {width: 100%;padding: 8px;border: 1px solid #ddd;border-radius: 4px;}button {background: #3498db;color: white;border: none;padding: 10px 20px;border-radius: 4px;cursor: pointer;font-size: 16px;transition: background-color 0.3s;}button:hover {background: #2980b9;}.visualization {display: flex;flex-wrap: wrap;gap: 20px;justify-content: center;}.canvas-container {background: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);flex: 1;min-width: 300px;}canvas {width: 100%;height: 300px;background: #f1f1f1;border-radius: 4px;}.explanation {margin-top: 30px;background: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);}.formula {font-family: 'Cambria Math', serif;text-align: center;margin: 20px 0;font-size: 18px;}.loading {display: none;text-align: center;margin: 20px 0;}.loading img {width: 50px;height: 50px;}@media (max-width: 768px) {.canvas-container {min-width: 100%;}}</style>
</head>
<body><div class="container"><h1>傅里叶变换的动态展示效果</h1><div class="controls"><div class="control-group"><label for="waveType">波形类型</label><select id="waveType"><option value="sine">正弦波</option><option value="square">方波</option><option value="sawtooth">锯齿波</option><option value="triangle">三角波</option><option value="custom">自定义波形</option></select></div><div class="control-group"><label for="frequency">频率 (Hz)</label><input type="range" id="frequency" min="1" max="20" value="5" step="1"><span id="frequencyValue">5 Hz</span></div><div class="control-group"><label for="amplitude">振幅</label><input type="range" id="amplitude" min="0.1" max="1" value="0.5" step="0.1"><span id="amplitudeValue">0.5</span></div><div class="control-group"><label for="harmonics">谐波数量</label><input type="range" id="harmonics" min="1" max="20" value="5" step="1"><span id="harmonicsValue">5</span></div><button id="updateBtn">更新波形</button><button id="computeBtn">计算傅里叶变换</button></div><div class="loading" id="loading"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzgiIGhlaWdodD0iMzgiIHZpZXdCb3g9IjAgMCAzOCAzOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBzdHJva2U9IiMzNDk4ZGIiPgogICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxIDEpIiBzdHJva2Utd2lkdGg9IjIiPgogICAgICAgICAgICA8Y2lyY2xlIHN0cm9rZS1vcGFjaXR5PSIuNSIgY3g9IjE4IiBjeT0iMTgiIHI9IjE4Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0zNiAxOGMwLTkuOTQtOC4wNi0xOC0xOC0xOCI+CiAgICAgICAgICAgICAgICA8YW5pbWF0ZVRyYW5zZm9ybQogICAgICAgICAgICAgICAgICAgIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIKICAgICAgICAgICAgICAgICAgICB0eXBlPSJyb3RhdGUiCiAgICAgICAgICAgICAgICAgICAgZnJvbT0iMCAxOCAxOCIKICAgICAgICAgICAgICAgICAgICB0bz0iMzYwIDE4IDE4IgogICAgICAgICAgICAgICAgICAgIGR1cj0iMXMiCiAgICAgICAgICAgICAgICAgICAgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiLz4KICAgICAgICAgICAgPC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" alt="加载中..."><p>计算中...</p></div><div class="visualization"><div class="canvas-container"><h3>原始波形</h3><canvas id="originalWave"></canvas></div><div class="canvas-container"><h3>傅里叶变换频谱</h3><canvas id="fourierSpectrum"></canvas></div></div><div class="visualization"><div class="canvas-container"><h3>傅里叶级数合成波形</h3><canvas id="synthesizedWave"></canvas></div><div class="canvas-container"><h3>相位谱</h3><canvas id="phaseSpectrum"></canvas></div></div><div class="explanation"><h2>傅里叶变换原理</h2><p>傅里叶变换是一种将时域信号转换为频域表示的数学工具。它可以将任何周期性的函数分解为简单正弦波的叠加。</p><div class="formula"><p>离散傅里叶变换 (DFT):</p><p>X(k) = Σ[n=0 to N-1] x(n) · e<sup>-j2πkn/N</sup></p><p>傅里叶级数:</p><p>f(t) = a<sub>0</sub>/2 + Σ[n=1 to ∞] [a<sub>n</sub>cos(nωt) + b<sub>n</sub>sin(nωt)]</p></div><p>在这个演示中,您可以通过控制面板选择不同的波形,并观察其傅里叶变换结果。傅里叶变换让我们能够看到波形中包含的各种频率成分及其振幅和相位。</p></div></div><script>// DOM 元素const waveTypeSelect = document.getElementById('waveType');const frequencySlider = document.getElementById('frequency');const frequencyValue = document.getElementById('frequencyValue');const amplitudeSlider = document.getElementById('amplitude');const amplitudeValue = document.getElementById('amplitudeValue');const harmonicsSlider = document.getElementById('harmonics');const harmonicsValue = document.getElementById('harmonicsValue');const updateBtn = document.getElementById('updateBtn');const computeBtn = document.getElementById('computeBtn');const loading = document.getElementById('loading');const originalCanvas = document.getElementById('originalWave');const fourierCanvas = document.getElementById('fourierSpectrum');const synthesizedCanvas = document.getElementById('synthesizedWave');const phaseCanvas = document.getElementById('phaseSpectrum');// 设置画布大小function setupCanvas(canvas) {canvas.width = canvas.offsetWidth;canvas.height = canvas.offsetHeight;return canvas.getContext('2d');}const originalCtx = setupCanvas(originalCanvas);const fourierCtx = setupCanvas(fourierCanvas);const synthesizedCtx = setupCanvas(synthesizedCanvas);const phaseCtx = setupCanvas(phaseCanvas);// 更新UI显示值frequencySlider.addEventListener('input', () => {frequencyValue.textContent = `${frequencySlider.value} Hz`;});amplitudeSlider.addEventListener('input', () => {amplitudeValue.textContent = amplitudeSlider.value;});harmonicsSlider.addEventListener('input', () => {harmonicsValue.textContent = harmonicsSlider.value;});// 窗口调整大小时重新设置画布window.addEventListener('resize', () => {setupCanvas(originalCanvas);setupCanvas(fourierCanvas);setupCanvas(synthesizedCanvas);setupCanvas(phaseCanvas);drawWave();});// 生成不同类型的波形数据function generateWaveData(type, frequency, amplitude, sampleCount) {const data = new Array(sampleCount);const period = sampleCount / frequency;for (let i = 0; i < sampleCount; i++) {const t = i / sampleCount;const x = 2 * Math.PI * frequency * t;switch (type) {case 'sine':data[i] = amplitude * Math.sin(x);break;case 'square':data[i] = amplitude * (Math.sin(x) >= 0 ? 1 : -1);break;case 'sawtooth':data[i] = amplitude * (2 * (x / (2 * Math.PI) - Math.floor(0.5 + x / (2 * Math.PI))));break;case 'triangle':data[i] = amplitude * (2 * Math.abs(2 * (x / (2 * Math.PI) - Math.floor(0.5 + x / (2 * Math.PI)))) - 1);break;case 'custom':// 自定义波形:基本正弦波加上一些谐波data[i] = amplitude * (Math.sin(x) + 0.5 * Math.sin(3 * x) + 0.3 * Math.sin(5 * x)) / 1.8; // 归一化break;default:data[i] = amplitude * Math.sin(x);}}return data;}// 绘制波形function drawWaveform(ctx, data, color = '#3498db') {const width = ctx.canvas.width;const height = ctx.canvas.height;ctx.clearRect(0, 0, width, height);ctx.beginPath();ctx.strokeStyle = color;ctx.lineWidth = 2;const stepSize = width / (data.length - 1);const centerY = height / 2;const scale = height / 2 * 0.9;for (let i = 0; i < data.length; i++) {const x = i * stepSize;const y = centerY - data[i] * scale;if (i === 0) ctx.moveTo(x, y);else ctx.lineTo(x, y);}ctx.stroke();// 绘制x轴和y轴ctx.beginPath();ctx.strokeStyle = '#aaa';ctx.lineWidth = 1;ctx.moveTo(0, centerY);ctx.lineTo(width, centerY);ctx.stroke();}// 绘制频谱function drawSpectrum(ctx, magnitudes, maxFreq = 20) {const width = ctx.canvas.width;const height = ctx.canvas.height;ctx.clearRect(0, 0, width, height);// 找出最大振幅以便归一化const maxMagnitude = Math.max(...magnitudes.slice(1)); // 忽略直流分量const barWidth = width / maxFreq;const scale = height * 0.9;// 绘制频谱柱状图ctx.fillStyle = '#3498db';for (let i = 0; i < Math.min(maxFreq, magnitudes.length); i++) {const magnitude = i === 0 ? 0 : magnitudes[i] / maxMagnitude; // 忽略直流分量const barHeight = magnitude * scale;const x = i * barWidth;const y = height - barHeight;ctx.fillRect(x, y, barWidth * 0.8, barHeight);}// 绘制x轴ctx.beginPath();ctx.strokeStyle = '#aaa';ctx.lineWidth = 1;ctx.moveTo(0, height);ctx.lineTo(width, height);ctx.stroke();// 绘制频率刻度ctx.fillStyle = '#666';ctx.font = '10px Arial';ctx.textAlign = 'center';for (let i = 0; i < maxFreq; i += 5) {const x = i * barWidth + barWidth / 2;ctx.fillText(`${i} Hz`, x, height - 5);}}// 绘制相位谱function drawPhaseSpectrum(ctx, phases, maxFreq = 20) {const width = ctx.canvas.width;const height = ctx.canvas.height;ctx.clearRect(0, 0, width, height);const barWidth = width / maxFreq;const centerY = height / 2;const scale = height / 2 * 0.8;// 绘制相位点和连线ctx.beginPath();ctx.strokeStyle = '#e74c3c';ctx.lineWidth = 2;for (let i = 1; i < Math.min(maxFreq, phases.length); i++) {const x = i * barWidth + barWidth / 2;const y = centerY - phases[i] / Math.PI * scale;if (i === 1) ctx.moveTo(x, y);else ctx.lineTo(x, y);// 绘制圆点ctx.fillStyle = '#e74c3c';ctx.beginPath();ctx.arc(x, y, 3, 0, 2 * Math.PI);ctx.fill();}ctx.stroke();// 绘制x轴和y轴ctx.beginPath();ctx.strokeStyle = '#aaa';ctx.lineWidth = 1;ctx.moveTo(0, centerY);ctx.lineTo(width, centerY);ctx.moveTo(0, centerY - scale);ctx.lineTo(width, centerY - scale);ctx.fillText('π', 10, centerY - scale);ctx.moveTo(0, centerY + scale);ctx.lineTo(width, centerY + scale);ctx.fillText('-π', 10, centerY + scale);ctx.stroke();}// FFT算法实现 (Cooley-Tukey)function fft(x) {const N = x.length;if (N <= 1) {return x;}// 确保N是2的幂if (N & (N - 1)) {throw new Error("FFT长度必须是2的幂");}// 分治法:分别计算偶数和奇数索引const even = new Array(N / 2);const odd = new Array(N / 2);for (let i = 0; i < N / 2; i++) {even[i] = x[i * 2];odd[i] = x[i * 2 + 1];}// 递归计算const evenResult = fft(even);const oddResult = fft(odd);// 合并结果const result = new Array(N).fill().map(() => new Complex(0, 0));for (let k = 0; k < N / 2; k++) {const twiddle = Complex.fromPolar(1, -2 * Math.PI * k / N);const oddTerm = Complex.multiply(twiddle, oddResult[k]);result[k] = Complex.add(evenResult[k], oddTerm);result[k + N / 2] = Complex.subtract(evenResult[k], oddTerm);}return result;}// 复数类class Complex {constructor(re, im) {this.re = re;this.im = im;}static add(a, b) {return new Complex(a.re + b.re, a.im + b.im);}static subtract(a, b) {return new Complex(a.re - b.re, a.im - b.im);}static multiply(a, b) {return new Complex(a.re * b.re - a.im * b.im,a.re * b.im + a.im * b.re);}static fromPolar(r, theta) {return new Complex(r * Math.cos(theta),r * Math.sin(theta));}magnitude() {return Math.sqrt(this.re * this.re + this.im * this.im);}phase() {return Math.atan2(this.im, this.re);}}// 计算傅里叶变换function computeFourier(data) {// 确保数据长度是2的幂const nextPow2 = Math.pow(2, Math.ceil(Math.log2(data.length)));const paddedData = new Array(nextPow2).fill(0);for (let i = 0; i < data.length; i++) {paddedData[i] = new Complex(data[i], 0);}for (let i = data.length; i < nextPow2; i++) {paddedData[i] = new Complex(0, 0);}// 计算FFTconst result = fft(paddedData);// 提取幅度和相位const magnitudes = result.map(c => c.magnitude() / Math.sqrt(nextPow2));const phases = result.map(c => c.phase());return { magnitudes, phases, result };}// 使用傅里叶级数合成波形function synthesizeWave(fourierResult, harmonics, sampleCount) {const synthesized = new Array(sampleCount).fill(0);const { magnitudes, phases, result } = fourierResult;// 使用指定数量的谐波合成for (let t = 0; t < sampleCount; t++) {let sum = magnitudes[0] / 2; // 直流分量for (let n = 1; n <= harmonics; n++) {if (n < magnitudes.length) {const amplitude = magnitudes[n];const phase = phases[n];const angle = 2 * Math.PI * n * t / sampleCount;sum += amplitude * Math.cos(angle + phase);}}synthesized[t] = sum;}return synthesized;}// 主波形绘制函数function drawWave() {const waveType = waveTypeSelect.value;const frequency = parseInt(frequencySlider.value);const amplitude = parseFloat(amplitudeSlider.value);const harmonics = parseInt(harmonicsSlider.value);const sampleCount = 1024;// 生成原始波形const waveData = generateWaveData(waveType, frequency, amplitude, sampleCount);drawWaveform(originalCtx, waveData);return { waveType, frequency, amplitude, harmonics, sampleCount, waveData };}// 计算并绘制傅里叶变换相关图像function computeAndDrawFourier() {loading.style.display = 'block';setTimeout(() => {const { waveType, frequency, amplitude, harmonics, sampleCount, waveData } = drawWave();// 计算傅里叶变换const fourierResult = computeFourier(waveData);const { magnitudes, phases } = fourierResult;// 绘制频谱drawSpectrum(fourierCtx, magnitudes);// 绘制相位谱drawPhaseSpectrum(phaseCtx, phases);// 合成波形const synthesized = synthesizeWave(fourierResult, harmonics, sampleCount);drawWaveform(synthesizedCtx, synthesized, '#27ae60');loading.style.display = 'none';}, 100);}// 事件监听updateBtn.addEventListener('click', drawWave);computeBtn.addEventListener('click', computeAndDrawFourier);// 初始绘制drawWave();computeAndDrawFourier();// AJAX功能 - 可以加载预设波形function loadPresetWaveform(presetName) {// 模拟AJAX请求loading.style.display = 'block';// 使用setTimeout模拟网络延迟setTimeout(() => {let presetConfig = {};// 预设配置switch (presetName) {case 'speech':presetConfig = {waveType: 'custom',frequency: 8,amplitude: 0.8,harmonics: 15};break;case 'music':presetConfig = {waveType: 'sine',frequency: 12,amplitude: 0.7,harmonics: 10};break;case 'noise':presetConfig = {waveType: 'sawtooth',frequency: 3,amplitude: 0.9,harmonics: 20};break;default:presetConfig = {waveType: 'sine',frequency: 5,amplitude: 0.5,harmonics: 5};}// 更新UIwaveTypeSelect.value = presetConfig.waveType;frequencySlider.value = presetConfig.frequency;frequencyValue.textContent = `${presetConfig.frequency} Hz`;amplitudeSlider.value = presetConfig.amplitude;amplitudeValue.textContent = presetConfig.amplitude;harmonicsSlider.value = presetConfig.harmonics;harmonicsValue.textContent = presetConfig.harmonics;// 重新绘制drawWave();computeAndDrawFourier();loading.style.display = 'none';}, 500);}// 添加预设按钮const presetContainer = document.createElement('div');presetContainer.className = 'control-group';presetContainer.innerHTML = `<label>预设波形</label><div style="display: flex; gap: 10px;"><button id="preset-speech">语音</button><button id="preset-music">音乐</button><button id="preset-noise">噪声</button></div>`;document.querySelector('.controls').appendChild(presetContainer);document.getElementById('preset-speech').addEventListener('click', () => loadPresetWaveform('speech'));document.getElementById('preset-music').addEventListener('click', () => loadPresetWaveform('music'));document.getElementById('preset-noise').addEventListener('click', () => loadPresetWaveform('noise'));</script>
</body>
</html>
仅供参考,我也不懂数学。
傅里叶变换是一种将时域信号转换为频域表示的数学工具。它可以将任何周期性的函数分解为简单正弦波的叠加。
离散傅里叶变换 (DFT):
X(k) = Σ[n=0 to N-1] x(n) · e-j2πkn/N
傅里叶级数:
f(t) = a0/2 + Σ[n=1 to ∞] [ancos(nωt) + bnsin(nωt)]
在这个演示中,您可以通过控制面板选择不同的波形,并观察其傅里叶变换结果。傅里叶变换让我们能够看到波形中包含的各种频率成分及其振幅和相位。