Someone on oDesk asked how to draw a grid of squares on html canvas in JavaScript, I thought I’d just answer here.

Here is what I’ve got.
[iframe width=”100%” height=”400” src=”//jsfiddle.net/15sj0rxt/1/embedded/result,js,html,css/“ allowfullscreen=”allowfullscreen” frameborder=”0”]

Task

There should be a function drawGrid with the following form which can be invoked to get the desired result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function configuration() {
return {
"rows": 80,
"cols": 52,
"size": 6,
"separation": 2,
"color_1": "#dd0000",
"color_2": "#00dd00",
};
}
function drawGrid(n) {
// ...
}

Details

  • Grid is rows x cols of squares
  • Each square is of size size
  • Each square is separated from adjacent squares and the canvas border by size separation
  • The fill color of the square will be one of two colors, color_1 or color_2
  • The drawGrid method accepts a parameter n. The first n squares will be color_1 (starting from the upper-left square, then going left-to-right, then top-to-bottom). The rest will be color_2. If n is more than the number of squares that would be drawn, then they are all color_1.
  • drawGrid should use the parameters specified in the configuration function.

Additional behavior

When you mouseover a given square, a small tooltip should appear that shows the text “row: …, col: …”, with the appropriate row and column entries for that square. The upper-left square is row 0, column 0.

Don’t attach onhover or similar events to each square, since that will be too expensive; instead detect the mouse position and work out which square is hovered.

No third-party libraries:)

Source code

grid.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<html>
<head>
<script type="text/javascript" src="grid.js"></script>
<style>
.popup{
position:absolute; /*allows span to be on top of image*/
border: 1px solid orange;
background-color: white;
display:none;
}
</style>
</head>
<body>
<canvas id="canvas" class="tooltip"></canvas>
<span id="tooltip-span"></span>
<span class="popup" popupText="This is some popup text" id="tt">Locate </span>
</body>
<script type="text/javascript">
function configuration() {
return {
"rows": 20,
"cols": 22,
"size": 25,
//"size": 42,
"separation": 10,
"color_1": "#dd0000",
"color_2": "#00dd00",
};
}
drawGrid(48);
</script>
</html>

grid.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function drawGrid(n) {
var config = configuration(),
canvas = document.getElementById('canvas');
function getOffset(el) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
};
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
var offset = getOffset(canvas);
return {
x: evt.pageX - offset.left,
y: evt.pageY - offset.top,
};
};
function getCoordinateByNum(num) {
return config.separation + num * (config.separation + config.size)
}
function getNumByCoordinate(x) {
return Math.floor((x - config.separation) / (config.size + config.separation))
}
function onMouseOver(evt) {
var x = evt.pageX,
y = evt.pageY,
mPos = getMousePos(canvas, evt),
tooltipSpan = document.getElementById('tt'); // relative position
tooltipSpan.style.top = (y + 20) + 'px';
tooltipSpan.style.left = (x + 20) + 'px';
var numX = getNumByCoordinate(mPos.x);
var numY = getNumByCoordinate(mPos.y);
tooltipSpan.innerHTML = 'row:' + (numY + 1) + ', col:' + (numX + 1 );
tooltipSpan.style.display = 'none';
if(
mPos.x >= getCoordinateByNum(numX)
&& mPos.x < config.size+ getCoordinateByNum(numX)
&& mPos.y >= getCoordinateByNum(numY)
&& mPos.y < config.size + getCoordinateByNum(numY)
) {
tooltipSpan.style.display = 'block';
}
}
function init() {
var i, j,
x = config.separation,
y = config.separation,
nSquares = 0, context = canvas.getContext('2d');
canvas.width = getCoordinateByNum(config.cols);
canvas.height = getCoordinateByNum(config.rows);
canvas.style.backgroundColor = '#d3d3d3'; //light grey, so we see where canvas is
//drawing
context.fillStyle = config.color_1;
context.beginPath();
for(i = 0; i < config.rows; i++) {
for(j = 0; j < config.cols; j++) {
x = getCoordinateByNum(j);
y = getCoordinateByNum(i);
context.rect(x, y, config.size, config.size);
nSquares++;
if(nSquares == n) {
context.fill();
context.beginPath();
context.fillStyle = config.color_2;
}
}
}
context.fill();
canvas.addEventListener('mousemove', onMouseOver, false);
}
init();
}