javascripthtmlhtml5-canvas

How to render HTML5 canvas within a loop


I'm trting to do the following:

I'm running through a for loop and each time I get a new information and want to draw it on the canvas. The problem is that the canvas is being drawn just after the loop finish, and I can't see no animation. If I step in with debug, or with alert messages, it will draw to the canvas immediatly. How can I do it?

Here is my code, as you can see I've already tried with setTimeout in multiple ways but didn't worked.

for(var i=0;i<1000; i ++)
    {
        addLines(ga.getBestCreature());   // This method draw on the canvas 
        
        setTimeout(function() {
        ga.startEvolution(1);       // This method change the best creature
        }, 10);   
    }

The drowing method:

function addLines(best) {
    
    if(!best) {
        return;
    }
    var global_path = best.data;
    
    redraw(); // Clear off the canvas

    var cityA;
    var cityB;
    for (var i = 0; i < (global_path.length - 1); i++) {
        
        cityA = ga.cities[global_path[i]];
        cityB = ga.cities[global_path[i+1]];
        
        ctx.moveTo(cityA.x,cityA.y);
        ctx.lineTo(cityB.x,cityB.y);
        ctx.stroke();
    }
}

Any Ideas??

Update: After was advised to use the requestAnimationFrame, I've came out with something like this. Is this the best approach? I'm updating a global variable called 'run' on click events

var run = 0;
function animate() {
    
    if(run > 0) {
        ga.startEvolution(1);
        run--;
    }
  
addLines(ga.getBestCreature());
    requestAnimationFrame(animate);
}

animate();

Solution

  • Your best take is to separate concerns : have an animation loop continuously running, and, "each time [you] get a new information", update your scene description, it will draw as soon as the display is ready.

    So a first very simple demo : here the 'new information' is a user click.

    var cv = document.getElementById('cv');
    var ctx = cv.getContext('2d');
    var cvWidth = cv.width;
    var cvHeight = cv.height;
    
    // Simple scene description : an array of colored rects
    var everyObject = [
      [60, 70, 30, 30, 'yellow']
    ];
    
    // animation : always running loop.
    
    function animate() {
      // call again next time we can draw
      requestAnimationFrame(animate);
      // clear canvas
      ctx.clearRect(0, 0, cvWidth, cvHeight);
      // draw everything
      everyObject.forEach(function(o) {
        ctx.fillStyle = o[4];
        ctx.fillRect(o[0], o[1], o[2], o[3]);
      });
      // 
      ctx.fillStyle = '#000';
      ctx.fillText('click to add random rects', 10, 10);
    }
    
    animate();
    
    
    // click handler to add random rects
    window.addEventListener('click', function() {
      addRandRect();
    });
    
    function addRandRect() {
      var randColor = Math.random() > 0.5 ? 'blue' : 'red';
      everyObject.push([Math.random() * cvWidth, Math.random() * cvHeight, 10 + Math.random() * 40, 10 + Math.random() * 40, randColor]);
    }
    <canvas id='cv' width=400 height=200></canvas>

    Now if you want some kind of animation, or a simple version using setTimeOut would be :

    var cv = document.getElementById('cv');
    var ctx = cv.getContext('2d');
    var cvWidth = cv.width;
    var cvHeight = cv.height;
    
    // Simple scene description : an array of colored rects
    var everyObject = [
      [60, 70, 30, 30, 'yellow']
    ];
    
    // animation : always running loop.
    
    function animate() {
      // call again next time we can draw
      requestAnimationFrame(animate);
      // clear canvas
      ctx.clearRect(0, 0, cvWidth, cvHeight);
      // draw everything
      everyObject.forEach(function(o) {
        ctx.fillStyle = o[4];
        ctx.fillRect(o[0], o[1], o[2], o[3]);
      });
      // 
      ctx.fillStyle = '#000';
      ctx.fillText('click to add 4 random rects with a delay', 10, 10);
    }
    
    animate();
    
    
    // click handler to add random rects
    window.addEventListener('click', function() {
      addRandRect();
      setTimeout(addRandRect, 300);
      setTimeout(addRandRect, 600);
      setTimeout(addRandRect, 900);
    });
    
    function addRandRect() {
      var randColor = Math.random() > 0.5 ? 'blue' : 'red';
      everyObject.push([Math.random() * cvWidth, Math.random() * cvHeight, 10 + Math.random() * 40, 10 + Math.random() * 40, randColor]);
    }
      <canvas id='cv' width=400 height=200></canvas>

    (By the way i wrote about animation here if interested : http://codepen.io/gamealchemist/post/animationcanvas1 )