
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#upload {
position: fixed;
top: 0;
left: 0;
}
audio {
position: fixed;
top: 0;
right: 0;
}
<input type="file" id="upload" accept="audio/mpeg">
const upload = document.querySelector(\'#upload\')
upload.addEventListener(\'change\', (e) => {
const [file] = e.target.files
if (!file) upload.value = \'\'
const { type } = file
if (!type.includes(\'audio\')) {
alert(\'仅支持音频格式文件\')
upload.value = \'\'
return false
}
const url = URL.createObjectURL(file)
draw(url)
})
function draw(url) {
const {
clientWidth: width,
clientHeight: height,
} = document.body
const audio = new Audio
audio.src = url
audio.controls = true
audio.load()
const canvas = document.createElement(\'canvas\')
canvas.width = width
canvas.height = height
document.body.appendChild(audio)
document.body.appendChild(canvas)
const canvasCtx = canvas.getContext(\'2d\')
const audioCtx = new AudioContext
const sourceNode = audioCtx.createMediaElementSource(audio)
// 音频分析器
const analyser = audioCtx.createAnalyser()
analyser.fftSize = 128 // FFT
sourceNode.connect(analyser)
analyser.connect(audioCtx.destination)
const len = analyser.frequencyBinCount
const buffer = new Uint8Array(len)
const gap = 2 // 间隙
const pillarWidth = (width / len) - gap
const gradient = canvasCtx.createLinearGradient(0, 0, 0, 500)
gradient.addColorStop(.8, \'#1eec1e\')
gradient.addColorStop(.5, \'#c97a3d\')
gradient.addColorStop(0, \'#f00f00\')
const render = () => {
canvasCtx.fillStyle = \'#fff\'
canvasCtx.fillRect(0, 0, width, height)
analyser.getByteFrequencyData(buffer)
let x = 0
buffer.forEach(v => {
const pillarHeight = (v / 255) * height
canvasCtx.fillStyle = gradient
canvasCtx.fillRect(x, height - (pillarHeight || 2), pillarWidth, pillarHeight || 2)
x += (pillarWidth + gap)
})
requestAnimationFrame(render)
}
render()
audio.play()
}