@bensitu/image-editor 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -47
- package/dist/image-editor.cjs +4407 -0
- package/dist/image-editor.cjs.map +7 -0
- package/dist/image-editor.esm.js +812 -273
- package/dist/image-editor.esm.js.map +3 -3
- package/dist/image-editor.esm.min.js +3 -3
- package/dist/image-editor.esm.min.js.map +3 -3
- package/dist/image-editor.esm.min.mjs +3 -3
- package/dist/image-editor.esm.min.mjs.map +3 -3
- package/dist/image-editor.esm.mjs +812 -273
- package/dist/image-editor.esm.mjs.map +3 -3
- package/dist/image-editor.js +812 -273
- package/dist/image-editor.js.map +3 -3
- package/dist/image-editor.min.js +2 -2
- package/dist/image-editor.min.js.map +3 -3
- package/image-editor.d.ts +6 -3
- package/package.json +4 -3
- package/src/image-editor.js +759 -165
package/README.md
CHANGED
|
@@ -19,9 +19,10 @@ ImageEditor offers:
|
|
|
19
19
|
- Merge, download, base64 export, and `File` export helpers
|
|
20
20
|
- Optional DOM/UI binding for common editor controls
|
|
21
21
|
- Large-image downsampling to reduce browser memory pressure
|
|
22
|
+
- Configurable history bounds for large image sessions
|
|
22
23
|
- Centralized error and warning callbacks
|
|
23
24
|
|
|
24
|
-
**Note:** This library
|
|
25
|
+
**Note:** This library uses **fabric.js v5.x**. Bundler and CommonJS entries load Fabric through the peer dependency. Browser global usage needs `window.fabric` available by the time `init()` runs; constructing the editor before Fabric is available is tolerated as long as Fabric is registered before initialization.
|
|
25
26
|
|
|
26
27
|
## Demo
|
|
27
28
|
|
|
@@ -61,6 +62,14 @@ import ImageEditor, {
|
|
|
61
62
|
} from "@bensitu/image-editor";
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
### CommonJS usage
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const ImageEditor = require("@bensitu/image-editor");
|
|
69
|
+
|
|
70
|
+
const editor = new ImageEditor();
|
|
71
|
+
```
|
|
72
|
+
|
|
64
73
|
### Browser global usage
|
|
65
74
|
|
|
66
75
|
Include fabric.js first, then ImageEditor:
|
|
@@ -115,50 +124,89 @@ Use `dist/image-editor.js` or `dist/image-editor.min.js` for browser global scri
|
|
|
115
124
|
|
|
116
125
|
### JavaScript Implementation
|
|
117
126
|
|
|
127
|
+
The constructor options are optional. For the smallest setup, create an editor instance and bind it to a canvas element.
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const editor = new ImageEditor();
|
|
131
|
+
|
|
132
|
+
editor.init({
|
|
133
|
+
canvas: "fabricCanvas"
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`canvas` is the only required DOM binding when your canvas element does not use the default ID `fabricCanvas`. All other DOM bindings are optional.
|
|
138
|
+
|
|
139
|
+
#### Optional Configuration
|
|
140
|
+
|
|
141
|
+
You can pass options to override the built-in defaults.
|
|
142
|
+
|
|
118
143
|
```javascript
|
|
119
|
-
// Create instance
|
|
120
144
|
const editor = new ImageEditor({
|
|
121
145
|
canvasWidth: 800,
|
|
122
146
|
canvasHeight: 600,
|
|
123
147
|
backgroundColor: "#ffffff",
|
|
148
|
+
initialImageBase64: null
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
These are common optional overrides, not required settings.
|
|
153
|
+
|
|
154
|
+
#### Demo-style Configuration
|
|
155
|
+
|
|
156
|
+
The docs demo uses an explicit configuration so the demo behavior is predictable. These options are not required for normal usage.
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
const editor = new ImageEditor({
|
|
160
|
+
// Layout mode. Enable only one layout mode at a time.
|
|
161
|
+
expandCanvasToImage: false,
|
|
162
|
+
fitImageToCanvas: true,
|
|
163
|
+
coverImageToCanvas: false,
|
|
164
|
+
|
|
165
|
+
// Image loading / performance.
|
|
166
|
+
downsampleOnLoad: true,
|
|
124
167
|
initialImageBase64: null,
|
|
125
168
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
169
|
+
// Mask behavior.
|
|
170
|
+
maskRotatable: true,
|
|
171
|
+
maskLabelOnSelect: true,
|
|
172
|
+
maskLabelOffset: 5,
|
|
129
173
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
// UI behavior.
|
|
175
|
+
backgroundColor: "transparent",
|
|
176
|
+
showPlaceholder: true,
|
|
177
|
+
animationDuration: 100,
|
|
178
|
+
|
|
179
|
+
// Export behavior.
|
|
180
|
+
exportImageAreaByDefault: true
|
|
133
181
|
});
|
|
134
182
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error("Failed to initialize ImageEditor:", error);
|
|
159
|
-
}
|
|
183
|
+
editor.init({
|
|
184
|
+
canvas: "fabricCanvas",
|
|
185
|
+
canvasContainer: null,
|
|
186
|
+
imagePlaceholder: "imagePlaceholder",
|
|
187
|
+
scalePercentageInput: "scalePercentageInput",
|
|
188
|
+
rotateLeftButton: "rotateLeftButton",
|
|
189
|
+
rotateRightButton: "rotateRightButton",
|
|
190
|
+
rotateLeftDegreesInput: "rotateLeftDegreesInput",
|
|
191
|
+
rotateRightDegreesInput: "rotateRightDegreesInput",
|
|
192
|
+
createMaskButton: null,
|
|
193
|
+
removeSelectedMaskButton: "removeSelectedMaskButton",
|
|
194
|
+
removeAllMasksButton: "removeAllMasksButton",
|
|
195
|
+
mergeMasksButton: "mergeMasksButton",
|
|
196
|
+
downloadImageButton: "downloadImageButton",
|
|
197
|
+
maskList: "maskList",
|
|
198
|
+
enterCropModeButton: "enterCropModeButton",
|
|
199
|
+
applyCropButton: "applyCropButton",
|
|
200
|
+
cancelCropButton: "cancelCropButton",
|
|
201
|
+
resetImageTransformButton: "resetImageTransformButton",
|
|
202
|
+
imageInput: null,
|
|
203
|
+
uploadArea: null
|
|
204
|
+
});
|
|
160
205
|
```
|
|
161
206
|
|
|
207
|
+
The demo binds `createMaskButton`, `imageInput`, and `uploadArea` itself, so it passes `null` for those built-in bindings. Regular integrations can omit those keys to use the default IDs, or pass `null` to disable any optional binding.
|
|
208
|
+
|
|
209
|
+
|
|
162
210
|
### Loading an Image Manually
|
|
163
211
|
|
|
164
212
|
```javascript
|
|
@@ -175,6 +223,8 @@ loadImageDataUrl("data:image/jpeg;base64,...");
|
|
|
175
223
|
|
|
176
224
|
## Configuration Options
|
|
177
225
|
|
|
226
|
+
All options are optional. If an option is not provided, the editor uses the default value listed below.
|
|
227
|
+
|
|
178
228
|
When creating the editor instance, pass an options object to override defaults.
|
|
179
229
|
|
|
180
230
|
| Option | Default | Description |
|
|
@@ -198,6 +248,8 @@ When creating the editor instance, pass an options object to override defaults.
|
|
|
198
248
|
| `downsampleMimeType` | `null` | Optional output MIME type for downsampled images. Supported values include `jpeg`, `jpg`, `png`, `webp`, `image/jpeg`, `image/png`, and `image/webp`. |
|
|
199
249
|
| `imageLoadTimeoutMs` | `30000` | Timeout for image decode/load operations. |
|
|
200
250
|
| `exportMultiplier` | `1` | Default scale multiplier for export. |
|
|
251
|
+
| `maxExportPixels` | `50000000` | Maximum output pixel count allowed per export after applying the multiplier. |
|
|
252
|
+
| `maxHistorySize` | `50` | Maximum undo/redo history entries to retain. Large base64 source images can make each history snapshot expensive. |
|
|
201
253
|
| `exportImageAreaByDefault` | `true` | Export only the image area by default instead of the full canvas. |
|
|
202
254
|
| `defaultMaskWidth` | `50` | Default mask width in pixels. |
|
|
203
255
|
| `defaultMaskHeight` | `80` | Default mask height in pixels. |
|
|
@@ -207,19 +259,28 @@ When creating the editor instance, pass an options object to override defaults.
|
|
|
207
259
|
| `maskName` | `mask` | Prefix for mask names and labels. |
|
|
208
260
|
| `groupSelection` | `false` | Whether Fabric can select multiple masks as an active selection. |
|
|
209
261
|
| `label.getText` | `(mask) => mask.maskName` | Callback for custom label text. The second argument is the mask's stable zero-based creation index (`mask.maskId - 1`). |
|
|
210
|
-
| `
|
|
262
|
+
| `label.create` | `undefined` | Optional callback that returns a custom Fabric label object for the selected mask. Invalid or throwing callbacks fall back to the default label. |
|
|
263
|
+
| `label.textOptions` | see defaults | Fabric text options merged into the default label when `label.create` is not used or falls back. |
|
|
264
|
+
| `showPlaceholder` | `true` | Show a placeholder when no image is loaded. When `false`, internal load, rollback, and status paths keep the placeholder hidden. |
|
|
211
265
|
| `initialImageBase64` | `null` | Base64 data URL to auto-load during initialization. |
|
|
212
266
|
| `defaultDownloadFileName` | `edited_image.jpg` | Default file name for downloads. |
|
|
267
|
+
| `crop.minWidth` | `100` | Minimum crop rectangle width, clamped to the current image bounds. |
|
|
268
|
+
| `crop.minHeight` | `100` | Minimum crop rectangle height, clamped to the current image bounds. |
|
|
269
|
+
| `crop.padding` | `10` | Initial inset from the image bounds when entering crop mode. |
|
|
270
|
+
| `crop.hideMasksDuringCrop` | `true` | Hide editable masks while crop mode is active. |
|
|
213
271
|
| `crop.preserveMasksAfterCrop` | `false` | Keep masks that intersect the crop area, shifted into the cropped canvas. Merge masks first if they should be baked into the image pixels. |
|
|
214
|
-
| `
|
|
272
|
+
| `crop.allowRotationOfCropRect` | `false` | Reserved for future rotated crop support. In v1.x, crop rectangles stay axis-aligned; setting this to `true` reports a warning and rotation remains disabled. |
|
|
273
|
+
| `onImageLoaded` | `null` | Callback invoked after an image load is committed. Callback errors are reported as warnings and do not roll back the loaded image. |
|
|
215
274
|
| `onError` | `null` | Callback invoked for recoverable internal errors. |
|
|
216
275
|
| `onWarning` | `null` | Callback invoked for recoverable internal warnings. |
|
|
217
276
|
|
|
218
|
-
`expandCanvasToImage`, `fitImageToCanvas`, and `coverImageToCanvas` are mutually exclusive layout modes. If more than one is enabled, the editor reports a warning and uses
|
|
277
|
+
`expandCanvasToImage`, `fitImageToCanvas`, and `coverImageToCanvas` are mutually exclusive layout modes. If more than one is enabled, the editor reports a warning and uses this precedence: `fitImageToCanvas`, then `coverImageToCanvas`, then `expandCanvasToImage`.
|
|
278
|
+
|
|
279
|
+
Numeric runtime options are normalized during construction. Non-finite or invalid dimensions, scale limits, export limits, crop sizes, label offsets, and history sizes fall back to safe defaults. `animationDuration: 0` and `downsampleQuality: 0` remain valid.
|
|
219
280
|
|
|
220
281
|
## DOM Binding Keys
|
|
221
282
|
|
|
222
|
-
`init(idMap)` binds editor behavior to DOM elements by ID. All keys are optional.
|
|
283
|
+
`init(idMap)` binds editor behavior to DOM elements by ID. All keys are optional when the default IDs are present. Optional bindings can also be set to `null` to disable the built-in listener for that element.
|
|
223
284
|
|
|
224
285
|
| Key | Description |
|
|
225
286
|
| --------------------------- | -------------------------------------------------------------- |
|
|
@@ -279,11 +340,12 @@ The following DOM binding keys remain supported in `1.x` for compatibility, but
|
|
|
279
340
|
| Method | Returns | Description |
|
|
280
341
|
| --------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
281
342
|
| `init(idMap)` | `void` | Bind the editor to DOM elements. Pass IDs in an object. |
|
|
343
|
+
| `dispose()` | `void` | Cleans up the canvas, listeners, crop state, animations, and captured DOM visibility/disabled/pointer-events state. |
|
|
282
344
|
| `loadImage(imageBase64, options)` | `Promise<void>` | Load an image from a base64 data URL. Resolves after the Fabric image is on the canvas. |
|
|
283
345
|
| `isImageLoaded()` | `boolean` | Return whether a valid image is loaded on the canvas. |
|
|
284
|
-
| `isBusy()` | `boolean` | Return whether the editor is loading, animating, cropping, or running another compound operation.
|
|
285
|
-
| `scaleImage(factor)` | `Promise<void>` | Scale the image to the given factor relative to the base scale.
|
|
286
|
-
| `rotateImage(degrees)` | `Promise<void>` | Rotate the image to the given angle in degrees.
|
|
346
|
+
| `isBusy()` | `boolean` | Return whether the editor is loading, animating, cropping, applying crop, or running another compound operation. |
|
|
347
|
+
| `scaleImage(factor)` | `Promise<void>` | Scale the image to the given finite factor relative to the base scale. Non-finite values are ignored. |
|
|
348
|
+
| `rotateImage(degrees)` | `Promise<void>` | Rotate the image to the given finite angle in degrees. Non-finite values are ignored. |
|
|
287
349
|
| `resetImageTransform()` | `Promise<void>` | Reset scale to `1` and rotation to `0`. |
|
|
288
350
|
| `undo()` | `Promise<void>` | Undo the last state change. Resolves after the canvas state is restored. |
|
|
289
351
|
| `redo()` | `Promise<void>` | Redo the next state change. Resolves after the canvas state is restored. |
|
|
@@ -291,8 +353,8 @@ The following DOM binding keys remain supported in `1.x` for compatibility, but
|
|
|
291
353
|
| `removeSelectedMask()` | `void` | Remove the currently selected mask or selected masks. |
|
|
292
354
|
| `removeAllMasks(options)` | `void` | Remove all masks from the canvas. |
|
|
293
355
|
| `enterCropMode()` | `void` | Create a resizable/movable selection rectangle on top of the image. |
|
|
294
|
-
| `cancelCrop()` | `void` | Cancel crop mode and remove the temporary selection rectangle.
|
|
295
|
-
| `applyCrop()` | `Promise<void>` | Apply the current crop rectangle to the canvas.
|
|
356
|
+
| `cancelCrop()` | `void` | Cancel crop mode and remove the temporary selection rectangle. No-ops while `applyCrop()` is already running. |
|
|
357
|
+
| `applyCrop()` | `Promise<void>` | Apply the current valid crop rectangle to the canvas. Invalid crop regions warn and keep crop mode active. |
|
|
296
358
|
| `mergeMasks()` | `Promise<void>` | Merge masks into the base image on the canvas. |
|
|
297
359
|
| `downloadImage(fileName)` | `void` | Download the edited image as a file. |
|
|
298
360
|
| `exportImageBase64(options)` | `Promise<string>` | Export an image data URL. `fileType` can be `jpeg`, `jpg`, `png`, `webp`, or a supported image MIME type. |
|
|
@@ -313,6 +375,8 @@ Deprecated method aliases are still available for compatibility and will be remo
|
|
|
313
375
|
|
|
314
376
|
`createMask(config)` accepts an optional configuration object.
|
|
315
377
|
|
|
378
|
+
Invalid mask configuration, such as non-finite numeric values, non-positive dimensions/radii, duplicate or zero-area polygon points, malformed polygon points, throwing custom generators, or custom generators that do not return a Fabric object, is rejected with a warning and returns `null` without mutating the canvas or history.
|
|
379
|
+
|
|
316
380
|
| Option | Description |
|
|
317
381
|
| ------------------ | ------------------------------------------------------------------------------------------ |
|
|
318
382
|
| `shape` | Mask shape. Built-in values include `rect`, `circle`, `ellipse`, and `polygon`. |
|
|
@@ -330,6 +394,10 @@ Deprecated method aliases are still available for compatibility and will be remo
|
|
|
330
394
|
| `fabricGenerator` | Factory callback for creating a custom Fabric object. |
|
|
331
395
|
| `onCreate` | Callback invoked after the mask is added to the canvas. |
|
|
332
396
|
|
|
397
|
+
`fabricGenerator` runs before the mask is committed. If it throws or returns an invalid object, `createMask()` reports a warning and returns `null`. `onCreate` runs after the mask and history entry are committed; if it throws, the mask remains on the canvas and the error is reported as a warning.
|
|
398
|
+
|
|
399
|
+
Label callbacks are also isolated. If `label.create` throws or returns an invalid object, the editor uses the default Fabric text label. If `label.getText` throws, the editor uses `mask.maskName`.
|
|
400
|
+
|
|
333
401
|
Example:
|
|
334
402
|
|
|
335
403
|
```javascript
|
|
@@ -350,6 +418,10 @@ By default, applying crop removes unmerged masks.
|
|
|
350
418
|
|
|
351
419
|
This is intentional: an unmerged mask is still an editable overlay object, not part of the image pixels. When `crop.preserveMasksAfterCrop` is `false`, applying crop discards unmerged masks instead of trying to keep or partially crop those overlay objects.
|
|
352
420
|
|
|
421
|
+
`applyCrop()` owns a short crop-apply lock while it exports the selected region and reloads the cropped image. Calls to `cancelCrop()` or `enterCropMode()` during that pending apply are ignored and reported as warnings so crop state cannot be mutated mid-apply. The crop region is validated before export; invalid or out-of-bounds regions do not silently produce a 1x1 image.
|
|
422
|
+
|
|
423
|
+
While crop mode is active, the built-in DOM binding disables editor operation controls but keeps the canvas, canvas container, and placeholder interaction available so the crop rectangle and scrollable viewport remain usable. The apply/cancel crop buttons stay enabled.
|
|
424
|
+
|
|
353
425
|
Choose the workflow based on the result you want:
|
|
354
426
|
|
|
355
427
|
- **Crop first, then add masks** when masks should be placed only on the final cropped image.
|
|
@@ -414,6 +486,24 @@ try {
|
|
|
414
486
|
}
|
|
415
487
|
```
|
|
416
488
|
|
|
489
|
+
Exports are limited by `maxExportPixels`. Increase that option only when the host page can tolerate the memory cost of larger canvas exports.
|
|
490
|
+
|
|
491
|
+
```javascript
|
|
492
|
+
const editor = new ImageEditor({
|
|
493
|
+
maxExportPixels: 80000000,
|
|
494
|
+
});
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
JPEG exports cannot preserve transparency. Transparent, zero-alpha, empty, or invalid `backgroundColor` values are flattened against white; valid opaque CSS colors are preserved.
|
|
498
|
+
|
|
499
|
+
Undo/redo history is limited by `maxHistorySize`. The default is `50`; lower it for workflows that load large base64 images and perform many edits.
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
const editor = new ImageEditor({
|
|
503
|
+
maxHistorySize: 20,
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
417
507
|
## Error Handling
|
|
418
508
|
|
|
419
509
|
Some ImageEditor API methods may throw synchronously or reject their returned Promise when the operation cannot be completed.
|
|
@@ -464,13 +554,13 @@ function showEditorMessage(message) {
|
|
|
464
554
|
|
|
465
555
|
Common failure cases include:
|
|
466
556
|
|
|
467
|
-
- fabric.js is not
|
|
557
|
+
- fabric.js is not available when `init()` or another canvas operation needs it.
|
|
468
558
|
- The configured canvas element cannot be found.
|
|
469
|
-
- The image data URL is invalid, unsupported, too large, or times out while loading.
|
|
559
|
+
- The image data URL or selected file is invalid, unsupported, too large, or times out while loading.
|
|
470
560
|
- Another operation is already running, such as image loading, animation, crop, undo, redo, merge, or export.
|
|
471
561
|
- The editor has been disposed before the operation completes.
|
|
472
|
-
- The browser cannot create a canvas export because of platform, memory, or security restrictions.
|
|
473
|
-
- A custom `fabricGenerator`, label
|
|
562
|
+
- The requested export exceeds `maxExportPixels`, uses an invalid multiplier, or the browser cannot create a canvas export because of platform, memory, or security restrictions.
|
|
563
|
+
- A custom callback such as `onImageLoaded`, `onCreate`, `fabricGenerator`, `label.create`, or `label.getText` throws. These are isolated where possible and reported through `onWarning` without rolling back already committed successful work.
|
|
474
564
|
|
|
475
565
|
## Example Workflow
|
|
476
566
|
|
|
@@ -529,7 +619,7 @@ IE11 and old mobile Safari are not supported by the distributed build. If you ne
|
|
|
529
619
|
|
|
530
620
|
## Dependencies
|
|
531
621
|
|
|
532
|
-
- **fabric.js v5.x** —
|
|
622
|
+
- **fabric.js v5.x** — Required peer dependency. Browser global usage needs `window.fabric` available before `init()`.
|
|
533
623
|
|
|
534
624
|
## License
|
|
535
625
|
|