我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过js来实现以下这个效果,下面是简单的效果图
首先我们需要有一个绘制音频的函数
function draw() {
// 请求下一帧动画
animationId = requestAnimationFrame(draw);
// 获取音频频谱数据
analyser.getByteFrequencyData(dataArray);
// 清空画布
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 计算每个频谱条的宽度
var barWidth = (canvas.width / bufferLength) * 2.5;
var barHeight;
var x = 0;
// 遍历频谱数据数组,绘制频谱条
for (var i = 0; i < bufferLength; i++) {
// 计算频谱条的高度
barHeight = dataArray[i] / 255 * canvas.height;
// 根据频谱条的索引值计算颜色(彩虹色)
var hue = i / bufferLength * 360;
ctx.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
// 绘制频谱条矩形
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
// 更新下一个频谱条的起始位置
x += barWidth + 1;
}
}
这个函数是用于绘制频谱图的核心部分。它使用requestAnimationFrame()
方法来请求下一帧动画,并将自身作为回调函数。这样可以不断更新频谱图。
在函数内部,analyser.getByteFrequencyData(dataArray)
用于获取当前的音频频谱数据,将数据存储在dataArray
数组中。
然后,画布被清空,使用黑色填充整个画布。
接下来,通过计算每个频谱条的宽度,以及根据频谱数据计算每个频谱条的高度,来确定频谱条的绘制参数。
然后,使用彩虹色调的渐变来设置频谱条的颜色,颜色的HSL值根据频谱条的索引值计算。
最后,在画布上绘制每个频谱条的矩形,每个矩形之间留有间距。
通过不断调用requestAnimationFrame()
方法并在每一帧更新频谱图,可以实现连续的动画效果。
接下来我们需要分析一下音频
document.getElementById('playButton').addEventListener('click', function() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
audioElement = document.createElement('audio');
audioElement.src = '1.mp3';
audioElement.controls = true;
audioElement.style.display = 'none';
document.body.appendChild(audioElement);
var source = audioContext.createMediaElementSource(audioElement);
source.connect(analyser);
analyser.connect(audioContext.destination);
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
}
audioElement.play();
draw();
});
document.getElementById('pauseButton').addEventListener('click', function() {
audioElement.pause();
cancelAnimationFrame(animationId);
});
当用户点击"播放"按钮时,创建一个新的AudioContext
对象用于处理音频,创建一个AnalyserNode
对象用于分析音频频谱。然后创建一个audio
元素并将其设置为要播放的音频文件。将audio
元素连接到AnalyserNode
,将AnalyserNode
连接到AudioContext
的目标(通常是扬声器)。设置频率分析器的参数,包括FFT大小。
当用户点击"播放"按钮时,音频开始播放,并且在draw()
函数中的requestAnimationFrame(draw)
中调用的循环中,更新频谱数据并绘制频谱图。首先,使用analyser.getByteFrequencyData(dataArray)
获取音频频谱数据。然后,通过遍历数据数组,计算每个频谱条的高度,并根据频谱条的位置在画布上绘制矩形。颜色根据频谱条的索引值计算,使得频谱图呈现彩虹色的效果。
当用户点击"暂停"按钮时,音频暂停播放,并调用cancelAnimationFrame(animationId)
来停止绘制频谱图。
请确保将audioElement.src
中的路径替换为你要播放的实际音频文件的路径。
当然修改draw函数可以得到其他的音频图,比如波形图
具体的draw代码如下
这个函数主要用于绘制音频的时域波形图。它也使用了requestAnimationFrame()
方法来请求下一帧动画,并将自身作为回调函数。
在函数内部,analyser.getByteTimeDomainData(dataArray)
用于获取当前的音频时域数据,将数据存储在dataArray
数组中。
然后,画布被清空,并将背景颜色设置为lime。
接下来,设置线条的宽度和颜色。
然后,开始绘制路径。
通过计算每个数据片段的宽度,以及根据时域数据计算每个点的纵坐标,确定波形图的绘制参数。
然后,根据波形点的位置,使用moveTo()
方法将绘制路径移动到第一个点的位置,并使用lineTo()
方法连接到下一个点的位置。这样就形成了一条完整的波形路径。
在遍历完所有的数据点后,使用lineTo()
方法将最后一个点连接到画布的右侧中点,以形成闭合路径。
最后,使用stroke()
方法绘制路径。
通过不断调用requestAnimationFrame()
方法并在每一帧更新波形图,可以实现连续的动画效果。
function draw() {
// 请求下一帧动画
animationId = requestAnimationFrame(draw);
// 获取音频时域数据
analyser.getByteTimeDomainData(dataArray);
// 清空画布并设置背景颜色为lime
ctx.fillStyle = 'lime';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 设置线条宽度和颜色
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
// 开始绘制路径
ctx.beginPath();
// 计算每个数据片段的宽度
var sliceWidth = canvas.width * 1.0 / bufferLength;
var x = 0;
// 遍历时域数据数组,绘制波形
for (var i = 0; i < bufferLength; i++) {
// 将数据归一化到范围[-1, 1]
var v = dataArray[i] / 128.0;
// 计算波形点的纵坐标
var y = v * canvas.height / 2;
if (i === 0) {
// 移动到第一个点的位置
ctx.moveTo(x, y);
} else {
// 连接到下一个点的位置
ctx.lineTo(x, y);
}
// 更新下一个点的横坐标
x += sliceWidth;
}
// 连接最后一个点到画布右侧中点,形成闭合路径
ctx.lineTo(canvas.width, canvas.height / 2);
// 绘制路径
ctx.stroke();
}