Разные результаты могут быть вызваны разными реализациями API, или, возможно, даже другими настройками, которые вы установили в своем браузере, из-за которых один предпочитает ускорение GPU по сравнению с вычислениями CPU, или один обрабатывает графическую память лучше, чем другой или что еще.
Но в любом случае, если я правильно понимаю ваш код, вы можете получить лучшее, чем эти два варианта.
Я могу придумать два основных способа, которые вам придется проверить.
Первый - визуализировать один большой прямоугольник размером всей матрицы в одном цвете, затем перебрать все ячейки другого цвета и объединить их в один подпуть, чтобы вы вызывали fill()
только в конец этой подпуть композиции, один раз.
Наконец, вы нарисуете сетку поверх всего этого (сетка, которая может быть либо простым перекрестным рисунком, либо предварительно отрисованным на неэкранном холсте, либо еще раз одним подпутем).
const W = 50;
const H = 50;
const cellSize = 10;
const grid_color = 'black';
var grid_mode = 'inline';
const ctx = canvas.getContext('2d');
const matrix = [];
canvas.width = W * cellSize;
canvas.height = H * cellSize;
for (let i = 0; i < H; i++) {
let row = [];
matrix.push(row);
for (let j = 0; j < W; j++) {
row.push(Math.random() > 0.5 ? 0 : 1);
}
}
const grid_pattern = generateGridPattern();
const grid_img = generateGridImage();
draw();
function draw() {
shuffle();
// first draw all our green rects ;)
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// now draw all the red ones
ctx.fillStyle = 'red';
ctx.beginPath(); // single sub-path declaration
for (let i = 0; i < H; i++) {
for (let j = 0; j < W; j++) {
// only if a red cell
if (matrix[i][j])
ctx.rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
ctx.fill(); // single fill operation
drawGrid();
requestAnimationFrame(draw);
}
function shuffle() {
let r = Math.floor(Math.random() * H);
for (let i = r; i < r + Math.floor(Math.random() * (H - r)); i++) {
let r = Math.floor(Math.random() * W);
for (let j = r; j < r + Math.floor(Math.random() * (W - r)); j++) {
matrix[i][j] = +!matrix[i][j];
}
}
}
function drawGrid() {
if (grid_mode === 'pattern') {
ctx.fillStyle = grid_pattern;
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.translate(-cellSize / 2, -cellSize / 2);
ctx.fill();
ctx.setTransform(1, 0, 0, 1, 0, 0);
} else if (grid_mode === 'image') {
ctx.drawImage(grid_img, 0, 0);
} else {
ctx.strokeStyle = grid_color;
ctx.beginPath();
for (let i = 0; i <= cellSize * H; i += cellSize) {
ctx.moveTo(0, i);
ctx.lineTo(cellSize * W, i);
for (let j = 0; j <= cellSize * W; j += cellSize) {
ctx.moveTo(j, 0);
ctx.lineTo(j, cellSize * H);
}
}
ctx.stroke();
}
}
function generateGridPattern() {
const ctx = Object.assign(
document.createElement('canvas'), {
width: cellSize,
height: cellSize
}
).getContext('2d');
// make a cross
ctx.beginPath();
ctx.moveTo(cellSize / 2, 0);
ctx.lineTo(cellSize / 2, cellSize);
ctx.moveTo(0, cellSize / 2);
ctx.lineTo(cellSize, cellSize / 2);
ctx.strokeStyle = grid_color;
ctx.lineWidth = 2;
ctx.stroke();
return ctx.createPattern(ctx.canvas, 'repeat');
}
function generateGridImage() {
grid_mode = 'inline';
drawGrid();
const buf = canvas.cloneNode(true);
buf.getContext('2d').drawImage(canvas, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
return buf;
}
field.onchange = e => {
grid_mode = document.querySelector('input:checked').value;
}
<fieldset id="field">
<legend>Draw grid using:</legend>
<label><input name="grid" type="radio" value="inline" checked>inline</label>
<label><input name="grid" type="radio" value="pattern">pattern</label>
<label><input name="grid" type="radio" value="image">image</label>
</fieldset>
<canvas id="canvas"></canvas>
Другим совершенно другим подходом, который вы могли бы использовать, было бы непосредственное управление ImageData.
Установите размер вашей матрицы (cellSize будет равен 1), поместите его на холст, а затем, наконец, просто измените его масштабирование и нарисуйте сетку.
ctx.putImageData(smallImageData, 0,0);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(ctx.canvas, 0, 0, ctx.canvas.width, ctx.canvas.height);
drawgrid();
const W = 50;
const H = 50;
const cellSize = 10;
const grid_color = 'black';
canvas.width = W * cellSize;
canvas.height = H * cellSize;
const ctx = canvas.getContext('2d');
// we'll do the matrix operations directly on an imageData
const imgData = ctx.createImageData(W, H);
const matrix = new Uint32Array(imgData.data.buffer);
const red = 0xFF0000FF;
const green = 0xFF008000;
for (let i = 0; i < H*W; i++) {
matrix[i] = (Math.random() > 0.5 ? green : red);
}
prepareGrid();
ctx.imageSmoothingEnabled = false;
draw();
function draw() {
shuffle();
// put our update ImageData
ctx.putImageData(imgData, 0, 0);
// scale its result
ctx.drawImage(ctx.canvas,
0,0,W,H,
0,0,canvas.width,canvas.height
);
// draw the grid which is already drawn in memory
ctx.stroke();
requestAnimationFrame(draw);
}
function shuffle() {
// here 'matrix' is actually the data of our ImageData
// beware it is a 1D array, so we need to normalize the coords
let r = Math.floor(Math.random() * H);
for (let i = r; i < r + Math.floor(Math.random() * (H - r)); i++) {
let r = Math.floor(Math.random() * W);
for (let j = r; j < r + Math.floor(Math.random() * (W - r)); j++) {
matrix[i*W + j] = matrix[i*W + j] === red ? green : red;
}
}
}
function prepareGrid() {
// we draw it only once in memory
// 'draw()' will then just have to call ctx.stroke()
ctx.strokeStyle = grid_color;
ctx.beginPath();
for (let i = 0; i <= cellSize * H; i += cellSize) {
ctx.moveTo(0, i);
ctx.lineTo(cellSize * W, i);
for (let j = 0; j <= cellSize * W; j += cellSize) {
ctx.moveTo(j, 0);
ctx.lineTo(j, cellSize * H);
}
}
}
<canvas id="canvas"></canvas>