Вероятно, это связано с тем, что вы используете нецелые значения для установки масштаба контекста и / или перевода.
При этом ваши ректы больше не находятся на границах пикселей, а на плавающихценности.
Давайте создадим простой пример:
Два пикселя, один по координатам (x, y) (11,10), другой по координатам (12,10).
В масштабе по умолчаниюоба пикселя должны быть соседями.
Теперь, если мы применим масштаб 1.3
, реальные координаты пикселей первого квадрата будут в (14,3,13), а те извторой (15.6,13).
Ни одна из этих координат не может содержать один пиксель, поэтому браузеры будут применять сглаживание, которое заключается в сглаживании вашего цвета цветом фона, чтобы создать впечатление меньших пикселей.Это то, что делает ваши сетки.
const ctx = small.getContext('2d');
ctx.scale(1.3, 1.3);
ctx.fillRect(2,10,10,10);
ctx.fillRect(12,10,10,10);
const mag = magnifier.getContext('2d');
mag.scale(10,10);
mag.imageSmoothingEnabled = false;
mag.drawImage(small, 0,-10);
/* it is actually transparent, not just more white */
body:hover{background:yellow}
<canvas id="small" width="50" height="50"></canvas><br>
<canvas id="magnifier" width="300" height="300"></canvas>
Чтобы избежать этого, несколько решений, все зависит от того, что вы делаете точно.
В вашем случае, кажется, выЯ бы много выиграл, работая над ImageData , который позволил бы вам заменить все эти fillRect
вызовы на более простые и быстрые манипуляции с пикселями.
Используя маленький ImageData, размер вашей матрицы,Вы можете заменить каждый прямоугольник на один пиксель.Затем вам нужно просто поместить эту матрицу на холст и перерисовать холст над собой в правильном масштабе после отключения флага imageSmootingEnabled , который позволяет отключить сглаживание для drawImage
и CanvasPatterns * 1036.* только.
// the original matrix will be 20x20 squares
const width = 20;
const height = 20;
const ctx = canvas.getContext('2d');
// create an ImageData the size of our matrix
const img = ctx.createImageData(width, height);
// wrap it inside an Uint32Array so that we can work on it faster
const pixels = new Uint32Array(img.data.buffer);
// we could have worked directly with the Uint8 version
// but our loop would have needed to iterate 4 pixels every time
// just to draw a radial-gradient
const rad = width / 2;
// iterate over every pixels
for(let x=0; x<width; x++) {
for(let y=0; y<height; y++) {
// make a radial-gradient
const dist = Math.min(Math.hypot(rad - x, rad - y), rad);
const color = 0xFF * ((rad - dist) / rad) + 0xFF000000;
pixels[(y * width) + x] = color;
}
}
// here we are still at 50x50 pixels
ctx.putImageData(img, 0, 0);
// in case we had transparency, this composite mode will ensure
// that only what we draw after is kept on the canvas
ctx.globalCompositeOperation = "copy";
// remove anti-aliasing for drawImage
ctx.imageSmoothingEnabled = false;
// make it bigger
ctx.scale(30,30);
// draw the canvas over itself
ctx.drawImage(canvas, 0,0);
// In case we draw again, reset all to defaults
ctx.setTransform(1,0,0,1,0,0);
ctx.globalCompositeOperation = "source-over";
body:hover{background:yellow}
<canvas id="canvas" width="600" height="600"></canvas>