0

I would like to be run an iteration of a loop every x seconds.

Every iteration will change the value of a pixel and paint the changed image to the canvas.

So far I have this:

        for(let i=0;i<width;i++){ //Rows
          for(o=i*(width*4);o<i*(width*4)+(width*4);o+=4){//pixels in current row
                imgData.data[o] = 255
          }
          ctx.putImageData(imgData, xPos, yPos);
        }

Which successfully changes all pixels in the image to be 255 red. However, this happens all at the same time. I would like this to happen a pixel a time every x seconds.

Any guidance?

Dean
  • 80
  • 7
  • Did you try using a `setInterval()` event over the function ? – mattdaspy May 18 '19 at 16:45
  • Set Interval doesnt work as the loop creates all the intervals at once and then they all fire after the time is up. – Dean May 18 '19 at 16:46
  • Check this answer : https://stackoverflow.com/questions/45498873/add-a-delay-after-executing-each-iteration-with-foreach-loop – mattdaspy May 18 '19 at 16:50
  • 1
    @MatheusSimões This would create `(width * height)` / `imageData.data.length` `Promise`s. This might work for smaller collections but not for pixels in an image. – Andreas May 18 '19 at 16:53

2 Answers2

2

If you want this to happen a pixel every second you shouldn't use the outer for-loop at all. Instead use a global variable which holds the position of the current pixel. With each interval increment that variable and put the changed imageData to the canvas.

Something like:

var originalWidth = width;
var currentPixel = 0;

function update() {
  for (o = currentPixel * (originalWidth * 4); o < currentPixel * (originalWidth * 4) + (originalWidth * 4); o += 4) {
    imgData.data[o] = 255;
  }
  ctx.putImageData(imgData, xPos, yPos);
  if (currentPixel + 1 < originalWidth) {
    currentPixel++
  } else {
    clearInterval(intervalId);
  }
}
var intervalId = setInterval(update, 1000);
Dean
  • 80
  • 7
obscure
  • 11,916
  • 2
  • 17
  • 36
  • Had to make a slight adjustment ,currentPixel += 4 was meant to be o+=4 if im not mistaken. Good answer though! – Dean May 18 '19 at 17:10
  • 1
    Outch - you're right! Sorry! I've overlooked that. It should have been o+=4 – obscure May 18 '19 at 17:27
1

A version with setTimeout() (or requestAnimationFrame()) and without the use of global variables (and without all the "complicated" math):

const modifyPixel = (function(canvas) {
  const context = canvas.getContext("2d");
  const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

  return function(index = 0) {
    if (index >= imageData.data.length) {
      return;
    }

    imageData.data[index] = 255;
    context.putImageData(imageData, 0, 0);

    index += 4;

    setTimeout(function() {
      modifyPixel(index + 4);
    }, 50);
  };
}(document.querySelector("canvas")));

(function init() {
  const canvas = document.querySelector("canvas");
  const context = canvas.getContext("2d");

  context.beginPath();
  context.rect(0, 0, canvas.width, canvas.height);
  context.fillStyle = "green";
  context.fill();
}());

const modifyPixel = (function(canvas) {
  const context = canvas.getContext("2d");
  const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

  return function(index = 0) {
    if (index >= imageData.data.length) {
      return;
    }

    imageData.data[index] = 255;
    context.putImageData(imageData, 0, 0);
    
    index += 4;

    setTimeout(function() {
      modifyPixel(index + 4);
    }, 50);
  };
}(document.querySelector("canvas")));

modifyPixel();
<canvas style="width:200px; height:200px" width="50px" height="50px"></canvas>
Andreas
  • 21,535
  • 7
  • 47
  • 56
  • My only concern with this it becomes a lot harder to customize down the line. a lot simpler though so +1 – Dean May 18 '19 at 17:37