Board Game Experiment, with JS Prototypes and Canvas

Rendering

Rendering scale

In order to render in the largest visible scale even after resizing, we’re going to trigger this .setSize() function on .ready() and .resize().

Quoridor.prototype.setSize = function () {
  'use strict';
  // Set the extremes, and calculate the size.
  var min = 200,
    max = jQuery(window).width() - 100,
    size = jQuery(window).height() - 225;
  // Responsive details
  if (jQuery(window).width() < 980) { size -= 10; }
  // Restrict sizes to the extremes
  if (size < min) { size = min; } else if (size > max) { size = max; }
  // Resize the canvas
  jQuery('figure').width(size + 60);
  this.canvas.width = size;
  this.canvas.height = size;
  // Resize the player's captions
  jQuery('#players').css({'width': size, 'height': size});
  // For calculations sake, all sizes are defined in ratio of a pattern unit
  this.pattern = size / (this.board.length - this.units.span);
  // Render the interface with the new ratio
  this.render();
};

Draw !

If you’re not very familiar with it, I suggest you to read this beautiful post about HTML5’s canvas manipulation. Otherwise what’s coming here is pretty basic.

Quoridor.prototype.render = function (preview, progress) {
  'use strict';
  // For animations, the "preview"'s opacity will equal the progress
  if (progress === undefined) { progress = 0.5; }
  var i, j, x, y,
    square = this.units.square * this.pattern,
    pawn = this.units.pawn * this.pattern, // This is a radius...
    fence = this.units.fence * this.pattern,
    span = this.units.span * this.pattern;
  this.ctx.globalAlpha = 1;
  this.ctx.strokeStyle = this.colors.stroke;
  this.ctx.lineWidth = 1;
  // Clear the canvas
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  // Draw the squares
  this.ctx.fillStyle = this.colors.square;
  for (i = 0; i < = this.size; i += 1) {
    for (j = 0; j <= this.size; j += 1) {
      x = i * this.pattern;
      y = j * this.pattern;
      this.ctx.fillRect(x, y, square, square);
      this.ctx.strokeRect(x, y, square, square);
    }
  }
  this.ctx.lineWidth = 2;
  // Draw the fences
  this.ctx.fillStyle = this.colors.fence;
  for (i = 0; i < this.fences.length; i += 1) {
    x = this.fences[i].a * this.pattern;
    y = this.fences[i].b * this.pattern;
    if (this.fences[i].horizontal) {
      y += square;
      this.ctx.fillRect(x, y, fence, span);
      this.ctx.strokeRect(x, y, fence, span);
    } else {
      x += square;
      this.ctx.fillRect(x, y, span, fence);
      this.ctx.strokeRect(x, y, span, fence);
    }
  }
  this.ctx.lineWidth = 4;
  // Draw the pawns
  for (i = 0; i < this.players.length; i += 1) {
    x = (this.players[i].a * this.pattern) + (square / 2);
    y = (this.players[i].b * this.pattern) + (square / 2);
    this.ctx.fillStyle = this.colors.pawns[i];
    this.ctx.beginPath();
    this.ctx.arc(x, y, pawn, 0, (Math.PI * 2), true);
    this.ctx.closePath();
    this.ctx.fill();
    this.ctx.stroke();
  }
  this.ctx.globalAlpha = 0.4;
  // Highlight the current player
  x = (this.players[this.current].a * this.pattern) + (square / 2);
  y = (this.players[this.current].b * this.pattern) + (square / 2);
  this.ctx.fillStyle = this.colors.pawns[this.current];
  this.ctx.beginPath();
  this.ctx.arc(x, y, (pawn + 4), 0, (Math.PI * 2), true);
  this.ctx.closePath();
  this.ctx.fill();
  // Draw the preview (if sent in parameter)
  if (preview) {
    // Opacity eventually depends on the progress
    this.ctx.globalAlpha = progress;
    x = preview.a * this.pattern;
    y = preview.b * this.pattern;
    if (preview instanceof Square) {
      // Draw a pawn
      x = (preview.a * this.pattern) + (square / 2);
      y = (preview.b * this.pattern) + (square / 2);
      this.ctx.beginPath();
      this.ctx.arc(x, y, pawn, 0, (Math.PI * 2), true);
      this.ctx.closePath();
      this.ctx.fill();
      this.ctx.stroke();
    } else {
      // Draw a fence
      if (preview.horizontal) {
        y += square;
        this.ctx.fillRect(x, y, fence, span);
        this.ctx.strokeRect(x, y, fence, span);
      } else {
        x += square;
        this.ctx.fillRect(x, y, span, fence);
        this.ctx.strokeRect(x, y, span, fence);
      }
    }
  }
  return true;
};