canvas context – cache the variable value for window.requestAnimationFrame call back function

var canvas;
var context;
var screenH;
var screenW;
var radius = 20;

$('document').ready(function(){

  // Get the canvas
    canvas = $('#heatmap');

  // Calculate the screen size
    screenH = canvas.height();
    screenW = canvas.width();

    // Fill out the canvas
    canvas.attr('height', screenH);
    canvas.attr('width', screenW);
    context = canvas[0].getContext('2d');

  var x = Math.round( (Math.random()*(.75-.25)+.25) * screenW);
  var y = Math.round( (Math.random()*(.65-.35)+.35) * screenH);

  circle(canvas, x, y);
})


function circle(canvas, x, y){

  context.save();
  context.beginPath();
  context.arc(x,y,radius,0,2*Math.PI);
  context.closePath();

  radius += 1;
  if(radius < 61){
    window.requestAnimationFrame(circle)
  }

  context.fill();
  context.restore(); 
}

The goal here is to have growing circle and randomly positioned inside canvas but within a range. So the problem here is window.requestAnimationFrame(circle) will ignite the circle() recursively until variable radius hits 60. But since circle() is a separate function, x and y are undefined. So I guess I need to cache the initial values of x and y and pass it into the callback?

Answer

Here’s a minimal example:

var radius = 20;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
canvas.height = innerHeight;
canvas.width = innerWidth;

var x = (Math.random() * (0.75 - 0.25) + 0.25) * canvas.width;
var y = (Math.random() * (0.65 - 0.35) + 0.35) * canvas.height;

circle(ctx, x, y, radius);

function circle(ctx, x, y, radius) {
  (function animate() {
    ctx.beginPath();
    ctx.arc(x, y, radius++, 0, 2 * Math.PI);
    ctx.fill();

    if (radius < 61) {
      window.requestAnimationFrame(animate);
    }
  })();
}
 

There’s some invalid JS in your original code (canvas.height() is not a function), so feel free to revert my boilerplate back to what you’re using. It also made sense to pass radius as well as x and y into the circle() function but you can discard that.

Even with those changes, this design doesn’t make much sense to me. Usually, the animation loop would be independent of any individual rendered entity such as a circle which should be an object with a draw() routine. The code as it stands sort of implies that you can make 2000 independent circles which is problematic if they each have their own requestAnimationFrame() loop running internally.

I’d prefer to design as seen in this fiddle, which I got a little carried away on, but the circle entities are renderable objects with their own set of encapsulated properties and there is one main animation loop running everything.