背景
最近正在做一个开源的项目–pear-rec,pear-rec 是一个跨平台的截图、录屏、录音、录像软件。截图功能上篇文章已经讲过了,现在正在做录音功能。其实录音功能不难,但是我想做的美观,所以我想实现一个波形图。来展示正在录音。
工具
实现
原理逻辑
页面加载一段音频(audio),然后通过AudioContext
对象解析音频的数据,然后转换成Uint8Array
的数组,最后通过canvas
实时画出数组的数据。就可以完成了!其实并不难,接下来我们就来实现吧!
准备工作
因为我们是简单实现一个demo
,我选择了jquery
和echarts
来辅助我来开发,其实也可以不用。
<script src="https://fastly.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>
<script src="https://fastly.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
静态页面
css 代码
html,
body {
margin: 0;
overflow: hidden;
font-family: sans-serif;
background: #13091B;
height: 100%;
}
body {
background: url(https://p1.music.126.net/gAmIGjlWnYXE_0O8LFp5-w==/109951164382001054.jpg) no-repeat;
background-size: cover;
}
#canvas {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
audio {
visibility: hidden;
}
#pause-btn,
#play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #13091B;
background: #007A99;
display: block;
width: 150px;
height: 45px;
line-height: 45px;
font-size: 18px;
cursor: pointer;
border-radius: 4px;
letter-spacing: 0.1em;
z-index: 1;
text-align: center;
text-decoration: none;
}
#play-btn {
transform: translate(-105%, -50%);
}
#pause-btn {
transform: translate(5%, -50%);
}
html代码
<div id="content">
<div id="main" style="width: 100%;height:100vh;"></div>
<canvas id="canvas"></canvas>
<audio id="audio" controls
src="//m8.music.126.net/21180815163607/04976f67866d4b4d11575ab418904467/ymusic/515a/5508/520b/f0cf47930abbbb0562c9ea61707c4c0b.mp3?infoId=92001"
crossOrigin="anonymous"></audio>
<a href="javascript:;" id="play-btn">PLAY</a>
<a href="javascript:;" id="pause-btn">pause </a>
</div>
实现效果
音频解析
首先我们页面上有两个按钮,一个是播放,另一个是暂停,点击播放时候,用AudioContext
解析当时的音频数据,并转换成Uint8Array
数组。
1.获取dom
var btn = document.getElementById("play-btn");
var pause = document.getElementById("pause-btn");
var audio = document.getElementById("audio");
2. 播放按钮绑定点击事件
btn.onclick = function () {
btn.style.display = "none";
btn.style.display = "block";
audio.play();
onLoadAudio();
};
3. 暂停按钮绑定点击事件
pause.onclick = function () {
pause.style.display = "none";
btn.style.display = "block";
audio.pause();
};
4. AudioContext解析音频
AudioContext
接口表示由音频模块连接而成的音频处理图,每个模块对应一个AudioNode
。AudioContext
可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建AudioContext
对象,因为一切都发生在这个环境之中。
function onLoadAudio() {
var context = new (window.AudioContext || window.webkitAudioContext)();
var analyser = context.createAnalyser();
analyser.fftSize = 512;
var source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = WIDTH / bufferLength * 1;
var barHeight;
}
方法一(echarts)
function initChart(bufferLength, barHeightList) {
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
var xAxisData = [];
option = {
graphic: {
elements: [
{
type: 'group',
bottom: 0,
children: new Array(bufferLength).fill(0).map((val, i) =>
({
type: 'rect',
x: i * 15,
shape: {
width: 10,
height: -barHeightList[i]
},
style: {
fill: `rgb(${barHeightList[i] + 25 * (i / bufferLength)}, ${250 * (i / bufferLength)}, ${50})`
},
})
)
}
]
}
};
option && myChart.setOption(option);
}
方法二(canvas)推荐
有的同学会说我也可以不用echarts
直接用canvas
来实现,其实也不难就是用fillRect
方法来画柱状图。
如果有不太懂的同学可以去查看文档。
语法:
context.fillRect(x,y,width,height);
参数 | 描述 |
---|---|
x | 矩形左上角的 x 坐标。 |
y | 矩形左上角的 y 坐标。 |
width | 矩形的宽度,以像素计。 |
height | 矩形的高度,以像素计。 |
具体代码:
function renderFrame() {
requestAnimationFrame(renderFrame);
analyser.getByteFrequencyData(dataArray);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
var barHeightList = [];
var xAxisList = [];
for (var i = 0, x = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
barHeightList.push(barHeight);
var r = barHeight + 25 * (i / bufferLength);
var g = 250 * (i / bufferLength);
var b = 50;
ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 2;
}
}
总结
- 使用
AudioContext
创建analyser
,将音频流stream
连接到analyser
- 设置
analyser
的fft
参数,以此获取音频数据 - 通过递归调用
requestAnimationFrame
来实现动画效果 - 使用
Canvas
或echarts
来绘制条形图以及小浮块,将这绘制操作放在requestAnimationFrame
的回调中,从而展示动态的频谱图。
如果你看完还是做不出来,可以在 pear-rec 里直接看源码实现。