傅里叶变换互动演示HTML实现

03 11月
作者:cinjep|分类:辅助教学

这个演示将包括:

  1. 一个由多个正弦波组成的时域信号。

  2. 每个正弦波的频率、振幅和相位可以调整。

  3. 显示每个正弦波的图形以及它们的叠加结果。

  4. 通过傅里叶变换(使用FFT)将时域信号转换为频域,并显示频谱。

傅里叶变换.png

以下是实现代码( 由 deepseek 依据人工指令生成):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>傅里叶变换互动演示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #0a1a3a, #1a3a5f, #2a5a8f);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
            width: 100%;
            overflow-x: hidden;
        }
        
        .container {
            max-width: 95vw;
            width: 100%;
            margin: 0 auto;
            padding: 20px;
        }
        
        header {
            text-align: center;
            margin-bottom: 20px;
            padding: 15px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
        }
        
        h1 {
            font-size: 2.2rem;
            margin-bottom: 8px;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
        }
        
        .subtitle {
            font-size: 1.1rem;
            opacity: 0.9;
            max-width: 800px;
            margin: 0 auto;
            line-height: 1.5;
        }
        
        .dashboard {
            display: grid;
            grid-template-columns: minmax(280px, 320px) 1fr;
            gap: 20px;
            height: calc(100vh - 180px);
            width: 100%;
        }
        
        @media (max-width: 1100px) {
            .dashboard {
                grid-template-columns: 1fr;
                height: auto;
            }
        }
        
        .control-panel {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            overflow-y: auto;
            overflow-x: hidden;
        }
        
        /* 美化滚动条 */
        .control-panel::-webkit-scrollbar {
            width: 10px;
        }
        
        .control-panel::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 10px;
        }
        
        .control-panel::-webkit-scrollbar-thumb {
            background: linear-gradient(to bottom, #4d9fff, #2a5a8f);
            border-radius: 10px;
            border: 2px solid rgba(255, 255, 255, 0.1);
        }
        
        .control-panel::-webkit-scrollbar-thumb:hover {
            background: linear-gradient(to bottom, #6babff, #3a6aaf);
        }
        
        .visualization-area {
            display: grid;
            grid-template-rows: 1fr 1fr;
            gap: 20px;
            width: 100%;
        }
        
        .visualization-row {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            width: 100%;
        }
        
        @media (max-width: 900px) {
            .visualization-row {
                grid-template-columns: 1fr;
            }
        }
        
        .panel {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            padding: 15px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            display: flex;
            flex-direction: column;
            width: 100%;
        }
        
        .panel h2 {
            margin-bottom: 12px;
            font-size: 1.3rem;
            border-bottom: 1px solid rgba(255, 255, 255, 0.2);
            padding-bottom: 8px;
        }
        
        .canvas-container {
            flex: 1;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 8px;
            overflow: hidden;
            margin-bottom: 10px;
            width: 100%;
        }
        
        canvas {
            width: 100%;
            height: 100%;
            display: block;
        }
        
        .controls {
            display: flex;
            flex-direction: column;
            gap: 12px;
            width: 100%;
        }
        
        .control-group {
            background: rgba(255, 255, 255, 0.05);
            padding: 12px;
            border-radius: 8px;
            width: 100%;
        }
        
        .control-group h3 {
            margin-bottom: 8px;
            font-size: 1.1rem;
            color: #a8d1ff;
        }
        
        .control-row {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            width: 100%;
        }
        
        .control-row label {
            width: 70px;
            font-weight: 500;
            font-size: 0.9rem;
        }
        
        .control-row input {
            flex: 1;
        }
        
        .control-row span {
            width: 50px;
            text-align: right;
            font-size: 0.9rem;
        }
        
        input[type="range"] {
            -webkit-appearance: none;
            height: 6px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 4px;
            outline: none;
        }
        
        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            background: #4d9fff;
            border-radius: 50%;
            cursor: pointer;
        }
        
        .button-group {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-top: 5px;
            width: 100%;
        }
        
        .btn {
            padding: 8px 12px;
            background: rgba(255, 255, 255, 0.1);
            border: none;
            border-radius: 6px;
            color: white;
            font-size: 0.9rem;
            cursor: pointer;
            transition: all 0.3s ease;
            flex: 1;
            min-width: 80px;
        }
        
        .btn:hover {
            background: rgba(255, 255, 255, 0.2);
        }
        
        .btn.active {
            background: #4d9fff;
            color: #0a1a3a;
        }
        
        .presets {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            width: 100%;
        }
        
        .preset-btn {
            padding: 8px;
            background: rgba(255, 255, 255, 0.1);
            border: none;
            border-radius: 6px;
            color: white;
            font-size: 0.85rem;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .preset-btn:hover {
            background: rgba(255, 255, 255, 0.2);
        }
        
        .tab-container {
            margin-top: 20px;
            width: 100%;
        }
        
        .tabs {
            display: flex;
            margin-bottom: 10px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.2);
            width: 100%;
        }
        
        .tab {
            padding: 8px 15px;
            cursor: pointer;
            opacity: 0.7;
            transition: all 0.3s ease;
            font-size: 0.9rem;
        }
        
        .tab.active {
            opacity: 1;
            border-bottom: 2px solid #4d9fff;
        }
        
        .tab-content {
            display: none;
            width: 100%;
        }
        
        .tab-content.active {
            display: block;
        }
        
        .harmonic-controls-container {
            max-height: 300px;
            overflow-y: auto;
            padding-right: 5px;
            width: 100%;
        }
        
        /* 谐波控制区的滚动条样式 */
        .harmonic-controls-container::-webkit-scrollbar {
            width: 8px;
        }
        
        .harmonic-controls-container::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 8px;
        }
        
        .harmonic-controls-container::-webkit-scrollbar-thumb {
            background: linear-gradient(to bottom, #4d9fff, #2a5a8f);
            border-radius: 8px;
        }
        
        .harmonic-controls-container::-webkit-scrollbar-thumb:hover {
            background: linear-gradient(to bottom, #6babff, #3a6aaf);
        }
        
        .harmonic-item {
            margin-bottom: 12px;
            padding: 10px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 6px;
            width: 100%;
        }
        
        .harmonic-title {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
            font-weight: 500;
            font-size: 0.9rem;
            width: 100%;
        }
        
        .harmonic-sliders {
            display: flex;
            flex-direction: column;
            gap: 8px;
            width: 100%;
        }
        
        .harmonic-slider {
            display: flex;
            align-items: center;
            width: 100%;
        }
        
        .harmonic-slider label {
            width: 50px;
            font-size: 0.85rem;
        }
        
        .harmonic-slider input {
            flex: 1;
            margin: 0 8px;
        }
        
        .harmonic-slider span {
            width: 40px;
            text-align: right;
            font-size: 0.85rem;
        }
        
        footer {
            text-align: center;
            margin-top: 20px;
            padding: 15px;
            opacity: 0.7;
            font-size: 0.85rem;
            width: 100%;
        }
        
        .comparison-canvas {
            height: 200px;
        }
        
        /* 响应式设计优化 */
        @media (max-width: 768px) {
            .container {
                padding: 10px;
                max-width: 100vw;
            }
            
            .dashboard {
                grid-template-columns: 1fr;
                gap: 15px;
            }
            
            .control-panel {
                padding: 15px;
            }
            
            .visualization-area {
                gap: 15px;
            }
            
            .visualization-row {
                gap: 15px;
            }
            
            h1 {
                font-size: 1.8rem;
            }
            
            .subtitle {
                font-size: 1rem;
            }
        }
        
        @media (max-width: 480px) {
            .container {
                padding: 5px;
            }
            
            .control-panel {
                padding: 10px;
            }
            
            .panel {
                padding: 10px;
            }
            
            .control-group {
                padding: 10px;
            }
            
            h1 {
                font-size: 1.5rem;
            }
            
            .button-group {
                flex-direction: column;
            }
            
            .presets {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div>
        <header>
            <h1>傅里叶变换互动演示</h1>
            <p>探索复杂波形如何分解为简单正弦波的组合,直观理解频域分析</p>
        </header>
        
        <div>
            <div>
                <div>
                    <h3>波形参数</h3>
                    <div>
                        <label for="frequency">频率:</label>
                        <input type="range" id="frequency" min="1" max="10" step="0.1" value="2">
                        <span id="frequencyValue">2 Hz</span>
                    </div>
                    <div>
                        <label for="amplitude">振幅:</label>
                        <input type="range" id="amplitude" min="0.1" max="2" step="0.1" value="1">
                        <span id="amplitudeValue">1.0</span>
                    </div>
                    <div>
                        <label for="phase">相位:</label>
                        <input type="range" id="phase" min="0" max="6.28" step="0.01" value="0">
                        <span id="phaseValue">0 rad</span>
                    </div>
                </div>
                
                <div>
                    <h3>波形类型</h3>
                    <div>
                        <button id="sineBtn" class="btn active">正弦波</button>
                        <button id="squareBtn">方波</button>
                        <button id="sawtoothBtn">锯齿波</button>
                        <button id="triangleBtn">三角波</button>
                    </div>
                </div>
                
                <div>
                    <h3>傅里叶级数</h3>
                    <div>
                        <label for="harmonics">谐波数量:</label>
                        <input type="range" id="harmonics" min="1" max="20" step="1" value="5">
                        <span id="harmonicsValue">5</span>
                    </div>
                </div>
                
                <div>
                    <h3>预设信号</h3>
                    <div>
                        <button data-preset="simple">简单正弦波</button>
                        <button data-preset="complex">复杂波形</button>
                        <button data-preset="heartbeat">心跳信号</button>
                        <button data-preset="voice">语音模拟</button>
                    </div>
                </div>
                
                <div>
                    <h3>谐波分解与合成</h3>
                    <div>
                        <div>
                            <div class="tab active" data-tab="harmonics">谐波控制</div>
                            <div data-tab="synthesis">合成过程</div>
                            <div data-tab="comparison">合成对比</div>
                        </div>
                        
                        <div class="tab-content active" id="harmonics-tab">
                            <div id="harmonicsControls">
                                <!-- 动态生成的谐波控制项 -->
                            </div>
                        </div>
                        
                        <div id="synthesis-tab">
                            <div>
                                <label for="synthesisStep">显示步骤:</label>
                                <input type="range" id="synthesisStep" min="1" max="5" step="1" value="5">
                                <span id="synthesisStepValue">5</span>
                            </div>
                            <p style="font-size: 0.85rem; margin-top: 10px; opacity: 0.8;">
                                调整显示步骤,观察从基波开始逐步添加谐波形成最终波形的过程。
                            </p>
                        </div>
                        
                        <div id="comparison-tab">
                            <div>
                                <label for="comparisonHarmonics">谐波数量:</label>
                                <input type="range" id="comparisonHarmonics" min="1" max="20" step="1" value="5">
                                <span id="comparisonHarmonicsValue">5</span>
                            </div>
                            <p style="font-size: 0.85rem; margin-top: 10px; opacity: 0.8;">
                                调整谐波数量,观察合成波形如何逐渐接近原始波形。
                            </p>
                        </div>
                    </div>
                </div>
            </div>
            
            <div>
                <div>
                    <div>
                        <h2>时域信号</h2>
                        <div>
                            <canvas id="timeDomain"></canvas>
                        </div>
                    </div>
                    <div>
                        <h2>频域分析</h2>
                        <div>
                            <canvas id="frequencyDomain"></canvas>
                        </div>
                    </div>
                </div>
                <div>
                    <div>
                        <h2>谐波分解</h2>
                        <div>
                            <canvas id="harmonicsCanvas"></canvas>
                        </div>
                    </div>
                    <div>
                        <h2>波形合成</h2>
                        <div>
                            <canvas id="synthesisCanvas"></canvas>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <footer>
            <p>傅里叶变换互动演示 &copy; 2023 | 使用HTML5 Canvas和JavaScript实现</p>
        </footer>
    </div>

    <script>
        // 获取Canvas元素和上下文
        const timeCanvas = document.getElementById('timeDomain');
        const freqCanvas = document.getElementById('frequencyDomain');
        const harmonicsCanvas = document.getElementById('harmonicsCanvas');
        const synthesisCanvas = document.getElementById('synthesisCanvas');
        
        const timeCtx = timeCanvas.getContext('2d');
        const freqCtx = freqCanvas.getContext('2d');
        const harmonicsCtx = harmonicsCanvas.getContext('2d');
        const synthesisCtx = synthesisCanvas.getContext('2d');
        
        // 设置Canvas尺寸
        function resizeCanvases() {
            const containerWidth = document.querySelector('.canvas-container').clientWidth;
            const containerHeight = document.querySelector('.canvas-container').clientHeight;
            
            timeCanvas.width = containerWidth;
            timeCanvas.height = containerHeight;
            freqCanvas.width = containerWidth;
            freqCanvas.height = containerHeight;
            harmonicsCanvas.width = containerWidth;
            harmonicsCanvas.height = containerHeight;
            synthesisCanvas.width = containerWidth;
            synthesisCanvas.height = containerHeight;
            
            draw();
        }
        
        // 获取控制元素
        const frequencySlider = document.getElementById('frequency');
        const amplitudeSlider = document.getElementById('amplitude');
        const phaseSlider = document.getElementById('phase');
        const harmonicsSlider = document.getElementById('harmonics');
        const synthesisStepSlider = document.getElementById('synthesisStep');
        const comparisonHarmonicsSlider = document.getElementById('comparisonHarmonics');
        
        const frequencyValue = document.getElementById('frequencyValue');
        const amplitudeValue = document.getElementById('amplitudeValue');
        const phaseValue = document.getElementById('phaseValue');
        const harmonicsValue = document.getElementById('harmonicsValue');
        const synthesisStepValue = document.getElementById('synthesisStepValue');
        const comparisonHarmonicsValue = document.getElementById('comparisonHarmonicsValue');
        
        const sineBtn = document.getElementById('sineBtn');
        const squareBtn = document.getElementById('squareBtn');
        const sawtoothBtn = document.getElementById('sawtoothBtn');
        const triangleBtn = document.getElementById('triangleBtn');
        
        const presetButtons = document.querySelectorAll('.preset-btn');
        const tabs = document.querySelectorAll('.tab');
        const tabContents = document.querySelectorAll('.tab-content');
        
        // 初始化变量
        let waveType = 'sine';
        let frequency = parseFloat(frequencySlider.value);
        let amplitude = parseFloat(amplitudeSlider.value);
        let phase = parseFloat(phaseSlider.value);
        let harmonics = parseInt(harmonicsSlider.value);
        let synthesisStep = parseInt(synthesisStepSlider.value);
        let comparisonHarmonics = parseInt(comparisonHarmonicsSlider.value);
        
        // 谐波数据
        let harmonicAmplitudes = [];
        let harmonicPhases = [];
        
        // 初始化谐波数据
        function initHarmonicsData() {
            harmonicAmplitudes = [];
            harmonicPhases = [];
            
            for (let i = 0; i < harmonics; i++) {
                harmonicAmplitudes.push(0);
                harmonicPhases.push(0);
            }
            
            // 根据波形类型设置谐波振幅
            updateHarmonicsFromWaveType();
        }
        
        // 根据波形类型更新谐波数据
        function updateHarmonicsFromWaveType() {
            if (waveType === 'sine') {
                harmonicAmplitudes[0] = amplitude;
                for (let i = 1; i < harmonics; i++) {
                    harmonicAmplitudes[i] = 0;
                }
            } else if (waveType === 'square') {
                for (let i = 0; i < harmonics; i++) {
                    const n = i + 1;
                    if (n % 2 === 1) { // 奇数次谐波
                        harmonicAmplitudes[i] = (4 / (Math.PI * n)) * amplitude;
                    } else {
                        harmonicAmplitudes[i] = 0;
                    }
                }
            } else if (waveType === 'sawtooth') {
                for (let i = 0; i < harmonics; i++) {
                    const n = i + 1;
                    harmonicAmplitudes[i] = (2 / (Math.PI * n)) * amplitude;
                }
            } else if (waveType === 'triangle') {
                for (let i = 0; i < harmonics; i++) {
                    const n = i + 1;
                    if (n % 2 === 1) { // 奇数次谐波
                        harmonicAmplitudes[i] = (8 / (Math.PI * Math.PI * n * n)) * amplitude;
                    } else {
                        harmonicAmplitudes[i] = 0;
                    }
                }
            }
            
            // 更新谐波控制界面
            updateHarmonicsControls();
        }
        
        // 更新显示的值
        function updateValues() {
            frequencyValue.textContent = frequency.toFixed(1) + ' Hz';
            amplitudeValue.textContent = amplitude.toFixed(1);
            phaseValue.textContent = phase.toFixed(2) + ' rad';
            harmonicsValue.textContent = harmonics;
            synthesisStepValue.textContent = synthesisStep;
            comparisonHarmonicsValue.textContent = comparisonHarmonics;
        }
        
        // 绘制时域波形
        function drawTimeDomain() {
            const width = timeCanvas.width;
            const height = timeCanvas.height;
            const centerY = height / 2;
            
            // 清除画布
            timeCtx.clearRect(0, 0, width, height);
            
            // 绘制网格
            timeCtx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
            timeCtx.lineWidth = 1;
            timeCtx.beginPath();
            
            // 水平网格线
            for (let y = 0; y < height; y += height / 4) {
                timeCtx.moveTo(0, y);
                timeCtx.lineTo(width, y);
            }
            
            // 垂直网格线
            for (let x = 0; x < width; x += width / 8) {
                timeCtx.moveTo(x, 0);
                timeCtx.lineTo(x, height);
            }
            
            timeCtx.stroke();
            
            // 绘制坐标轴
            timeCtx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
            timeCtx.lineWidth = 2;
            timeCtx.beginPath();
            timeCtx.moveTo(0, centerY);
            timeCtx.lineTo(width, centerY);
            timeCtx.stroke();
            
            // 绘制波形
            timeCtx.strokeStyle = '#4d9fff';
            timeCtx.lineWidth = 2;
            timeCtx.beginPath();
            
            const samples = 300;
            
            for (let i = 0; i <= samples; i++) {
                const x = (i / samples) * width;
                const t = (i / samples) * Math.PI * 4; // 时间范围
                
                let y = centerY;
                
                if (waveType === 'sine') {
                    y += amplitude * Math.sin(frequency * t + phase) * (height / 4);
                } else if (waveType === 'square') {
                    y += amplitude * Math.sign(Math.sin(frequency * t + phase)) * (height / 4);
                } else if (waveType === 'sawtooth') {
                    y += amplitude * (2 * ((frequency * t + phase) / (2 * Math.PI) - Math.floor(0.5 + (frequency * t + phase) / (2 * Math.PI)))) * (height / 4);
                } else if (waveType === 'triangle') {
                    const period = 2 * Math.PI / frequency;
                    const normalizedTime = ((frequency * t + phase) % period) / period;
                    y += amplitude * (2 * Math.abs(2 * normalizedTime - 1) - 1) * (height / 4);
                }
                
                if (i === 0) {
                    timeCtx.moveTo(x, y);
                } else {
                    timeCtx.lineTo(x, y);
                }
            }
            
            timeCtx.stroke();
            
            // 绘制标题
            timeCtx.fillStyle = 'white';
            timeCtx.font = '14px Arial';
            timeCtx.fillText('时域信号', 10, 20);
        }
        
        // 绘制频域分析
        function drawFrequencyDomain() {
            const width = freqCanvas.width;
            const height = freqCanvas.height;
            
            // 清除画布
            freqCtx.clearRect(0, 0, width, height);
            
            // 绘制网格
            freqCtx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
            freqCtx.lineWidth = 1;
            freqCtx.beginPath();
            
            // 水平网格线
            for (let y = 0; y < height; y += height / 4) {
                freqCtx.moveTo(0, y);
                freqCtx.lineTo(width, y);
            }
            
            // 垂直网格线
            for (let x = 0; x < width; x += width / 8) {
                freqCtx.moveTo(x, 0);
                freqCtx.lineTo(x, height);
            }
            
            freqCtx.stroke();
            
            // 绘制坐标轴
            freqCtx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
            freqCtx.lineWidth = 2;
            freqCtx.beginPath();
            freqCtx.moveTo(0, height);
            freqCtx.lineTo(width, height);
            freqCtx.stroke();
            
            // 绘制频谱
            const barWidth = width / (harmonics + 1);
            
            for (let i = 0; i < harmonics; i++) {
                const x = (i + 1) * barWidth;
                let barHeight;
                
                if (waveType === 'sine') {
                    // 正弦波只有基频
                    barHeight = i === 0 ? amplitude * (height / 2) : 0;
                } else if (waveType === 'square') {
                    // 方波的傅里叶级数: 4/π * (1/n) * sin(nωt) for odd n
                    if (i % 2 === 0) { // 奇数次谐波
                        const n = i + 1;
                        barHeight = (4 / (Math.PI * n)) * amplitude * (height / 2);
                    } else {
                        barHeight = 0;
                    }
                } else if (waveType === 'sawtooth') {
                    // 锯齿波的傅里叶级数: 2/π * (-1)^(n+1) * (1/n) * sin(nωt)
                    const n = i + 1;
                    barHeight = (2 / (Math.PI * n)) * amplitude * (height / 2);
                } else if (waveType === 'triangle') {
                    // 三角波的傅里叶级数: 8/π² * (1/n²) * sin(nωt) for odd n
                    if (i % 2 === 0) { // 奇数次谐波
                        const n = i + 1;
                        barHeight = (8 / (Math.PI * Math.PI * n * n)) * amplitude * (height / 2);
                    } else {
                        barHeight = 0;
                    }
                }
                
                // 绘制频谱柱
                freqCtx.fillStyle = '#2a5a8f';
                freqCtx.fillRect(x - barWidth/2, height - barHeight, barWidth, barHeight);
                
                // 绘制边框
                freqCtx.strokeStyle = '#4d9fff';
                freqCtx.lineWidth = 1;
                freqCtx.strokeRect(x - barWidth/2, height - barHeight, barWidth, barHeight);
                
                // 绘制频率标签
                if (barHeight > 15) {
                    freqCtx.fillStyle = 'white';
                    freqCtx.font = '10px Arial';
                    freqCtx.textAlign = 'center';
                    freqCtx.fillText(`${(i + 1) * frequency} Hz`, x, height - 8);
                }
            }
            
            // 绘制标题
            freqCtx.fillStyle = 'white';
            freqCtx.font = '14px Arial';
            freqCtx.textAlign = 'left';
            freqCtx.fillText('频域分析', 10, 20);
        }
        
        // 绘制谐波分解
        function drawHarmonics() {
            const width = harmonicsCanvas.width;
            const height = harmonicsCanvas.height;
            const centerY = height / 2;
            const harmonicHeight = height / (harmonics + 1);
            
            // 清除画布
            harmonicsCtx.clearRect(0, 0, width, height);
            
            // 绘制每个谐波
            for (let i = 0; i < harmonics; i++) {
                const yOffset = (i + 1) * harmonicHeight;
                const harmonicAmplitude = harmonicAmplitudes[i];
                const harmonicPhase = harmonicPhases[i];
                
                // 绘制谐波标签
                harmonicsCtx.fillStyle = 'white';
                harmonicsCtx.font = '10px Arial';
                harmonicsCtx.fillText(`谐波 ${i+1} (${(i+1)*frequency} Hz)`, 10, yOffset - harmonicHeight/2 + 5);
                
                // 绘制谐波波形
                harmonicsCtx.strokeStyle = `hsl(${210 + i * 30}, 70%, 60%)`;
                harmonicsCtx.lineWidth = 1.5;
                harmonicsCtx.beginPath();
                
                const samples = 150;
                
                for (let j = 0; j <= samples; j++) {
                    const x = (j / samples) * width;
                    const t = (j / samples) * Math.PI * 4;
                    
                    const y = yOffset + harmonicAmplitude * Math.sin((i+1) * frequency * t + harmonicPhase) * (harmonicHeight / 3);
                    
                    if (j === 0) {
                        harmonicsCtx.moveTo(x, y);
                    } else {
                        harmonicsCtx.lineTo(x, y);
                    }
                }
                
                harmonicsCtx.stroke();
            }
            
            // 绘制标题
            harmonicsCtx.fillStyle = 'white';
            harmonicsCtx.font = '14px Arial';
            harmonicsCtx.fillText('谐波分解', 10, 20);
        }
        
        // 绘制波形合成
        function drawSynthesis() {
            const width = synthesisCanvas.width;
            const height = synthesisCanvas.height;
            const centerY = height / 2;
            
            // 清除画布
            synthesisCtx.clearRect(0, 0, width, height);
            
            // 绘制网格
            synthesisCtx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
            synthesisCtx.lineWidth = 1;
            synthesisCtx.beginPath();
            
            // 水平网格线
            for (let y = 0; y < height; y += height / 4) {
                synthesisCtx.moveTo(0, y);
                synthesisCtx.lineTo(width, y);
            }
            
            // 垂直网格线
            for (let x = 0; x < width; x += width / 8) {
                synthesisCtx.moveTo(x, 0);
                synthesisCtx.lineTo(x, height);
            }
            
            synthesisCtx.stroke();
            
            // 绘制坐标轴
            synthesisCtx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
            synthesisCtx.lineWidth = 2;
            synthesisCtx.beginPath();
            synthesisCtx.moveTo(0, centerY);
            synthesisCtx.lineTo(width, centerY);
            synthesisCtx.stroke();
            
            // 绘制合成过程
            const samples = 300;
            
            // 绘制每个步骤的合成结果
            for (let step = 1; step <= synthesisStep; step++) {
                synthesisCtx.strokeStyle = `hsla(${200 + step * 10}, 70%, 60%, ${0.3 + 0.7 * step / synthesisStep})`;
                synthesisCtx.lineWidth = 1 + step / synthesisStep;
                synthesisCtx.beginPath();
                
                for (let i = 0; i <= samples; i++) {
                    const x = (i / samples) * width;
                    const t = (i / samples) * Math.PI * 4;
                    
                    let y = centerY;
                    
                    // 累加前step个谐波
                    for (let h = 0; h < step; h++) {
                        y += harmonicAmplitudes[h] * Math.sin((h+1) * frequency * t + harmonicPhases[h]) * (height / 4);
                    }
                    
                    if (i === 0) {
                        synthesisCtx.moveTo(x, y);
                    } else {
                        synthesisCtx.lineTo(x, y);
                    }
                }
                
                synthesisCtx.stroke();
            }
            
            // 绘制标题
            synthesisCtx.fillStyle = 'white';
            synthesisCtx.font = '14px Arial';
            synthesisCtx.fillText('波形合成过程', 10, 20);
        }
        
        // 更新谐波控制界面
        function updateHarmonicsControls() {
            const harmonicsControls = document.getElementById('harmonicsControls');
            harmonicsControls.innerHTML = '';
            
            for (let i = 0; i < harmonics; i++) {
                const harmonicItem = document.createElement('div');
                harmonicItem.className = 'harmonic-item';
                
                const harmonicTitle = document.createElement('div');
                harmonicTitle.className = 'harmonic-title';
                harmonicTitle.innerHTML = `谐波 ${i+1} <span>${(i+1)*frequency} Hz</span>`;
                
                const harmonicSliders = document.createElement('div');
                harmonicSliders.className = 'harmonic-sliders';
                
                // 振幅控制
                const amplitudeControl = document.createElement('div');
                amplitudeControl.className = 'harmonic-slider';
                amplitudeControl.innerHTML = `
                    <label>振幅:</label>
                    <input type="range" data-index="${i}" min="0" max="1" step="0.01" value="${harmonicAmplitudes[i]}">
                    <span>${harmonicAmplitudes[i].toFixed(2)}</span>
                `;
                
                // 相位控制
                const phaseControl = document.createElement('div');
                phaseControl.className = 'harmonic-slider';
                phaseControl.innerHTML = `
                    <label>相位:</label>
                    <input type="range" data-index="${i}" min="0" max="6.28" step="0.01" value="${harmonicPhases[i]}">
                    <span>${harmonicPhases[i].toFixed(2)}</span>
                `;
                
                harmonicSliders.appendChild(amplitudeControl);
                harmonicSliders.appendChild(phaseControl);
                
                harmonicItem.appendChild(harmonicTitle);
                harmonicItem.appendChild(harmonicSliders);
                
                harmonicsControls.appendChild(harmonicItem);
            }
            
            // 添加事件监听器
            document.querySelectorAll('.harmonic-amplitude').forEach(slider => {
                slider.addEventListener('input', function() {
                    const index = parseInt(this.getAttribute('data-index'));
                    harmonicAmplitudes[index] = parseFloat(this.value);
                    this.parentElement.querySelector('.harmonic-amplitude-value').textContent = this.value;
                    draw();
                });
            });
            
            document.querySelectorAll('.harmonic-phase').forEach(slider => {
                slider.addEventListener('input', function() {
                    const index = parseInt(this.getAttribute('data-index'));
                    harmonicPhases[index] = parseFloat(this.value);
                    this.parentElement.querySelector('.harmonic-phase-value').textContent = this.value;
                    draw();
                });
            });
        }
        
        // 绘制函数
        function draw() {
            drawTimeDomain();
            drawFrequencyDomain();
            drawHarmonics();
            drawSynthesis();
        }
        
        // 事件监听器
        frequencySlider.addEventListener('input', function() {
            frequency = parseFloat(this.value);
            updateValues();
            updateHarmonicsFromWaveType();
            draw();
        });
        
        amplitudeSlider.addEventListener('input', function() {
            amplitude = parseFloat(this.value);
            updateValues();
            updateHarmonicsFromWaveType();
            draw();
        });
        
        phaseSlider.addEventListener('input', function() {
            phase = parseFloat(this.value);
            updateValues();
            draw();
        });
        
        harmonicsSlider.addEventListener('input', function() {
            harmonics = parseInt(this.value);
            updateValues();
            initHarmonicsData();
            draw();
        });
        
        synthesisStepSlider.addEventListener('input', function() {
            synthesisStep = parseInt(this.value);
            updateValues();
            draw();
        });
        
        comparisonHarmonicsSlider.addEventListener('input', function() {
            comparisonHarmonics = parseInt(this.value);
            updateValues();
            draw();
        });
        
        // 波形类型按钮事件
        sineBtn.addEventListener('click', function() {
            waveType = 'sine';
            setActiveWaveButton(this);
            updateHarmonicsFromWaveType();
            draw();
        });
        
        squareBtn.addEventListener('click', function() {
            waveType = 'square';
            setActiveWaveButton(this);
            updateHarmonicsFromWaveType();
            draw();
        });
        
        sawtoothBtn.addEventListener('click', function() {
            waveType = 'sawtooth';
            setActiveWaveButton(this);
            updateHarmonicsFromWaveType();
            draw();
        });
        
        triangleBtn.addEventListener('click', function() {
            waveType = 'triangle';
            setActiveWaveButton(this);
            updateHarmonicsFromWaveType();
            draw();
        });
        
        function setActiveWaveButton(button) {
            document.querySelectorAll('.btn').forEach(btn => {
                btn.classList.remove('active');
            });
            button.classList.add('active');
        }
        
        // 预设按钮事件
        presetButtons.forEach(button => {
            button.addEventListener('click', function() {
                const preset = this.getAttribute('data-preset');
                
                switch(preset) {
                    case 'simple':
                        frequency = 2;
                        amplitude = 1;
                        phase = 0;
                        waveType = 'sine';
                        setActiveWaveButton(sineBtn);
                        break;
                    case 'complex':
                        frequency = 1;
                        amplitude = 1.5;
                        phase = 0.5;
                        waveType = 'square';
                        setActiveWaveButton(squareBtn);
                        break;
                    case 'heartbeat':
                        frequency = 1.2;
                        amplitude = 1.8;
                        phase = 0;
                        waveType = 'sawtooth';
                        setActiveWaveButton(sawtoothBtn);
                        break;
                    case 'voice':
                        frequency = 3;
                        amplitude = 0.8;
                        phase = 1.2;
                        waveType = 'sine';
                        setActiveWaveButton(sineBtn);
                        break;
                }
                
                frequencySlider.value = frequency;
                amplitudeSlider.value = amplitude;
                phaseSlider.value = phase;
                
                updateValues();
                updateHarmonicsFromWaveType();
                draw();
            });
        });
        
        // 标签切换事件
        tabs.forEach(tab => {
            tab.addEventListener('click', function() {
                const tabId = this.getAttribute('data-tab');
                
                // 更新活动标签
                tabs.forEach(t => t.classList.remove('active'));
                this.classList.add('active');
                
                // 更新活动内容
                tabContents.forEach(content => content.classList.remove('active'));
                document.getElementById(`${tabId}-tab`).classList.add('active');
            });
        });
        
        // 初始化
        window.addEventListener('load', function() {
            resizeCanvases();
            updateValues();
            initHarmonicsData();
        });
        
        window.addEventListener('resize', resizeCanvases);
    </script>
</body>
</html>

傅里叶变换互动演示HTML实现_deepseek.html

浏览129
返回
目录
返回
首页
电磁波调制原理演示 幼儿时钟学习应用设计