From 641abf711c8853db46737af72f17e51a21207a88 Mon Sep 17 00:00:00 2001 From: Arda Samadi Date: Tue, 23 Jun 2026 16:27:28 +0330 Subject: [PATCH] fix(upload): preserve resolution when cropping product images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crop canvas was sized to cropArea.width/height, which are the editor's on-screen (CSS) pixels — a few hundred px. Phone photos were therefore downscaled to that size before upload, so cropped product images came out blurry (most visibly on iPhone, whose source photos are large). Size the canvas to the crop region in the source image's native pixels (adjustedCropWidth/Height, already computed) and draw 1:1. Bumped JPEG quality 0.9 -> 0.92. The backend still caps the final image to 1600px webp, so output size stays bounded. Co-Authored-By: Claude Opus 4.8 --- app/components/store/ImageEditor.tsx | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/app/components/store/ImageEditor.tsx b/app/components/store/ImageEditor.tsx index a27be80..66e3212 100644 --- a/app/components/store/ImageEditor.tsx +++ b/app/components/store/ImageEditor.tsx @@ -302,20 +302,30 @@ export function ImageEditor({ const adjustedCropWidth = cropArea.width * scaleX; const adjustedCropHeight = cropArea.height * scaleY; - // Set canvas size to crop area - canvas.width = cropArea.width; - canvas.height = cropArea.height; + // Crop rectangle expressed in the SOURCE image's native pixels + // (adjusted* already scales the on-screen crop back up to the original). + const srcX = Math.max(0, adjustedCropX); + const srcY = Math.max(0, adjustedCropY); + const srcWidth = Math.min(adjustedCropWidth, img.width - srcX); + const srcHeight = Math.min(adjustedCropHeight, img.height - srcY); + + // Size the canvas to the full-resolution crop region, NOT the editor's + // on-screen crop size. Using the display size here was downscaling phone + // photos to a few hundred pixels, which is what made uploads blurry. + // The backend still caps the final image to 1600px. + canvas.width = Math.max(1, Math.round(srcWidth)); + canvas.height = Math.max(1, Math.round(srcHeight)); // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); - // Draw the cropped portion + // Draw the cropped portion at native resolution ctx.drawImage( img, - Math.max(0, adjustedCropX), - Math.max(0, adjustedCropY), - Math.min(adjustedCropWidth, img.width - Math.max(0, adjustedCropX)), - Math.min(adjustedCropHeight, img.height - Math.max(0, adjustedCropY)), + srcX, + srcY, + srcWidth, + srcHeight, 0, 0, canvas.width, @@ -331,7 +341,7 @@ export function ImageEditor({ onClose(); }, "image/jpeg", - 0.9 + 0.92 ); };