То, что вы заметили, очень просто объяснить:
Когда вы рисуете с холста, вы фактически изменяете размер уменьшенной версии исходного изображения, где пиксели были удалены.Хотя при рисовании исходного изображения все пиксели все еще доступны для увеличения.
Очевидно, что при использовании ухудшенной версии, которая в настоящее время нарисована на холсте, вы увидите больше артефактов.
Чтобы обойти это, вы можете сохранить копию своего холста с нарисованным на нем полным качеством вашего изображения и выполнять операции рисования на этом высококачественном холсте.
Затем вы сможетенарисуйте этот высококачественный холст обратно на видимом холсте при любом уровне масштабирования.
var out = output.getContext('2d'); // the canvas we will see
// create an 'offscreen' canvas
// it will store our high quality scene
var offscreen = document.createElement('canvas');
var off = offscreen.getContext('2d');
// 'img' won't get used anymore out of 'begin'
var img = new Image();
img.onload = begin;
img.src = "https://www.maisons-elytis-lyonouest.fr/wp-content/uploads/Maison-M.-ST-GENIS-LES-OLLIERES-HD-3.jpg";
var zoomed = false;
var mousedown = false;
var rad = 20;
var grad = off.createRadialGradient(rad/2, rad/2, rad, rad/2, rad/2, 0);
grad.addColorStop(0.2, 'transparent');
grad.addColorStop(1, 'gold');
function begin() {
// first draw at full quality
offscreen.width = img.width;
offscreen.height = img.height;
off.drawImage(img, 0, 0);
off.fillStyle = grad;
off.globalCompositeOperation = 'lighter';
// then draw to the visible canvas
drawToMain();
}
function drawToMain() {
// draw our full quality canvas on the distorted, visible one
if(zoomed) {
// uniform scale + translate
out.setTransform(2,0,0,2,-output.width/2, -output.height/2);
}
else {
out.setTransform(1,0,0,1,0,0);
}
out.drawImage(offscreen, 0, 0, output.width, output.height);
}
function drawDot(x, y) {
// we will draw on the offscreen canvas
// so we need to convert the visible canvas coordinates
// to the ones of our normal sized 'offscreen' canvas
var ratio_x = offscreen.width / output.width;
var ratio_y = offscreen.height / output.height;
if(zoomed) {
// apply the camera transforms
// and non uniform scale (output distortion)
off.setTransform(ratio_x / 2, 0,0,ratio_y / 2,0,0);
x += (output.width / 2);
y += (output.height / 2);
}
else {
// simply distort our offscreen context by setting its scale factor
off.setTransform(ratio_x, 0, 0, ratio_y, 0, 0);
}
// draw a dot.
// Note that for such a drawing, you'd be better even redraw from scratch every time
// instead of keeping an offscreen canvas
// but for demo purposes...
off.beginPath();
off.arc(x, y, (rad * (+zoomed + 1)), 0, Math.PI*2);
off.translate(x - rad /2, y - rad / 2);
off.fill();
// render our offscreen canvas on the visible one
drawToMain();
}
check.onchange = function toggleZoom() {
zoomed = !zoomed;
drawToMain();
};
output.onmousemove = function mousemovehandler(evt) {
if(mousedown) {
var rect = output.getBoundingClientRect();
drawDot(evt.clientX - rect.left, evt.clientY - rect.top);
}
};
output.onmousedown = function mousedownhandler(evt) {
mousedown = true;
output.onmousemove(evt);
};
output.onmouseup = function() {
mousedown = false;
}
<label>zoom<input type="checkbox" id="check"></label><br>
click and drag to draw dots on the canvas<br>
<canvas id="output" width="500" height="500"></canvas>