前一阵子有个网友通过之前写的一篇音频可视化的文章找到我,让我帮忙给他的音乐网站加个播放效果,自己正好没啥事做欣然答应。
由于疏于学习,基础掌握的不好,我一直以为音频可视化是通过Web audio API实现的,脑子不会转弯,总觉得只能通过这种方法载入音频:
var audio = new Audio("hello.mp4");
然后打一套API的组合拳来读取音频数据:
AudioContext = AudioContext || webkitAudioContext;
context = new AudioContext;
//加载媒体
//创建节点
source = context.createMediaElementSource(audio);
analyser = context.createAnalyser();
//连接:source → analyser → destination
source.connect(analyser);
analyser.connect(context.destination);
直到看到另一个使用Jplayer的网站才恍然大悟,完全可以获取页面上已有的audio
控件来载入音频:
var audio= document.getElementById('audio');
source = context.createMediaElementSource(audio);
下面以JYMUSIC这套程序为例,写一下大概实现原理。
先在\resources\web\default\home
目录的player_default.html
里面加一个canvas
层:
<canvas id="canvas" width="500" height="350">canvas>
然后找到\resources\web\default\assets\js
目录下面的player.js
,加入如下代码:
/*音频可视化*/
function initVisualizer(){
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext||window.oAudioContext;
var player = document.getElementById('jp_audio_0');
try {
var audioContext = new window.AudioContext();
} catch (e) {
alert('你的浏览器不支持AudioContext。错误:'+e);
}
playerAnalyser = audioContext.createAnalyser();
playerSource = audioContext.createMediaElementSource(player);
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
capStyle = 'rgba(0,255,0,.6)';//频谱颜色
CANVAS_WIDTH = canvas.clientWidth;
CANVAS_HEIGHT = canvas.clientHeight;
cheight = canvas.height - 2;
capYPositionArray = [];
//以上为变量
playerAnalyser.connect(audioContext.destination);//声音连接到扬声器
playerSource.connect(playerAnalyser);//截取音频信号
playerFrequencyData = new Uint8Array(playerAnalyser.frequencyBinCount);//得到音频能量值
var gradient,topXy,capHeight=2;
var meterWidth = 8, //能量条的宽度
gap = 4, //能量条间的间距
steplength = meterWidth + gap,
meterNum = CANVAS_WIDTH / steplength; //计算当前画布上能画多少条
process();
function process() {
playerAnalyser.getByteFrequencyData(playerFrequencyData);//得到音频能量值
playerTimeDomainData = new Uint8Array(playerAnalyser.fftSize);
requestAnimationFrame(process)
context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
var step = Math.floor((playerFrequencyData.length-200)/ meterNum); //计算从analyser中的采样步
context.beginPath();
for (var i = 0; i < meterNum; i++) {
var value = playerFrequencyData[i * step];
topXy = cheight - value;
if(topXy==cheight){
continue;
}
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(topXy); //初始化保存帽头位置的数组,将第一个画面的数据压入其中
};
context.fillStyle = "#fff";
//定义一个渐变样式用于画图
gradient = context.createLinearGradient(i * steplength , topXy+capHeight, i * steplength, cheight);
gradient.addColorStop(0, 'rgba(255,0,0,.7)');
gradient.addColorStop(1, 'rgba(61,247,0,.5)');
context.fillStyle=gradient;
context.fillRect(i * 12 , topXy+capHeight, meterWidth, cheight);
}
context.closePath();
}
}
最终自己添加一下CSS就可以了,我用的是:
#canvas {
position: absolute;
bottom: 51px;
left: 0;
right: 0;
margin: auto;
transform: translateX(50%);
box-reflect: left -2px;
-moz-box-reflect: left -2px;
-webkit-box-reflect: left 2px;
-ms-box-reflect: left 2px;
-o-box-reflect: left 2px;
-moz-transform: rotateY(180deg);
-o-transform: rotateY(180deg);
pointer-events: none;
}
至于代码逻辑,可以看我前一篇文章的分析。
帮这位朋友加上效果还得到了一大笔打赏,实在是受宠若惊,这也是第一次通过博客内容收到打赏,坚定了我分享一些觉得还有用的东西的决心。