A crop picker often displays an image smaller than its source. If the picker reports a rectangle in displayed coordinates, passing those numbers directly to drawImage() selects the wrong part of the source. Scale the selection into intrinsic image coordinates first.
For an HTMLImageElement, drawImage() uses intrinsic dimensions. image.width can describe a displayed size, so use naturalWidth and naturalHeight when calculating the source rectangle. See MDN's drawImage notes.
Assume the entire decoded image is displayed in a 600 by 400 coordinate area. Its intrinsic dimensions are 2400 by 1600. A selection beginning at displayed point (150, 100), with displayed width 300 and height 200, maps to source point (600, 400), width 1200 and height 800.
This helper performs the scaling. The rectangle must already be relative to the displayed image content, excluding borders, padding, and any letterboxing. All arguments are numbers.
function sourceRect(image, rect, shownWidth, shownHeight) {
const values = [rect.x, rect.y, rect.width, rect.height,
shownWidth, shownHeight];
if (!values.every(Number.isFinite) ||
shownWidth <= 0 || shownHeight <= 0 ||
image.naturalWidth <= 0 || image.naturalHeight <= 0 ||
rect.x < 0 || rect.y < 0 ||
rect.width <= 0 || rect.height <= 0 ||
rect.x + rect.width > shownWidth ||
rect.y + rect.height > shownHeight) {
throw new RangeError("Selection must be inside a loaded image");
}
const scaleX = image.naturalWidth / shownWidth;
const scaleY = image.naturalHeight / shownHeight;
return {
x: rect.x * scaleX,
y: rect.y * scaleY,
width: rect.width * scaleX,
height: rect.height * scaleY,
};
}
Use the result as the four source arguments in the nine-argument drawImage() overload. Choose the destination width and height separately; those describe the output canvas region, not the selection in the original image.
This helper does not translate pointer coordinates or model CSS transforms. A picker using object-fit: contain must first locate the actual image rectangle inside its element. A rotated or mirrored preview also needs its own inverse mapping. Resolve those layout details before calling the helper.
Put a different mark near each corner of a test image. Select one corner in the reduced preview and inspect the resulting crop. Then repeat at another corner to catch an incorrect offset.
Top comments (0)