Guide
Getting started
npm install @devix-labs/image-cropper
<label for="avatar">Profile picture</label>
<input id="avatar" name="avatar" type="file">
import { createImageCropper } from '@devix-labs/image-cropper';
import '@devix-labs/image-cropper/styles.css';
const picker = createImageCropper(document.querySelector('#avatar'), {
aspect: 1,
mode: 'fixed',
round: true,
maxWidth: 512,
});
// The part every other cropper leaves to you:
const file = await picker.toFile(); // a real File, cropped, 512px, named after the original
The input you point at stays the control. It is already focusable, already announced by a screen reader, and already a form field; the drop zone is its label.
Getting the picture out
This is the whole point. Three ways, all giving you the pixels you actually selected, at the resolution of the original rather than of the screen:
await picker.toBlob(); // a Blob
await picker.toFile(); // a File, named after the original
await picker.toFile('avatar', { type: 'image/jpeg' }); // "avatar.jpg"
await picker.toDataURL(); // a data: URL
Every option can be overridden per call, so one picker can produce a thumbnail and a full-size copy:
const thumb = await picker.toFile('thumb', { maxWidth: 128, type: 'image/webp', quality: 0.8 });
const full = await picker.toFile('photo', { maxWidth: 2000, type: 'image/jpeg', quality: 0.9 });
Sending it
const body = new FormData();
body.append('avatar', await picker.toFile());
await fetch('/profile/avatar', { method: 'POST', body });
Or hand it straight to the file uploader:
uploader.addFiles([await picker.toFile()]);
Shapes
createImageCropper(input, { aspect: 16 / 9 }); // a fixed shape
createImageCropper(input, { aspect: null }); // free
createImageCropper(input, { aspect: [0.5, 2] }); // anywhere between 1:2 and 2:1
A range is the thing people keep asking other croppers for: it leaves the selection exactly as drawn while it is between the two, and only holds at the ends.
Offer the choice as buttons:
createImageCropper(input, {
aspects: [
{ label: 'Free', value: null },
{ label: 'Square', value: 1 },
{ label: 'Wide', value: 16 / 9 },
{ label: 'Tall', value: 4 / 5 },
],
});
Two ways to crop
mode: 'free' (the default) — the picture stays put and the crop is dragged and resized over it.
This is what you want when the whole image matters and the crop is a choice.
mode: 'fixed' — the crop is a hole in the middle and the picture moves under it. This is the
avatar case: with round: true it is the circle every profile form needs, and the picture can never
pull away from behind it.
An output size that is honest
createImageCropper(input, { maxWidth: 1600, maxHeight: 1600, type: 'image/webp', quality: 0.82 });
maxWidth and maxHeight only ever scale down: enlarging a crop invents detail that was never
there. width/height force an exact size when a design demands one. The widget shows the pixel
size you are about to get, under the stage, while you drag.
With no type, a PNG stays a PNG — so a picture chosen for its transparency keeps it — and anything
else becomes WebP where the browser can write it.