参考原文:JS | Web Audio API (下) 我的音乐浪 - 简书
需求:根据所播放音乐实现律动
效果:
音乐浪
wxml
<div class="visualize_wrap">
<canvas type='2d' id="audio_canvas" canvas-id="audio_canvas" style="margin: auto;"></canvas>
</div>
js
onShow() {
this.audioCtx = wx.createWebAudioContext()
this.music = "https://lk-mirror.oss-cn-beijing.aliyuncs.com/f698da27eb4946fd82b3707a0d63a4a5.mp3"
this.loadMusic(this.music)
},
// 绘图
drawAudio(analyser) {
let dpr = wx.getSystemInfoSync().pixelRatio
let c_width = this.res[0].width,
c_height = this.res[0].height,
canvas = this.res[0].node,
ctx = canvas.getContext('2d')
canvas.width = this.res[0].width * dpr
canvas.height = this.res[0].height * dpr
ctx.scale(dpr, 0.9)
// 条形的参数
let bar_width = 3,
bar_gap = 2,
bar_part = bar_width + bar_gap,
bar_num = Math.round(c_width / bar_part);
// 线条高度
let cap_height = 2;
// 获取的音频数据
let bufferLength = analyser.frequencyBinCount,
dataArray = new Uint8Array(bufferLength);
// 每段包含的频谱宽
let array_width = Math.round(bufferLength / bar_num);
// 变量提前,i 表示变量, value表示音频值, isStop表示上方线条的结束
let i, value, isStop;
let that = this;
// 创建数组,线条数组,判断动画结束
let array = [];
that.animation_id = null;
let drawVisual = function () {
that.analyser.getByteFrequencyData(dataArray);
if (that.isEnd) {
isStop = true;
for (i = dataArray.length - 1; i >= 0; i--) {
dataArray[i] = 0;
}
// 使其静止时图为空
for (i = array.length - 1; i >= 0; i--) {
isStop = isStop && (array[i] == 0);
};
if (isStop) {
that.res[0].node.cancelAnimationFrame(that.animation_id);
that.playAudio(that.buffer)
that.setData({
progress: 0
})
return;
}
}
ctx.clearRect(0, 0, c_width, c_height)
for (i = 0; i < bar_num; i++) {
value = dataArray[i * array_width];
if (array.length < bar_num) {
array.push(value)
}
if (value < array[i]) {
--array[i];
ctx.fillRect(i * bar_part, c_height - array[i], bar_width, cap_height);
} else {
ctx.fillRect(i * 12, c_height - value, bar_width, cap_height);
array[i] = value;
};
ctx.fillStyle = '#f99'; //柱形图颜色
ctx.fillRect(bar_part * i, c_height - value, bar_width, value);
}
that.animation_id = that.res[0].node.requestAnimationFrame(drawVisual);
}
that.animation_id = that.res[0].node.requestAnimationFrame(drawVisual);
},
playAudio(buffer,start) {
if (typeof this.source !== 'undefined') {
this.source.stop(0);
}
if (this.animationId !== null) {
this.res[0].node.cancelAnimationFrame(this.animationId);
}
this.analyser = this.audioCtx.createAnalyser();
this.source = this.audioCtx.createBufferSource();
this.source.buffer = buffer;
this.isEnd = false;
this.source.connect(this.analyser);
this.analyser.connect(this.audioCtx.destination);
this.source.start(start ? start : 0);
let that = this;
this.source.onended = function () {
if (!that.isForceStop) {
that.isEnd = true;
}
};
console.log("this.source",this.source)
this.drawAudio(this.analyser);
},
loadMusic(music) {
wx.createSelectorQuery().select('#audio_canvas').fields({
node: true,
size: true
}).exec(res => {
this.res = res
let audioContext = this.audioCtx
wx.showLoading({
title: '加载音频中',
})
wx.request({
url: music,
responseType: 'arraybuffer',
success: (res) => {
wx.hideLoading({})
let audioData = res.data
audioContext.decodeAudioData(audioData, (buffer) => {
this.playAudio(buffer)
this.buffer = buffer
this.setData({
startPosition: 0,
lastPlay: audioContext.currentTime,
videoDuration: buffer.duration
})
}, (err) => {
wx.hideLoading({})
console.error('decodeAudioData fail', err)
})
},
fail: (err) => {
wx.hideLoading({})
console.error('request fail', err)
}
})
})
},