Reputation: 10802
I have a canvas on my page and I have an instrument which allows users to select an area, which he/she wants to crop. It looks like so:
To crop an image I calculate top-left pixel coordinate [xmin, ymin]
and bottom-right coordinate [xmax,ymax]
. And cropping itself is done like so:
context.drawImage(image,xmin,ymin,xmax-xmin,ymax-ymin,0,0,xmax-xmin,ymax-ymin);
And the problem is, on some random computers and some random browsers, this code produces correct chunk and on some others - incorrect. That is why I can not provide even a demo or a fiddle. Locally, on my laptop it's ok on all browsers (FF, IE, Opera, Chrome,Safari), but on my users computers it is incorrect.
So, my question is, what may be wrong with this call:
context.drawImage(image,xmin,ymin,xmax-xmin,ymax-ymin,0,0,xmax-xmin,ymax-ymin);
Is there something, I should take into account to make this code cross-browser and cross-platform?
Upvotes: 1
Views: 791
Reputation: 136638
Your problem is* that some of your users have their browser's zoom-level set to something else than 100%.
With your current logic, you assume the image is displayed at its original size, which is the one used by drawImage
. But when zoomed in or out, the browser does scale the rendered image, making the coordinates of the cursor wrong with regard to the natural size of your image.
To circumvent this, you need to scale your coordinates relatively to the ratio displayedSize / naturalSize.
Here is a simple example.
onload = function() {
const ctx = c.getContext('2d');
c.width = img.width;
c.height = img.height;
img.onmousedown = c.onmousedown = handleMouseDown;
img.onmousemove = c.onmousemove = handleMouseMove;
img.onmouseup = c.onmouseup = handleMouseUp;
var rect = { min_x: 0, min_y: 0, max_x: 0, max_y: 0, updating: false};
draw();
function handleMouseDown(evt) {
evt.preventDefault();
var targetBB = evt.target.getBoundingClientRect();
rect.updating = true;
rect.min_x = rect.max_x = evt.clientX - targetBB.left;
rect.min_y = rect.max_y = evt.clientY - targetBB.top;
draw();
}
function handleMouseUp(evt) {
rect.updating = false;
draw();
}
function handleMouseMove(evt) {
if(!rect.updating) return;
var targetBB = evt.target.getBoundingClientRect();
rect.max_x = evt.clientX - targetBB.left;
rect.max_y = evt.clientY - targetBB.top;
draw();
}
function draw() {
ctx.filter = 'blur(2px)';
ctx.drawImage(img, 0,0);
ctx.filter = 'none';
var dx = Math.min(rect.min_x, rect.max_x),
dy = Math.min(rect.min_y, rect.max_y),
dw = Math.abs(rect.min_x - rect.max_x),
dh = Math.abs(rect.min_y - rect.max_y);
if(!dh || !dw) return;
ctx.strokeRect(dx, dy, dw, dh);
var ratio_W = img.clientWidth / img.naturalWidth,
ratio_H = img.clientHeight / img.naturalHeight,
sx = dx * ratio_W,
sy = dy * ratio_H,
sw = dw * ratio_W,
sh = dh * ratio_H;
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
}
}
<h3>Try to zoom-in/out your browser</h3>
<img id="img" src="https://i.sstatic.net/ujq5W.png">
<canvas id="c"></canvas>
*According to your comment.
Upvotes: 2