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;
};