i did implement the audio visualization but i am not happy with the results. i was hoping for a better solution than what i came up with. like for example is there a better way to draw the bars and also a better way to animate other than what i did which is changing the gradient.
this is the audio track before the play button is clicked
this is the audio track after play button is clicked
here is the code
window.addEventListener('load', init)
const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')
const ctx = canvas.getContext('2d')
const sample = [...] // this has a random height values between 0 and 24 px
function init() {
let barLength = 55
let barWidth = (canvas.width - (barLength - 1)) / barLength
let region = new Path2D()
for(let i = 0; i < sample.length; i ) {
let halfHeight = sample[i] / 2
region.rect(i * (barWidth 1), (24 - halfHeight), barWidth, sample[i])
}
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.clip(region)
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let xPos = 0
function drawRect() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
let pos = xPos / canvas.width
pos = pos > 1.0 ? 1 : pos
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "orange")
grd.addColorStop(pos, "orange")
grd.addColorStop(pos, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let prev
let requestId
function draw(sec) {
if (!prev)
prev = new Date()
let now = new Date()
let difference = now - prev
prev = now
xPos = (difference * canvas.width) / (duration * 1000)
drawRect()
requestId = requestAnimationFrame(draw)
if(xPos >= canvas.width) {
prev = null
cancelAnimationFrame(requestId)
}
}
btn.addEventListener('click', () => {
draw()
})
CodePudding user response:
Your idea is pretty good.
The only thing I could think of is that you are currently redrawing the full image every frame, but that just changes the orange part.
What you could do instead is draw "holes" into the picture as to have the white bars be fully transparent. That way, we would see through the waves. The picture would then be in a div and its background would be changed to have the orange highlight.
This in no way changes the visual, it just reduces the computations needed for the same effect.
const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')
const ctx = canvas.getContext('2d')
const sample = []; // this has a random height values between 0 and 24 px
for(let i=0;i<55;i ){
sample.push(Math.random()*24);
}
function init() {
ctx.fillRect(0,0, canvas.width, canvas.height);
let barLength = 55
let barWidth = (canvas.width - (barLength - 1)) / barLength
let region = new Path2D()
for(let i = 0; i < sample.length; i ) {
let halfHeight = sample[i] / 2
region.rect(i * (barWidth 1), (24 - halfHeight), barWidth, sample[i])
}
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.clip(region);
ctx.globalCompositeOperation = 'destination-atop';
ctx.globalAlpha = 0;
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
let xPos = 0
function drawRect() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
let pos = xPos / canvas.width
pos = pos > 1.0 ? 1 : pos
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
grd.addColorStop(0, "orange")
grd.addColorStop(pos, "orange")
grd.addColorStop(pos, "#ddd")
grd.addColorStop(1, "#ddd")
ctx.fillStyle = grd
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
init();
const div = document.querySelector('#div');
setInterval(function(){
xPos ;
const percent = xPos / canvas.width *100;
const t = `linear-gradient(90deg, rgba(255,100,0,1), rgba(255,100,0,1) ${percent}%, rgba(255,255,255,1) ${percent}%, rgba(255,255,255,1) 100%)`;
div.style.backgroundImage = t;
},100);
div{
background-color:white;
display:inline-block;
height:50px;
}
<div id="div"><canvas height="50"></canvas></div>