0

I made easy background generator, which makes bg filled with same-sized diferently grayed cubes (blocks). Then I've prepared function to animate some random block to blue and then back to starting color. I set interval to run random block each 5 seconds. Inside function are two loops (2x 100 steps), with timeout setted to 25ms (5 sec in total). Problem is, that by console color changes but not on Canvas. Can you find, what is wrong, please?

Code on jsFiddle, this is here only to allow me to ask the question:

var blockSize = 20; // Size of one block
var canW = 200; // Size of canvas
var canH = 100;
var cvsName = 'cvsBg'; // Name of canvas
var color = [0, 0, 0]; // container for changing color
var defaultColor = [0, 0, 0] // container for block color given by prepareBG()
var blueColor = [0, 204, 255]; // color to which is default color changing
var rndPositonW = getRandomInt(0, canW); // random point on Canvas, to select actually changing block
var rndPositonH = getRandomInt(0, canH);
var cSteps = 100; // animation steps between default color and blue color
var cChangeInterval = 5000; // run one block animation  each 5s

var can = document.getElementById(cvsName);
can.width = canW;
can.height = canH;
var ctx = can.getContext('2d');

prepareBG(ctx); //prepare bg with gray colored blocks

setInterval(colorAnimation, cChangeInterval, ctx, rndPositonW, rndPositonH);
// in this interval to do animation: 
//    100 steps from default color to blue
//    100 steps from blue back to default color

function colorAnimation(ctx, cBlockX, cBlockY) {
  var pixelData = ctx.getImageData(cBlockX, cBlockY, 1, 1).data; // get default color at randomly selected point
  defaultColor = [pixelData[0], pixelData[1], pixelData[2]];

  var blockX = Math.floor(cBlockX / blockSize) * blockSize; // get zero coordinated of block under randomly selected point 
  var blockY = Math.floor(cBlockY / blockSize) * blockSize;

  var i, j;
  for (i = 0; i <= cSteps; i++) { // coloring from default color to blue: 100 steps, one step each 25ms (color change interval is 5000 divided by total steps 200 )
    for (j = 0; j < 3; j++) { // compute changing RGB e.g. if i is near 100, color will be almost blue
      color[j] = defaultColor[j] + ((blueColor[j] - defaultColor[j]) / cSteps) * i;
    }
    console.log("Drawing: X=%i, Y=%i, Color=%i,%i,%i", blockX, blockY, color[0], color[1], color[2]);
    setTimeout(drawBlock, cChangeInterval / cSteps / 2, ctx, blockX, blockY, color, blockSize);
  }
  for (i = 0; i <= cSteps; i++) { // coloring from blue color to defalut: 100 steps, one step each 25ms (color change interval is 5000 divided by total steps 200 )
    for (j = 0; j < 3; j++) { // compute changing RGB e.g. if i is near 100, color will be almost default
      color[j] = blueColor[j] - ((blueColor[j] - defaultColor[j]) / cSteps) * i;
    }
    setTimeout(drawBlock, cChangeInterval / cSteps / 2, ctx, blockX, blockY, color, blockSize);
  }
  rndPositonW = getRandomInt(0, canW); // find new random point for next block animation
  rndPositonH = getRandomInt(0, canH);
}

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function prepareBG(ctx) {
  var x = 0;
  var y = 0;
  var c = 0
  for (x = 0; x <= can.width; x += blockSize) {
    for (y = 0; y <= can.height; y += blockSize) {
      c = getRandomInt(50, 56);
      drawBlock(ctx, x, y, [c, c, c], blockSize);
    }
  }
  //CANVAS will be used as body's background:
  //document.body.style.background = 'url(' + can.toDataURL() + ')';
}

function drawBlock(ctx, x, y, c, blocksize) {
  ctx.fillStyle = 'rgb(' + c[0] + ', ' + c[1] + ', ' + c[2] + ')';
  ctx.fillRect(x, y, blockSize, blockSize);
}
<canvas id="cvsBg">
Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56
  • My recommendation, add more `console.log` reduce the steps, and instead of the random, to make your debugging easier just hard code it to the top left tile. – Helder Sepulveda Oct 18 '18 at 12:59
  • @HelderSepulveda note that it is not recommended to include code from outsources into questions yourself. You have no right on this code and by posting it here, you are licensing it under CC-BY-SA, which may be a copyright infringement in itself. – Kaiido Oct 18 '18 at 14:44
  • Your code is a bit too complex so that I can have a clear reading on it, but if I still read correctly at this late time, your assumption that *2x 100 steps x 25ms = (5 sec in total)* is mislead. You are setting all these callbacks to the same timeout in a loop => all your 2x 100 steps will fire roughly 25ms later => ~25ms in total (a bit more with execution time, but still very far from 5s) – Kaiido Oct 18 '18 at 14:56
  • 1
    Already burnt my CloseVote, but *Possible duplicate of [Why does everything display at once, using setTimeout in Javascript?](https://stackoverflow.com/questions/18646908/why-does-everything-display-at-once-using-settimeout-in-javascript).* – Kaiido Oct 18 '18 at 14:58

1 Answers1

1

Great, thanks Kaiido! It was my wrong understanding to how setTimeout works... I've wrongly expected behavior similar to pause because that was what i searched for.

var blockSize = 20; // Size of one block
var canW = 500; // Size of canvas
var canH = 200;
var cvsName = 'cvsBg'; // Name of canvas
var color = [0,0,0]; // container for changing color
var defaultColor = [0,0,0] // container for block color given by prepareBG()
var blueColor = [0,204,255]; // color to which is default color changing
var animationSteps = 30; // total animation steps up & down
var animationPause = 1000; // pause between animations
var animationTime = 1000; // duration of one animation
var animationRunning = false;

var can = document.getElementById(cvsName); 
can.width = canW;
can.height = canH;
var ctx = can.getContext('2d');

prepareBG(ctx); //prepare bg with gray colored blocks

var count = 0;
function update(rndPositonW,rndPositonH) {
  if(!animationRunning) {
    var pixelData = ctx.getImageData(rndPositonW, rndPositonH, 1, 1).data; // get default color at randomly selected point
    defaultColor = [pixelData[0],pixelData[1],pixelData[2]];
    animationRunning = true;
  }
  var blockX = Math.floor(rndPositonW / blockSize) * blockSize; // get zero coordinated of block under randomly selected point 
  var blockY = Math.floor(rndPositonH / blockSize) * blockSize;
  var j;
  for(j=0; j<3; j++){ // compute changing RGB e.g. if i is near 100, color will be almost blue
    if(count < (animationSteps / 2)) color[j] = defaultColor[j] + ((blueColor[j] - defaultColor[j]) / (animationSteps / 2)) * count; //way up
    else color[j] = blueColor[j] - ((blueColor[j] - defaultColor[j]) / (animationSteps / 2)) * (count - (animationSteps / 2)); //way down
  }
  drawBlock(ctx, blockX, blockY, color, blockSize);
  
  if (++count <= animationSteps) {
    setTimeout(update, animationTime / animationSteps, rndPositonW, rndPositonH);
  } else {
    count = 0;
    animationRunning = false;
    setTimeout(update, animationPause, getRandomInt(0, canW), getRandomInt(0, canH));
  }       
}
update(getRandomInt(0, canW), getRandomInt(0, canH));

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
} 

function prepareBG(ctx){     
  var x = 0;
  var y = 0;
  var c = 0
  for (x = 0; x <= can.width; x+=blockSize) {
    for (y = 0; y <= can.height; y+=blockSize) {
      c = getRandomInt(50, 60);
      drawBlock(ctx, x, y, [c,c,c], blockSize);
    }
  }
  //CANVAS will be used as body's background:
  //document.body.style.background = 'url(' + can.toDataURL() + ')';
}

function drawBlock(ctx,x,y,c,blocksize){
  ctx.fillStyle = 'rgb(' + c[0] + ', ' + c[1] + ', ' + c[2] + ')';
  ctx.fillRect(x, y, blockSize, blockSize);
}
<canvas id="cvsBg">