Files
SnapOtter/packages/image-engine/src/operations/crop.ts
T
Siddharth Kumar Sah b8227c45b9 feat(crop): improve UI and fix batch crop for multi-file
- Replace Rule of Thirds button with checkbox for clearer toggle
- Show side-by-side comparison after crop instead of overlay slider
- Add custom aspect ratio option with W:H number inputs
- Fix batch crop failing on files with different dimensions by
  sending percentage-based coordinates instead of absolute pixels
- Add failed-file error state display in tool page
2026-04-11 14:47:46 +08:00

45 lines
1.3 KiB
TypeScript

import type { CropOptions, Sharp } from "../types.js";
export async function crop(image: Sharp, options: CropOptions): Promise<Sharp> {
const metadata = await image.metadata();
const imgWidth = metadata.width ?? 0;
const imgHeight = metadata.height ?? 0;
let left: number;
let top: number;
let width: number;
let height: number;
if (options.unit === "percent") {
left = Math.round((options.left / 100) * imgWidth);
top = Math.round((options.top / 100) * imgHeight);
width = Math.round((options.width / 100) * imgWidth);
height = Math.round((options.height / 100) * imgHeight);
} else {
left = Math.round(options.left);
top = Math.round(options.top);
width = Math.round(options.width);
height = Math.round(options.height);
}
if (width <= 0 || height <= 0) {
throw new Error("Crop width and height must be greater than 0");
}
if (left < 0 || top < 0) {
throw new Error("Crop left and top must be non-negative");
}
if (left + width > imgWidth) {
throw new Error(
`Crop region exceeds image width: left(${left}) + width(${width}) > ${imgWidth}`,
);
}
if (top + height > imgHeight) {
throw new Error(
`Crop region exceeds image height: top(${top}) + height(${height}) > ${imgHeight}`,
);
}
return image.extract({ left, top, width, height });
}