Search code examples
javascriptcanvashtml5-canvashtml2canvas

How to draw vertical lines as a music track progressbar using Canvas in Javascript


I am trying to build a custom audio player. I am using HtmlAudioElement for that. I have managed to do play, pause, display duration and all but i have stuck where i need to show a progress bar when playing a track. I have percentage value( How much song already played ) which is updating every millisecond. I was wondering using that percentages value can i use canvas and draw vertical lines as percentage increases?

enter image description here

So this is how progress bar should change color as percentage increases. I have no idea where to start. I am very new to Canvas. Any Help ?

I have tried this so far. ( This is not what i want) .

const canvas = document.getElementById('progress');
canvas.width = 1920;
canvas.height = 100;
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'grey';

for(let i = 0; i < 1000; i+=10){
    console.log(i);
    ctx.fillRect(i,0,5,100);
}

let count = 0;
let percent = 0;
ctx.fillStyle = 'orange';

function draw(){
    console.log('Hello')
    ctx.fillRect(count,0,5,100);
    count+=10;
    if(count > 100/1){
        return;
    }
    window.requestAnimationFrame(draw);
}

draw();

enter image description here

So this what it looks right now.

I want as playing percentage changes i need to hook that value somewhere in Canvas so i can draw those lines.

Thanks in advance :)


Solution

  • You can read the value (% done) directly from the media object that holds it.

    I can not workout what it is you are having trouble with. you mention color the following is a simple color changing bar.

    Basic progress bar

    Some constants to define the progress bar

    const width = 500;
    const height = 20;
    const startColor = {r: 255, g: 128, b: 0}; // unsigned byte values 0 - 255
    const endColor = {r: 255, g: 128, b: 0};
    

    To interpolate the color

    function progressColor(start, end, progress) {
        const r = (end.r - start.r) * progress + start.r;
        const g = (end.g - start.g) * progress + start.g;
        const b = (end.b - start.b) * progress + start.b;
        return `rgb(${r | 0},${g |0 }, ${b | 0})`;
    }
    

    To draw the bar

    function progressBar(progress) {
        ctx.fillStyle =  progressColor(startColor, endColor, progress);
        // whatever design you want the example is just a simple bar
    
        ctx.fillRect(0,0, width * progress, height);
    }
    

    The main loop

    • The main loop every 60th second.

    • If the audio is paused it stops.

    • Care must be taken when starting the animation loop not to start it more than once

    • Note that the audio will have stopped up to ~1/60th second before the last animation frame is rendered.

    The main animation loop...

    var progressAnimating = false;  // this is to avoid more that one mainLoop playing
    function mainLoop() {
        ctx.clearRect(0,0,width,height);
    
        // draw backing bar here
    
        const fractionDone = audio.time / audio.duration;
        progressBar(fractionDone);
    
        // only while not paused
        if (!audio.paused) {
            requestAnimationFrame(mainLoop);
        } else {
            progressAnimating = false;  // flag that the animation has stopped
        }
    }
    

    Use Audio events to start animation

    • Use the audio play event to start the animation.

    • Check to make sure that the animation is not playing before you request the first frame.

    • If the animation is still active then it will continue by its self

    .

    audio.addEventListener("play",() => {
        if (!progressAnimating) {
            requestAnimationFrame(mainLoop);
            progressAnimating = true;
        }
    });
    

    Styling the progress bar

    The next function replaces the solid bar from above. It uses the clip area to limit the width so you dont need to fiddle with fractional bars.

    const barWidth = 4; // in px
    const barSpacing = 8; // distance from left edge to next left edge in px
    
    function progressBar(progress) {
        ctx.save(); // save the unclipped state
    
        ctx.beginPath(); // create the clip path
        ctx.rect(0,0, width * progress, height);
        ctx.clip();
    
        ctx.fillStyle =  progressColor(startColor, endColor, progress);
    
        ctx.beginPath(); // draw all bars as one
        var x = 0;
        while (x < width * progress) {
            ctx.rect(x, 0, barWidth, height);
            x += barSpacing;
        }
        ctx.fill(); // draw that bars
    
        ctx.restore(); // restore unclipped state
    }