I have a Chrome extension that allows the user to take a screenshot from a selected area of their screen.
The flow looks like this:
- User clicks the screenshot button in the Chrome Extension popup menu. The popup sends a message to the Background script to capture the tab.
- The background script screenshots the entire tab and sends the URL to the content script.
- The content script allows the user to draw a rectangle on the screen and it crops out that area of the screen from the screenshot and sends it to the server.
The Issue: When the user selects the area to crop, the final result is scaled in and shifted to the left and up a bit. For the life of me, I can’t figure out why. Also, if I try to crop when the page is scrolled vertically/horizontally the canvas gets created at the top. I’m not sure if that’s a related issue or not.
Example Screenshots
Original Image
Cropped Result (I selected the entire image when cropping)
The code below is a modified version of just the content script, without the server calls.
let selectableCanvasArea: HTMLCanvasElement;
let screenshotUrl: string;
let startX: number, startY: number, endX: number, endY: number;
export const startScreenshotProcess = (screenshotUrlParam: string): void => {
screenshotUrl = screenshotUrlParam; // This comes from the background script
createCanvas();
// Change the cursor
document.body.style.cursor = "crosshair";
// Darken the entire screen so we can see the cursor
document.body.style.filter = "brightness(50%)";
window.addEventListener("mousedown", mouseDownListener);
window.addEventListener("mouseup", mouseUpListener);
}
const createCanvas = () => {
// Create the canvas and prepend it to the HTML
selectableCanvasArea = document.createElement("canvas");
selectableCanvasArea.style.position = "absolute";
selectableCanvasArea.style.top = "0px";
selectableCanvasArea.style.left = "0px";
selectableCanvasArea.width = 0;
selectableCanvasArea.height = 0;
selectableCanvasArea.style.zIndex = "9999";
selectableCanvasArea.style.border = "3px dashed lightblue";
document.body.insertAdjacentElement('beforebegin', selectableCanvasArea);
}
const mouseDownListener = (e: MouseEvent): void => {
startX = e.clientX;
startY = e.clientY;
selectableCanvasArea.style.top = startY + "px";
selectableCanvasArea.style.left = startX + "px";
window.addEventListener("mousemove", mouseMoveListener);
}
const mouseMoveListener = (e: MouseEvent): void => {
e.preventDefault();
selectableCanvasArea.width = Math.abs(e.clientX - startX);
selectableCanvasArea.height = Math.abs(e.clientY - startY);
}
const mouseUpListener = (e: MouseEvent): void => {
endX = e.clientX;
endY = e.clientY;
processScreenshot();
}
export const processScreenshot = async () => {
const image = new Image();
image.src = screenshotUrl;
image.onload = () => {
let pos = selectableCanvasArea.getBoundingClientRect();
let originalX = (pos.left + window.scrollX);
let originalY = (pos.top + window.scrollY);
let croppedWidth = pos.width;
let croppedHeight = pos.height;
let croppedCanvas = document.createElement("canvas");
croppedCanvas.width = croppedWidth;
croppedCanvas.height = croppedHeight;
let ratioX = image.naturalWidth / croppedWidth;
let ratioY = image.naturalHeight / croppedHeight;
let context = croppedCanvas.getContext("2d");
context?.drawImage(image,
originalX, originalY, // sx, sy
croppedWidth, croppedHeight, // sWidth, sHeight
0, 0, // dx, dy
croppedWidth, croppedHeight // dWidth, dHeight
);
document.body.insertAdjacentHTML('beforebegin', `<img src="${croppedCanvas.toDataURL()}" alt="Cropped Image"/>`);
}
}