Now.js Framework Documentation
ImageCropper - crop an image in the browser
ImageCropper - crop an image in the browser
Loaded on demand — part of the feature bundle
now.imagecrop.min.js(+.css), not ofnow.core.min.js: a file field withdata-crop="true"loads it; code that usesImageCropperdirectly callsawait Now.require('ImageCropper')first — see Feature bundles
ImageCropper opens one image in a crop dialog, lets the user move, zoom and turn it under a frame, and returns the result as a new File.
The frame stays fixed at the requested aspect ratio and the image moves beneath it, always covering the whole frame, so the result never has empty edges.
The usual way in is FileElementFactory, opened by data-crop="true" on a file field.
This page covers the cropper itself, for calling it from code.
Table of Contents
Usage
await Now.require('ImageCropper');
const file = await ImageCropper.open(input.files[0], {aspect: '3/4', width: 600});
if (file) {
// the cropped File
} else {
// the user pressed Cancel, the close button or Escape
}ImageCropper.open(source, options)
| Parameter | Type | Description |
|---|---|---|
source |
File, Blob or string |
The image to crop, or its URL (same origin, or one served with CORS), data:image/... included |
options |
Object |
Overrides for ImageCropper.config (see Options) |
Returns Promise<File|null> — null when the user cancels.
Rejects with an Error (translated message "Unable to open this image") when the image cannot be opened: not an image, or a type the browser cannot read.
One crop dialog is shown at a time. open() calls may overlap; the next image waits until the previous dialog has closed.
Options
Every option takes a number or the string a data attribute carries.
| Option | Default | Description |
|---|---|---|
aspect |
1 |
Frame width/height: 1, 0.75, '3/4', '3:4', '16/9'; a value that cannot be read is 1 |
width |
0 |
Width of the result in px, height from the aspect · 0 = the cropped area's own size, at most maxWidth |
maxWidth |
2048 |
Width limit when width is 0 |
shape |
'rect' |
'rect' or 'circle' — circle only changes the frame's mask; the file is always a rectangle |
format |
'auto' |
'auto', 'jpeg', 'png', 'webp' — auto keeps a JPEG, PNG or WebP source's type, anything else becomes JPEG |
quality |
0.9 |
JPEG and WebP quality (0-1) |
minWidth |
0 |
Warn in the dialog when the selected area holds fewer source pixels across than this · 0 = never |
maxZoom |
4 |
Furthest zoom, relative to the zoom at which the image just covers the frame |
title |
'Crop image' |
Dialog title (translated with Now.translate()) |
Controls in the crop dialog
| Action | Mouse / touch | Keyboard (focus on the crop area) |
|---|---|---|
| Move the image | Drag | Arrow keys (hold Shift for bigger steps) |
| Zoom | Wheel, two-finger pinch, the slider, the zoom out/in buttons | + / - |
| Turn 90° clockwise | Rotate button | - |
| Back to the whole image, unturned | Reset button | - |
| Confirm | OK button | Enter |
| Cancel | Cancel button or the close button | Escape |
Wheel and pinch zoom keep the point under the pointer in place · Escape closes the crop dialog only, not a modal open beneath it.
The crop area refits its frame whenever it changes size (turning a phone or resizing the window keeps the selection).
The resulting file
width×round(width / aspect)px (or the cropped area's own size whenwidthis0)- The source's own name while the type is unchanged · a new type changes the extension (
photo.png+format: 'jpeg'=photo.jpg) - A source without a name (
Blob,data:URL) is calledimage+ extension - Browsers that cannot write WebP (Safari) produce JPEG instead
- JPEG has no transparency: transparent parts of the source become white
- A camera photo's orientation (EXIF) is already applied when the image opens
Cropping in the browser is not a security boundary: the server must still check the file type and save the image anew
(Kotchasan's UploadedFile::cropImage() / resizeImage(), for example).
Cropping again from the original
A File ImageCropper produced remembers its source and how it was cropped.
Passing that File to open() again opens the original at the same position, zoom and turn, so the image is not resampled each time it is edited.
let photo = await ImageCropper.open(original, {aspect: 1, width: 400});
// later: edit the crop
photo = await ImageCropper.open(photo, {aspect: 1, width: 400}) || photo;CSS
File Now/css/image-cropper.css (in now.imagecrop.min.css)
| Class | What it is |
|---|---|
.image-cropper-modal |
The crop dialog's modal |
.image-cropper-stage |
The crop area (takes mouse, touch and keyboard) · .dragging while dragging |
.image-cropper-image |
The image moving under the frame |
.image-cropper-frame |
The frame · .circle with shape: 'circle' |
.image-cropper-toolbar |
Buttons and the zoom slider |
.image-cropper-zoom |
The zoom slider |
.image-cropper-warning |
The warning when the selection is narrower than minWidth |