2

I have researched a lot, mostly in SO, about setTimeout being "not blocking", and so being not suitable to be used inside a for loop, since the loop keeps going on and one while the function calls keep building up.

I have an HTML file which documents an image processing algorithm, so I want to display the active pixels "walking" in a "human-readable" speed. The implementation I tried and does not work is the following:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>

<body onload="run();">

    <canvas id="Canvas" width=501 height=601></canvas>

    <script>

        function run() {
            reevaluate();
        };

        var fixedcenteri;
        var fixedcenterj;



        function reevaluate() {

            var altura_imagem = 50;
            var largura_imagem = 40;

            for (var i = 0; i < altura_imagem; i++) {
                for (var j = 0; j < largura_imagem; j++) {

                    fixedcenteri = i;
                    fixedcenterj = j;

                    setTimeout(draw, 100);


                    // if I uncomment this I can see what I want, but...
                    // alert(i+j);
                };
            };
        };


        function draw () {
            var elem = document.getElementById('Canvas');
            var cx = elem.getContext('2d');

            w = elem.width;
            h = elem.height;

            cx.fillStyle = "white";
            cx.fillRect(0,0,w,h);

            cx.fillStyle = 'blue';
            cx.fillRect(fixedcenteri, fixedcenterj, 10, 10);
        }

    </script>
</body>
</html>
heltonbiker
  • 26,657
  • 28
  • 137
  • 252
  • Why not put the contents of the loop in a function that is called in the timeout, passing the appropriate values for i and j as parameters? – jonhopkins Apr 01 '13 at 20:30
  • @jonhopkins That would be another way to do it, surely, but I think it would be a matter of preference, and probably is not related to my actual problem. – heltonbiker Apr 01 '13 at 20:35
  • @apsillers actually I think the problem is not solved there (at least that would not solve my problem), because I need each call to the function `draw` to complete BEFORE the loop continues. My code, and the answers to the linked question, seem to be building up function calls during the while loop, and executing them "all together" after some timeout. :o( – heltonbiker Apr 01 '13 at 20:46

3 Answers3

5

Try RequestAnimationFrame!

RequestAnimationFrame is asynchronous just like setTimeout and it's more efficient than setTimeout.

In addition, it offers animation grouping and auto-stop for off-screen animations.

You can even throttle it to your desired FPS using this technique:

var fps = 15;
function draw() {
    setTimeout(function() {
        requestAnimationFrame(draw);

        // your draw() stuff goes here

    }, 1000 / fps);
}
markE
  • 102,905
  • 11
  • 164
  • 176
2

The easiest implementation would be to store all you draw commands in an array and then process that array using setTimeout to wait between draw commands.

Here's a quick example -> http://jsfiddle.net/K4D84/

//in your initial loop instead of draw
drawCommands.push({i: i, j: j});

Then...

function slowDraw(index) {
    index = index || 0;

    var drawCommand = drawCommands[index];

    if (drawCommand) {
        fixedcenteri = drawCommand.i;
        fixedcenterj = drawCommand.j;
        setTimeout(function () {
            draw();
            slowDraw(++index);
        }, 100);
    }    
}
plalx
  • 42,889
  • 6
  • 74
  • 90
0

I think this does what you are looking for.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>

<body onload="draw();">

    <canvas id="Canvas" width=501 height=601></canvas>

    <script>

        var fixedcenteri = 0;
        var fixedcenterj = 0;

        function draw () {
            var elem = document.getElementById('Canvas');
            var cx = elem.getContext('2d');

            w = elem.width;
            h = elem.height;

            cx.fillStyle = "white";
            cx.fillRect(0,0,w,h);

            cx.fillStyle = 'blue';
            cx.fillRect(fixedcenteri, fixedcenterj, 10, 10);

            if(fixedcenteri < 50) {
                if(fixedcenterj < 40) {
                    fixedcenterj++;
                } else {
                    fixedcenterj = 0;
                    fixedcenteri++;
                }
                setTimeout(draw, 100);
            }
        }

</script>

clav
  • 4,221
  • 30
  • 43
  • I accepted this one, as it keeps the interval, modifies very little my original code, and accounts for loop termination. +1! – heltonbiker Apr 01 '13 at 21:00