Image cropper

#online-cropper { max-width: 950px; margin: 20px auto; padding: 22px; background: #f4f7fb; border-radius: 14px; font-family: Arial, sans-serif; color: #1e293b; box-sizing: border-box; } #online-cropper * { box-sizing: border-box; } #online-cropper h2 { text-align: center; color: #173b70; font-size: 27px; } #online-cropper .description { text-align: center; color: #64748b; margin-bottom: 22px; } #online-cropper .upload-box { padding: 28px 15px; border: 2px dashed #2563eb; background: white; text-align: center; border-radius: 12px; } #online-cropper input[type=file] { max-width: 100%; margin-top: 12px; } #online-cropper .heading { font-size: 18px; font-weight: bold; margin: 22px 0 12px; } #online-cropper .ratio-buttons { display: grid; grid-template-columns: repeat(auto-fit,minmax(85px,1fr)); gap: 9px; } #online-cropper button { padding: 12px 8px; border: 0; background: #2563eb; color: white; border-radius: 7px; cursor: pointer; font-weight: bold; font-size: 13px; } #online-cropper button:hover { background: #1d4ed8; } #online-cropper button.active { background: #0f766e; } #online-cropper .custom-box { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 14px; } #online-cropper .custom-box input { width: 100px; padding: 11px; border: 1px solid #cbd5e1; border-radius: 6px; } #online-cropper .canvas-area { display: none; margin-top: 22px; padding: 12px; background: white; border-radius: 10px; } #online-cropper .canvas-wrapper { position: relative; width: 100%; margin: auto; overflow: hidden; touch-action: none; background: #ddd; user-select: none; } #online-cropper #crop-image { display: block; width: 100%; height: 100%; pointer-events: none; } #online-cropper #crop-selection { position: absolute; border: 2px solid #fff; box-shadow: 0 0 0 9999px rgba(0,0,0,.55); cursor: move; touch-action: none; } #online-cropper #crop-selection:after { content: ""; position: absolute; inset: 33.33%; border: 1px dashed rgba(255,255,255,.8); pointer-events: none; } #online-cropper .handle { position: absolute; width: 13px; height: 13px; background: white; border: 2px solid #2563eb; border-radius: 50%; z-index: 2; } #online-cropper .tl { left:-7px;top:-7px;cursor:nwse-resize; } #online-cropper .tr { right:-7px;top:-7px;cursor:nesw-resize; } #online-cropper .bl { left:-7px;bottom:-7px;cursor:nesw-resize; } #online-cropper .br { right:-7px;bottom:-7px;cursor:nwse-resize; } #online-cropper .action-buttons { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 17px; } #online-cropper .action-buttons button { flex: 1; min-width: 130px; padding: 14px; } #online-cropper .reset-btn { background: #64748b; } #online-cropper .download-btn { background: #16a34a; } #online-cropper .preview-box { display: none; text-align: center; background: white; border-radius: 10px; padding: 18px; margin-top: 20px; } #online-cropper .preview-box img { max-width: 100%; max-height: 350px; object-fit: contain; } #online-cropper .status { text-align: center; margin-top: 15px; color: #2563eb; font-weight: bold; } #online-cropper .help { color: #64748b; font-size: 13px; line-height: 1.6; margin-top: 15px; } @media(max-width:550px) { #online-cropper { padding: 13px; } #online-cropper h2 { font-size: 22px; } }

Online Image Cropper

Crop your images easily with custom and predefined aspect ratios.

Upload Your Image

Select an image from your device

Select Aspect Ratio
Custom Aspect Ratio
:
Adjust Crop Area

Drag inside the rectangle to move it. Drag the corner handles to resize it.

Image to crop

Cropped Image Preview

Cropped image

Download Cropped Image

Note: Your images are processed directly in your browser. They are not uploaded to any server. Choose an aspect ratio and adjust the crop rectangle before downloading.

(function () { const root = document.getElementById("online-cropper"); const upload = root.querySelector("#crop-upload"); const editor = root.querySelector("#crop-editor"); const wrapper = root.querySelector("#crop-wrapper"); const image = root.querySelector("#crop-image"); const selection = root.querySelector("#crop-selection"); const status = root.querySelector("#crop-status"); const preview = root.querySelector("#crop-preview"); let source = null; let ratio = null; let rect = {x:0,y:0,w:0,h:0}; let drag = null; let originalName = "cropped-image"; const clamp = (v,min,max) => Math.max(min,Math.min(max,v)); function wrapperSize() { return { w: wrapper.clientWidth, h: wrapper.clientHeight }; } function setRect(x,y,w,h) { const size = wrapperSize(); w = clamp(w, 20, size.w); h = clamp(h, 20, size.h); x = clamp(x, 0, size.w-w); y = clamp(y, 0, size.h-h); rect = {x,y,w,h}; selection.style.left = x + "px"; selection.style.top = y + "px"; selection.style.width = w + "px"; selection.style.height = h + "px"; } function initialRect() { const size = wrapperSize(); let w = size.w * .8; let h = size.h * .8; if (ratio) { const r = ratio; if (w/h > r) w = h*r; else h = w/r; } setRect((size.w-w)/2,(size.h-h)/2,w,h); } function setRatio(value) { if (value === "free") { ratio = null; } else { const parts = value.split(":").map(Number); ratio = parts[0]/parts[1]; } root.querySelectorAll("[data-ratio]").forEach(btn => { btn.classList.toggle("active",btn.dataset.ratio===value); }); if (source) initialRect(); preview.style.display = "none"; } root.querySelectorAll("[data-ratio]").forEach(btn => { btn.addEventListener("click", () => setRatio(btn.dataset.ratio)); }); root.querySelector("#apply-custom").addEventListener("click", () => { const w = Number(root.querySelector("#custom-width").value); const h = Number(root.querySelector("#custom-height").value); if (!Number.isInteger(w) || !Number.isInteger(h) || w < 1 || h 10000 || h > 10000) { status.textContent = "Enter valid positive width and height."; return; } ratio = w/h; root.querySelectorAll("[data-ratio]").forEach(btn => { btn.classList.remove("active"); }); if (source) initialRect(); preview.style.display = "none"; status.textContent = "Custom ratio applied: " + w + ":" + h; }); upload.addEventListener("change", function () { const file = this.files[0]; if (!file) return; if (!["image/jpeg","image/png","image/webp"].includes(file.type)) { status.textContent = "Please select JPG, PNG or WEBP."; return; } const url = URL.createObjectURL(file); const img = new Image(); img.onload = function () { if (source) URL.revokeObjectURL(source.url); source = {img,url}; originalName = file.name.replace(/\.[^.]+$/,"") || "image"; image.src = url; const maxWidth = 800; const scale = Math.min(1,maxWidth/img.width); const displayW = Math.round(img.width*scale); const displayH = Math.round(img.height*scale); wrapper.style.width = displayW + "px"; wrapper.style.height = displayH + "px"; editor.style.display = "block"; preview.style.display = "none"; requestAnimationFrame(initialRect); status.textContent = "Image loaded: " + img.width + " × " + img.height; }; img.onerror = function () { URL.revokeObjectURL(url); status.textContent = "Unable to load this image."; }; img.src = url; }); function pointerPosition(e) { const bounds = wrapper.getBoundingClientRect(); return { x:e.clientX-bounds.left, y:e.clientY-bounds.top }; } selection.addEventListener("pointerdown", function(e) { e.preventDefault(); const handle = e.target.dataset.handle || null; const point = pointerPosition(e); drag = { handle, startX:point.x, startY:point.y, rect:{...rect} }; selection.setPointerCapture(e.pointerId); }); selection.addEventListener("pointermove", function(e) { if (!drag) return; const point = pointerPosition(e); const dx = point.x-drag.startX; const dy = point.y-drag.startY; const start = drag.rect; const size = wrapperSize(); if (!drag.handle) { setRect(start.x+dx,start.y+dy,start.w,start.h); return; } let left = start.x; let top = start.y; let right = start.x+start.w; let bottom = start.y+start.h; if (drag.handle.includes("l")) left = clamp(start.x+dx,0,right-20); if (drag.handle.includes("r")) right = clamp(start.x+start.w+dx,left+20,size.w); if (drag.handle.includes("t")) top = clamp(start.y+dy,0,bottom-20); if (drag.handle.includes("b")) bottom = clamp(start.y+start.h+dy,top+20,size.h); if (ratio) { const fixedX = drag.handle.includes("l") ? right : left; const fixedY = drag.handle.includes("t") ? bottom : top; let w = right-left; let h = bottom-top; if (w/h > ratio) w = h*ratio; else h = w/ratio; if (drag.handle.includes("l")) left = fixedX-w; else right = fixedX+w; if (drag.handle.includes("t")) top = fixedY-h; else bottom = fixedY+h; // Keep the opposite corner fixed and stay inside the image. if (left size.w || top size.h) { const maxW = Math.min( drag.handle.includes("l") ? fixedX : size.w-fixedX, (drag.handle.includes("t") ? fixedY : size.h-fixedY)*ratio ); w = Math.max(20,Math.min(w,maxW)); h = w/ratio; left = drag.handle.includes("l") ? fixedX-w : fixedX; right = left+w; top = drag.handle.includes("t") ? fixedY-h : fixedY; bottom = top+h; } } setRect(left,top,right-left,bottom-top); }); function stopDrag() { drag = null; } selection.addEventListener("pointerup",stopDrag); selection.addEventListener("pointercancel",stopDrag); selection.addEventListener("lostpointercapture",stopDrag); root.querySelector("#reset-crop").addEventListener("click",() => { if (source) initialRect(); preview.style.display = "none"; status.textContent = "Crop selection reset."; }); root.querySelector("#crop-now").addEventListener("click",function() { if (!source) { status.textContent = "Please upload an image first."; return; } const scaleX = source.img.width / wrapper.clientWidth; const scaleY = source.img.height / wrapper.clientHeight; const sx = Math.round(rect.x*scaleX); const sy = Math.round(rect.y*scaleY); const sw = Math.round(rect.w*scaleX); const sh = Math.round(rect.h*scaleY); const canvas = document.createElement("canvas"); canvas.width = Math.max(1,sw); canvas.height = Math.max(1,sh); const ctx = canvas.getContext("2d"); ctx.drawImage( source.img, sx,sy,sw,sh, 0,0,canvas.width,canvas.height ); canvas.toBlob(blob => { if (!blob) { status.textContent = "Could not crop image."; return; } const resultUrl = URL.createObjectURL(blob); const result = root.querySelector("#cropped-result"); const link = root.querySelector("#crop-download"); if (result.dataset.url) { URL.revokeObjectURL(result.dataset.url); } result.src = resultUrl; result.dataset.url = resultUrl; link.href = resultUrl; link.download = originalName + "-cropped.png"; root.querySelector("#crop-info").textContent = "Final Dimensions: " + canvas.width + " × " + canvas.height + " pixels | File Size: " + (blob.size/1024).toFixed(2) + " KB"; preview.style.display = "block"; status.textContent = "Your cropped image is ready!"; },"image/png"); }); })();
Scroll to Top