2013-03-20

Canvas Crossroad

Canvas context

The canvas element is part of HTML5 and allows for dynamic, scriptable rendering of shapes and images. The actual drawing happens within a context. In this example we'll use 2d context for drawing:

 var canvas = document.createElement('canvas');

 canvas.width = window.innerWidth;
 canvas.height = window.innerHeight;

 var ctx = canvas.getContext('2d');
 document.body.appendChild(canvas);


Drawing shapes and lines

In order to make the changes to the context as easy as possible the best practice is to save the context before doing any changes and restoring it after we are done with drawing. Especially if we change context rotation, colors and fill styles:

  ctx.save();
  ...
  ctx.restore();

The crossroad canvas example uses these constructs for drawing shapes:

  • arc
  • fillRect
  • moveTo + lineTo


Circle

There's no method named circle, instead we use arc and make one from 0 to 2 * Pi:

 ctx.fillStyle = 'red';
 ctx.beginPath();

 ctx.arc(x, y, radius, startAngle, endAngle, antiClockwise);

 // ctx.stroke() would draw circumference, fill makes a circle
 ctx.fill();


Rectangle

Drawing rectangles is pretty straight forward. One must set coordinates, width and height:

 ctx.fillRect (x, y, width, height);


Lines

When working with lines we can move starting point and then make a line to some other position:

 ctx.beginPath();

 ctx.moveTo(x1, y1);
 ctx.lineTo(x2, y2);

 ctx.closePath();
 
 ctx.stroke();


Clearing the drawing

Very important when doing animations because calculating what pixels to clear every time would be pretty complicated to code. Changing a small piece of canvas takes less time than clearing everything and then drawing it again. In the past developers kept an off screen canvas and did drawing on it and then at the end of the animation loop they copied the off screen canvas to the on screen canvas.

Most of the browser vendors started to do this automatically so in modern browsers keeping an off screen canvas actually degrades performance.

 ctx.clearRect (x, y, width, height);


Collision detection

All objects in the crossroad animation are represented as circles. The formula for collision detection between two circles is:

 function collision(c1, c2) {
 
  var dx = c1.x - c2.x;
  var dy = c1.y - c2.y;
  var dist = c1.radius + c2.radius;
   
  return (dx * dx + dy * dy <= dist * dist);
 }
If the movement happens along a single axis line it's even simpler we just need to check the coordinate on the relevant axis (in this case x):
  Math.abs(x1 - x2) > (radius + safety_distance)


requestAnimationFrame

The browser vendors optimized the usual timer animation loop and provided api for it. But since some vendors don't support it or the users have older browsers we use this pattern:

 // shim layer with setTimeout fallback
 window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    function( callback ){
     window.setTimeout(callback, 1000 / 60);
    };
 })();
 
 (function animloop(){
  requestAnimFrame(animloop);
  // call drawing functions here
 })();


requestAnimationFrame

And here's my example on:
CSSDeck
Codepen
GitHub

No comments: