Search code examples
javascriptcanvasdrawing

Javascript: does canvas get refreshed only when <script> ends?


I am writing very basic code hear (just started learning) using Javascript & canvas. I try to do some random drawing (rectangles have 50% chance to appear on certain spots). Once this happens I want browser to wait one second, clear the screen and instantly draw it again (visually one drawing should be instantly replaced by another).

When I do so, it seems like canvas does not want to update, it only updates once when main loop ends, you can find code below:

<script>
  let canvas = document.querySelector("canvas");
  let context = canvas.getContext("2d");

  function wait(ms) {
    var start = new Date().getTime();
    var end = start;
    while (end < start + ms) {
      end = new Date().getTime();
    }
  }

  for (let k = 0; k < 3; k++) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    console.log("First checkpoint");
    for (let i = 0; i < 5; i++) {
      for (j = 0; j < 5; j++) {
        let width_custom = 60;
        let height_custom = 60;
        let gap = 20;
        let x = 100 + (width_custom + gap) * i;
        let y = 100 + (height_custom + gap) * j;

        context.beginPath();
        context.rect(x, y, width_custom, height_custom);
        context.stroke();
        if (Math.random() > 0.5) {
          context.beginPath();
          context.rect(x + 8, y + 8, width_custom - 16, height_custom - 16);
          context.stroke();
        }
      }
    }
    wait(1000); 
    console.log("Second checkpoint");
  }

Is there any way to force canvas to refresh during the loop?

Best, Mat


Solution

  • Your wait function goes against the asynchronous nature of javascript. You are blocking the program in a way so things don't get refreshed. You should be using setTimeout for this kind of things (or setInterval or even better requestAnimationFrame).

    I have prepared quite confusing but fun example how to loop asynchronously and wait. I'm sure other people will suggest to use Promise instead. Or await and async. It's all good.

    let canvas = document.querySelector("canvas");
    let context = canvas.getContext("2d");
    
    my_loop(3)
    
    function my_loop(k) {
      if (k > 0) {
        one_round(1000, function() {
          my_loop(k - 1)
        })
      }
    }
    
    function one_round(delay, foo_then) {
      context.clearRect(0, 0, canvas.width, canvas.height);
      for (let i = 0; i < 5; i++) {
        for (j = 0; j < 5; j++) {
          let width_custom = 60;
          let height_custom = 60;
          let gap = 20;
          let x = 100 + (width_custom + gap) * i;
          let y = 100 + (height_custom + gap) * j;
          context.beginPath();
          context.rect(x, y, width_custom, height_custom);
          context.stroke();
          if (Math.random() > 0.5) {
            context.beginPath();
            context.rect(x + 8, y + 8, width_custom - 16, height_custom - 16);
            context.stroke();
          }
        }
      }
      setTimeout(foo_then, delay);
    }
    <canvas></canvas>