Reputation: 2460
I want to use the Canvas API to create a square grid where each cell has a 1px border on its bottom and a 1px border on its right. The entire grid would then have a 1px border drawn around it, so it'd look like this:
Once the grid exists, I want to write a function that will highlight a given cell. So for example calling highlightCell(1, 3, 'green')
would result in this:
I thought this would be simple enough, but I'm having a devil of a time working it out. My problem is that when I account for having to straddle pixel lines -- drawing from eg 3.5 to 7.5, rather than 3 to 7, to not get blurry lines -- the math to figure out coordinates doesn't seem to work the way I expect at the edges, and I get results like this, where the highlight isn't placed correctly.
My math is:
2,4
by drawing a 19x19 px rectangle at ???. I cannot figure out a value here that consistently works.I'd appreciate someone explaining what I've done wrong because I'm sure it's something dumb but I just can't see it.
Here's the JS Fiddle of my attempt.
Upvotes: 1
Views: 5977
Reputation: 6872
I would do it by separating the logic and the drawing. For example by having a state
object and a drawFrame
function; like this:
// setup state
const state = {
__arr: [],
__width: 20,
__height: 20,
__cell: {
width: 20,
height: 20,
},
__onUpdate: () => {},
__calcIndex(x, y) {
const index = x + y * this.__width;
if (index >= this.__arr.length || index < 0) {
throw new Error('Invalid index!');
}
return index;
},
init(onUpdate) {
this.__arr = Array(this.__width * this.__height).fill(0);
this.__onUpdate = onUpdate;
},
get(x, y) {
const index = this.__calcIndex(x, y);
return this.__arr[index];
},
set(x, y, value) {
const index = this.__calcIndex(x, y);
this.__arr[index] = value;
this.__onUpdate();
},
};
// setup drawing logic
const canvas = document.createElement('canvas');
document.body.append(canvas);
const ctx = canvas.getContext('2d');
const drawFrame = () => {
const cell = state.__cell;
ctx.lineWidth = 1;
ctx.strokeStyle = 'black';
ctx.fillStyle = 'orangered';
for (let x = 0; x < state.__width; x++) {
for (let y = 0; y < state.__width; y++) {
ctx.strokeRect(cell.width * x, cell.height * y, cell.width, cell.height);
if (state.get(x, y) !== 0) {
ctx.fillRect(1 + cell.width * x, 1 + cell.height * y, cell.width-1, cell.height-1);
}
}
}
}
state.init(drawFrame);
canvas.width = state.__width * state.__cell.width;
canvas.height = state.__height * state.__cell.height;
drawFrame();
state.set(2, 4, 1);
state.set(3, 5, 1);
state.set(2, 6, 1);
state.set(7, 4, 1);
Upvotes: 1