在 Web 开发中,傅里叶变换(Fourier Transform, FT)是信号处理的核心,常用于音频可视化、图像压缩等领域。传统解释依赖公式,但交互式可视化能直观展示其本质:任何周期信号可分解为不同频率、幅度和相位的旋转圆圈(epicycles)。本文聚焦 HTML5 Canvas 实现实时 FT 可视化器,演示信号分解、采样混叠(aliasing)、频率变形(morphing)、FFT 卷积及交互 chirp 合成,提供可落地代码框架、参数阈值与性能监控策略。
核心原理与 Canvas 实现框架
傅里叶变换将时域信号 x (t) 转换为频域系数 c_k = ∫ x (t) * exp (-i2πkt/T) dt,其中每个 c_k 是复数,表征第 k 阶频率的幅度 | c_k |、相位 arg (c_k)。可视化时,从中心出发,按频率升序绘制圆圈:圆半径 r_k = |c_k|,角速度 ω_k = 2πk/T,相位 φ_k = arg (c_k)。箭头链从原点连至末端,形成轨迹投影至波形。
使用离散傅里叶变换(DFT)处理采样点。定义 Complex 类处理复数运算:
class Complex {
constructor(re, im) { this.re = re; this.im = im; }
mul(other) {
return new Complex(this.re * other.re - this.im * other.im,
this.re * other.im + this.im * other.re);
}
add(other) {
return new Complex(this.re + other.re, this.im + other.im);
}
mag() { return Math.sqrt(this.re**2 + this.im**2); }
arg() { return Math.atan2(this.im, this.re); }
}
DFT 函数计算系数:
function dft(x, N) {
const coeffs = [];
for (let k = 0; k < N; k++) {
let sum = new Complex(0, 0);
for (let n = 0; n < N; n++) {
const angle = -2 * Math.PI * k * n / N;
sum.add(x[n].mul(new Complex(Math.cos(angle), Math.sin(angle))));
}
coeffs.push(sum);
}
return coeffs;
}
动画循环用 requestAnimationFrame,canvas 尺寸 800x600,中心 (200, 300):
function animate(time) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let x = 200, y = 300;
ctx.beginPath(); ctx.moveTo(x, y);
coeffs.forEach((c, k) => {
const r = c.mag() * scale;
const phase = c.arg() + time * k * speed;
const dx = r * Math.cos(phase);
const dy = r * Math.sin(phase);
// 绘制圆、箭头
ctx.arc(x, y, r, 0, 2*Math.PI);
ctx.lineTo(x + dx, y + dy);
x += dx; y += dy;
});
ctx.stroke(); // 轨迹
requestAnimationFrame(animate);
}
初始信号:方波或 chirp。采样点 N=256,scale=50 避免溢出。证据:如 BetterExplained 文章所述,此链式 epicycles 合成精确复原信号。
采样混叠效果动画
采样定理:采样率 fs ≥ 2*f_max 避免混叠。演示高频 sin (ωt),ω>fs/2 时折叠为低频伪影。
参数:fs=100Hz,f_sig=40~70Hz 滑块控制。生成离散点 x [n] = sin (2πf_sign/fs),DFT 后仅低频分量主导。动画叠加采样点与连续波,fs 降至 < 2*f_sig 时轨迹扭曲。
落地清单:
- 采样间隔 dt=1/fs=0.01s
- Nyquist 阈值:f_max=fs/2,红色标记超 Nyquist 频率
- 动画步长:每帧采样 10 点,观察 aliasing 渐现
监控:若 N>512,DFT O (N^2) 耗时 > 16ms(60FPS),降 N 或用 FFT。
频率变形与实时交互
morphing:两个信号 coeffs1、coeffs2,lerp α∈[0,1]:c_k (α)=(1-α)c1_k + αc2_k。滑块控制 α,实时重绘 epicycles。
示例:从 sin 波变形至方波,低频主导形状,高频锐化边缘。交互:鼠标拖拽轨迹点,逆 DFT 更新 coeffs。
参数:lerp 步长 0.01,maxHarm=20(仅前 20 谐波,避免噪声)。
FFT 卷积与 Chirp 合成
卷积:时域 xy ↔ 频域 X . Y。实现简单 FFT(Cooley-Tukey)或 dsp.js 库。
Chirp 合成:线性调频 f (t)=f0 + (f1-f0)t/T,x(t)=sin(2π∫f(τ)dτ)=sin(2π(f0*t + (f1-f0)*t^2/(2T)))。滑块 f0=1Hz、f1=20Hz、T=5s 生成 chirp coeffs。
卷积演示:chirp 与脉冲卷积产生扫频响应,频谱乘法可视化:幅度谱 | X | .* | Y |,相位相加。
工程阈值:
- FFT 大小:256~1024,零填充对齐
- 窗函数:Hann 窗减泄漏,overlap=50%
- 回滚:若变形畸变 > 10%,α=0 重置
性能优化与监控要点
- FPS:requestAnimationFrame 自适应,>60 丢帧用 setTimeout (16ms)
- Offload:Web Worker 计算 DFT/FFT,主线程仅绘制
- 内存:复用 coeffs 数组,避免 GC
- 兼容:Canvas2D fallback WebGL particles 高频
- 阈值:drawTime>16ms 降 maxHarm=10;内存 > 100MB 暂停动画
实际部署:~200 行 JS,<50KB gz,Chrome/Firefox 60FPS@N=128。
资料来源:
此可视化器不止教育工具,还适用于 Web 音频实验、DSP 教学原型。完整 demo 见 GitHub fork 自 jezzamon。
(字数:1256)