Skip to content

feat: add maxWidth and maxHeight options for finer image resizing con… #232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,15 @@ If this project helps you reduce the time to develop, you can buy me a cup of co
## API
### Main function
```javascript
// you should provide one of maxSizeMB, maxWidthOrHeight in the options
// you should provide one of maxSizeMB, maxWidthOrHeight, maxWidth, or maxHeight in the options
const options: Options = {
maxSizeMB: number, // (default: Number.POSITIVE_INFINITY)
maxWidthOrHeight: number, // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
// but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
// Please check the Caveat part for details.
maxWidth: number, // (optional, new) clamp width only (preserve aspect ratio, no upscaling)
maxHeight: number, // (optional, new) clamp height only (preserve aspect ratio, no upscaling)
// If both maxWidth and maxHeight are set, the most restrictive is used. If maxWidthOrHeight is also set, it is applied after maxWidth/maxHeight.
onProgress: Function, // optional, a function takes one progress argument (percentage from 0 to 100)
useWebWorker: boolean, // optional, use multi-thread web worker, fallback to run in main-thread (default: true)
libURL: string, // optional, the libURL of this library for importing script in Web Worker (default: https://cdn.jsdelivr.net/npm/browser-image-compression/dist/browser-image-compression.js)
Expand Down
4 changes: 4 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export interface Options {
maxSizeMB?: number;
/** @default undefined */
maxWidthOrHeight?: number;
/** @default undefined */
maxWidth?: number; // new: clamp width only (preserve aspect ratio)
/** @default undefined */
maxHeight?: number; // new: clamp height only (preserve aspect ratio)
/** @default true */
useWebWorker?: boolean;
/** @default 10 */
Expand Down
47 changes: 32 additions & 15 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,28 +380,45 @@ export function getExifOrientation(file) {
export function handleMaxWidthOrHeight(canvas, options) {
const { width } = canvas;
const { height } = canvas;
const { maxWidthOrHeight } = options;

const needToHandle = isFinite(maxWidthOrHeight) && (width > maxWidthOrHeight || height > maxWidthOrHeight);

let newCanvas = canvas;
let ctx;
const { maxWidthOrHeight, maxWidth, maxHeight } = options;

let newWidth = width;
let newHeight = height;

// Step 1: Apply maxWidth and/or maxHeight if provided
if (isFinite(maxWidth) || isFinite(maxHeight)) {
let widthRatio = isFinite(maxWidth) ? maxWidth / newWidth : 1;
let heightRatio = isFinite(maxHeight) ? maxHeight / newHeight : 1;
let ratio = Math.min(widthRatio, heightRatio, 1); // Don't upscale
if (ratio < 1) {
newWidth = Math.round(newWidth * ratio);
newHeight = Math.round(newHeight * ratio);
}
}

if (needToHandle) {
[newCanvas, ctx] = getNewCanvasAndCtx(width, height);
if (width > height) {
newCanvas.width = maxWidthOrHeight;
newCanvas.height = (height / width) * maxWidthOrHeight;
// Step 2: Apply maxWidthOrHeight if provided (on already-resized image)
if (isFinite(maxWidthOrHeight) && (newWidth > maxWidthOrHeight || newHeight > maxWidthOrHeight)) {
if (newWidth > newHeight) {
const ratio = maxWidthOrHeight / newWidth;
newWidth = maxWidthOrHeight;
newHeight = Math.round(newHeight * ratio);
} else {
newCanvas.width = (width / height) * maxWidthOrHeight;
newCanvas.height = maxWidthOrHeight;
const ratio = maxWidthOrHeight / newHeight;
newHeight = maxWidthOrHeight;
newWidth = Math.round(newWidth * ratio);
}
ctx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height);
}

// Only create a new canvas if resizing is needed
if (newWidth !== width || newHeight !== height) {
let newCanvas, ctx;
[newCanvas, ctx] = getNewCanvasAndCtx(newWidth, newHeight);
ctx.drawImage(canvas, 0, 0, newWidth, newHeight);
cleanupCanvasMemory(canvas);
return newCanvas;
}

return newCanvas;
return canvas;
}

/**
Expand Down