#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.
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"); }); })();