@bensitu/image-editor 1.3.0 → 1.3.1
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/dist/image-editor.esm.js +230 -277
- package/dist/image-editor.esm.js.map +2 -2
- 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 +230 -277
- package/dist/image-editor.esm.mjs.map +2 -2
- package/dist/image-editor.js +230 -277
- package/dist/image-editor.js.map +2 -2
- package/dist/image-editor.min.js +2 -2
- package/dist/image-editor.min.js.map +3 -3
- package/package.json +9 -4
- package/src/image-editor.js +104 -54
package/dist/image-editor.js
CHANGED
|
@@ -3,19 +3,16 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file image-editor.js
|
|
5
5
|
* @module image-editor
|
|
6
|
-
* @version 1.3.
|
|
6
|
+
* @version 1.3.1
|
|
7
7
|
* @author Ben Situ
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @description Lightweight canvas-based image editor with masking/transform/export support.
|
|
10
10
|
*/
|
|
11
11
|
var fabric = null;
|
|
12
12
|
function getGlobalScope() {
|
|
13
|
-
if (typeof globalThis !== "undefined")
|
|
14
|
-
|
|
15
|
-
if (typeof
|
|
16
|
-
return self;
|
|
17
|
-
if (typeof window !== "undefined")
|
|
18
|
-
return window;
|
|
13
|
+
if (typeof globalThis !== "undefined") return globalThis;
|
|
14
|
+
if (typeof self !== "undefined") return self;
|
|
15
|
+
if (typeof window !== "undefined") return window;
|
|
19
16
|
return null;
|
|
20
17
|
}
|
|
21
18
|
function getGlobalFabric() {
|
|
@@ -27,8 +24,7 @@
|
|
|
27
24
|
return fabric;
|
|
28
25
|
}
|
|
29
26
|
function ensureFabric() {
|
|
30
|
-
if (!fabric)
|
|
31
|
-
setFabric();
|
|
27
|
+
if (!fabric) setFabric();
|
|
32
28
|
return fabric;
|
|
33
29
|
}
|
|
34
30
|
var ImageEditor = class {
|
|
@@ -196,8 +192,7 @@
|
|
|
196
192
|
* });
|
|
197
193
|
*/
|
|
198
194
|
init(idMap = {}) {
|
|
199
|
-
if (!this._fabricLoaded)
|
|
200
|
-
return;
|
|
195
|
+
if (!this._fabricLoaded) return;
|
|
201
196
|
const defaults = {
|
|
202
197
|
canvas: "fabricCanvas",
|
|
203
198
|
canvasContainer: null,
|
|
@@ -238,8 +233,7 @@
|
|
|
238
233
|
}
|
|
239
234
|
_reportError(message, error = null) {
|
|
240
235
|
const handler = this.options && this.options.onError;
|
|
241
|
-
if (typeof handler !== "function")
|
|
242
|
-
return;
|
|
236
|
+
if (typeof handler !== "function") return;
|
|
243
237
|
try {
|
|
244
238
|
handler(error, message);
|
|
245
239
|
} catch {
|
|
@@ -247,8 +241,7 @@
|
|
|
247
241
|
}
|
|
248
242
|
_reportWarning(message, error = null) {
|
|
249
243
|
const handler = this.options && this.options.onWarning;
|
|
250
|
-
if (typeof handler !== "function")
|
|
251
|
-
return;
|
|
244
|
+
if (typeof handler !== "function") return;
|
|
252
245
|
try {
|
|
253
246
|
handler(error, message);
|
|
254
247
|
} catch {
|
|
@@ -262,8 +255,7 @@
|
|
|
262
255
|
*/
|
|
263
256
|
_initCanvas() {
|
|
264
257
|
const canvasElement = document.getElementById(this.elements.canvas);
|
|
265
|
-
if (!canvasElement)
|
|
266
|
-
throw new Error("Canvas is not found: " + this.elements.canvas);
|
|
258
|
+
if (!canvasElement) throw new Error("Canvas is not found: " + this.elements.canvas);
|
|
267
259
|
this.canvasElement = canvasElement;
|
|
268
260
|
if (this.elements.canvasContainer) {
|
|
269
261
|
const containerElement = document.getElementById(this.elements.canvasContainer);
|
|
@@ -293,16 +285,13 @@
|
|
|
293
285
|
this.canvas.on("selection:updated", (event) => this._handleSelectionChanged(event.selected));
|
|
294
286
|
this.canvas.on("selection:cleared", () => this._handleSelectionChanged([]));
|
|
295
287
|
this.canvas.on("object:moving", (event) => {
|
|
296
|
-
if (event.target && event.target.maskId)
|
|
297
|
-
this._syncMaskLabel(event.target);
|
|
288
|
+
if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
|
|
298
289
|
});
|
|
299
290
|
this.canvas.on("object:scaling", (event) => {
|
|
300
|
-
if (event.target && event.target.maskId)
|
|
301
|
-
this._syncMaskLabel(event.target);
|
|
291
|
+
if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
|
|
302
292
|
});
|
|
303
293
|
this.canvas.on("object:rotating", (event) => {
|
|
304
|
-
if (event.target && event.target.maskId)
|
|
305
|
-
this._syncMaskLabel(event.target);
|
|
294
|
+
if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
|
|
306
295
|
});
|
|
307
296
|
this.canvas.on("object:modified", (event) => this._handleObjectModified(event.target));
|
|
308
297
|
this.canvasElement.style.display = "block";
|
|
@@ -316,11 +305,9 @@
|
|
|
316
305
|
*/
|
|
317
306
|
_handleObjectModified(target) {
|
|
318
307
|
const masks = this._getModifiedMasks(target);
|
|
319
|
-
if (!masks.length)
|
|
320
|
-
return;
|
|
308
|
+
if (!masks.length) return;
|
|
321
309
|
masks.forEach((mask) => {
|
|
322
|
-
if (typeof mask.setCoords === "function")
|
|
323
|
-
mask.setCoords();
|
|
310
|
+
if (typeof mask.setCoords === "function") mask.setCoords();
|
|
324
311
|
this._syncMaskLabel(mask);
|
|
325
312
|
});
|
|
326
313
|
this._expandCanvasToFitObjects(masks);
|
|
@@ -334,10 +321,8 @@
|
|
|
334
321
|
* @private
|
|
335
322
|
*/
|
|
336
323
|
_getModifiedMasks(target) {
|
|
337
|
-
if (!target)
|
|
338
|
-
|
|
339
|
-
if (target.maskId)
|
|
340
|
-
return [target];
|
|
324
|
+
if (!target) return [];
|
|
325
|
+
if (target.maskId) return [target];
|
|
341
326
|
const objects = typeof target.getObjects === "function" ? target.getObjects() : [];
|
|
342
327
|
return Array.isArray(objects) ? objects.filter((object) => object && object.maskId) : [];
|
|
343
328
|
}
|
|
@@ -350,8 +335,7 @@
|
|
|
350
335
|
* @private
|
|
351
336
|
*/
|
|
352
337
|
_syncContainerOverflow(options = {}) {
|
|
353
|
-
if (!this.containerElement || !this.containerElement.style)
|
|
354
|
-
return;
|
|
338
|
+
if (!this.containerElement || !this.containerElement.style) return;
|
|
355
339
|
if (this._containerOriginalOverflow === void 0) {
|
|
356
340
|
this._containerOriginalOverflow = this.containerElement.style.overflow || "";
|
|
357
341
|
}
|
|
@@ -379,14 +363,12 @@
|
|
|
379
363
|
_bindEvents() {
|
|
380
364
|
this._bindIfExists("uploadArea", "click", () => {
|
|
381
365
|
const uploadAreaElement = document.getElementById(this.elements.uploadArea);
|
|
382
|
-
if (this._isElementDisabled(uploadAreaElement))
|
|
383
|
-
return;
|
|
366
|
+
if (this._isElementDisabled(uploadAreaElement)) return;
|
|
384
367
|
document.getElementById(this.elements.imageInput)?.click();
|
|
385
368
|
});
|
|
386
369
|
this._bindIfExists("imageInput", "change", (event) => {
|
|
387
370
|
const file = event.target.files && event.target.files[0];
|
|
388
|
-
if (file)
|
|
389
|
-
this._loadImageFile(file);
|
|
371
|
+
if (file) this._loadImageFile(file);
|
|
390
372
|
});
|
|
391
373
|
this._bindIfExists("zoomInBtn", "click", () => this.scaleImage(this.currentScale + this.options.scaleStep));
|
|
392
374
|
this._bindIfExists("zoomOutBtn", "click", () => this.scaleImage(this.currentScale - this.options.scaleStep));
|
|
@@ -405,8 +387,7 @@
|
|
|
405
387
|
let step = this.options.rotationStep;
|
|
406
388
|
if (rotationInputElement) {
|
|
407
389
|
const parsedStep = parseFloat(rotationInputElement.value);
|
|
408
|
-
if (!isNaN(parsedStep))
|
|
409
|
-
step = parsedStep;
|
|
390
|
+
if (!isNaN(parsedStep)) step = parsedStep;
|
|
410
391
|
}
|
|
411
392
|
this.rotateImage(this.currentRotation - step);
|
|
412
393
|
});
|
|
@@ -415,8 +396,7 @@
|
|
|
415
396
|
let step = this.options.rotationStep;
|
|
416
397
|
if (rotationInputElement) {
|
|
417
398
|
const parsedStep = parseFloat(rotationInputElement.value);
|
|
418
|
-
if (!isNaN(parsedStep))
|
|
419
|
-
step = parsedStep;
|
|
399
|
+
if (!isNaN(parsedStep)) step = parsedStep;
|
|
420
400
|
}
|
|
421
401
|
this.rotateImage(this.currentRotation + step);
|
|
422
402
|
});
|
|
@@ -439,8 +419,7 @@
|
|
|
439
419
|
if (element) {
|
|
440
420
|
element.addEventListener(eventName, handler);
|
|
441
421
|
this._handlersByElementKey = this._handlersByElementKey || {};
|
|
442
|
-
if (!this._handlersByElementKey[key])
|
|
443
|
-
this._handlersByElementKey[key] = [];
|
|
422
|
+
if (!this._handlersByElementKey[key]) this._handlersByElementKey[key] = [];
|
|
444
423
|
this._handlersByElementKey[key].push({ eventName, handler });
|
|
445
424
|
}
|
|
446
425
|
}
|
|
@@ -451,8 +430,7 @@
|
|
|
451
430
|
* @private
|
|
452
431
|
*/
|
|
453
432
|
_loadImageFile(file) {
|
|
454
|
-
if (!file || !file.type.startsWith("image/"))
|
|
455
|
-
return;
|
|
433
|
+
if (!file || !file.type.startsWith("image/")) return;
|
|
456
434
|
const reader = new FileReader();
|
|
457
435
|
reader.onload = (event) => this.loadImage(event.target.result);
|
|
458
436
|
reader.onerror = (event) => {
|
|
@@ -472,8 +450,7 @@
|
|
|
472
450
|
["coverImageToCanvas", this.options.coverImageToCanvas],
|
|
473
451
|
["expandCanvasToImage", this.options.expandCanvasToImage]
|
|
474
452
|
].filter(([, isEnabled]) => !!isEnabled).map(([name]) => name);
|
|
475
|
-
if (activeModes.length <= 1)
|
|
476
|
-
return;
|
|
453
|
+
if (activeModes.length <= 1) return;
|
|
477
454
|
this._reportWarning(
|
|
478
455
|
`Only one image layout mode should be enabled. Active modes: ${activeModes.join(", ")}.`
|
|
479
456
|
);
|
|
@@ -488,12 +465,9 @@
|
|
|
488
465
|
* @public
|
|
489
466
|
*/
|
|
490
467
|
async loadImage(imageBase64, options = {}) {
|
|
491
|
-
if (!this._fabricLoaded)
|
|
492
|
-
|
|
493
|
-
if (!
|
|
494
|
-
return;
|
|
495
|
-
if (!imageBase64 || typeof imageBase64 !== "string" || !imageBase64.startsWith("data:image/"))
|
|
496
|
-
return;
|
|
468
|
+
if (!this._fabricLoaded) return;
|
|
469
|
+
if (!this.canvas) return;
|
|
470
|
+
if (!imageBase64 || typeof imageBase64 !== "string" || !imageBase64.startsWith("data:image/")) return;
|
|
497
471
|
this._warnOnImageLayoutOptionConflict();
|
|
498
472
|
this._setPlaceholderVisible(false);
|
|
499
473
|
this._syncContainerOverflow({ preserveScroll: options.preserveScroll === true });
|
|
@@ -514,8 +488,7 @@
|
|
|
514
488
|
return new Promise((resolve, reject) => {
|
|
515
489
|
fabric.Image.fromURL(loadSource, (fabricImage) => {
|
|
516
490
|
try {
|
|
517
|
-
if (!fabricImage)
|
|
518
|
-
throw new Error("Image could not be loaded");
|
|
491
|
+
if (!fabricImage) throw new Error("Image could not be loaded");
|
|
519
492
|
this.canvas.discardActiveObject();
|
|
520
493
|
this._hideAllMaskLabels();
|
|
521
494
|
this.canvas.clear();
|
|
@@ -609,8 +582,7 @@
|
|
|
609
582
|
const safeTimeoutMs = Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0 ? Number(timeoutMs) : 3e4;
|
|
610
583
|
let timerId;
|
|
611
584
|
const settle = (callback) => {
|
|
612
|
-
if (isSettled)
|
|
613
|
-
return;
|
|
585
|
+
if (isSettled) return;
|
|
614
586
|
isSettled = true;
|
|
615
587
|
clearTimeout(timerId);
|
|
616
588
|
imageElement.onload = null;
|
|
@@ -622,6 +594,7 @@
|
|
|
622
594
|
try {
|
|
623
595
|
imageElement.src = "";
|
|
624
596
|
} catch (error) {
|
|
597
|
+
void error;
|
|
625
598
|
}
|
|
626
599
|
}, safeTimeoutMs);
|
|
627
600
|
imageElement.onload = () => settle(() => resolve(imageElement));
|
|
@@ -644,8 +617,7 @@
|
|
|
644
617
|
offscreenCanvas.width = targetWidth;
|
|
645
618
|
offscreenCanvas.height = targetHeight;
|
|
646
619
|
const context = offscreenCanvas.getContext("2d");
|
|
647
|
-
if (!context)
|
|
648
|
-
throw new Error("2D canvas context is unavailable");
|
|
620
|
+
if (!context) throw new Error("2D canvas context is unavailable");
|
|
649
621
|
context.drawImage(imageElement, 0, 0, imageElement.naturalWidth, imageElement.naturalHeight, 0, 0, targetWidth, targetHeight);
|
|
650
622
|
return offscreenCanvas.toDataURL("image/jpeg", quality);
|
|
651
623
|
}
|
|
@@ -662,8 +634,7 @@
|
|
|
662
634
|
const integerHeight = Math.max(1, Math.round(Number(height) || 1));
|
|
663
635
|
this.canvas.setWidth(integerWidth);
|
|
664
636
|
this.canvas.setHeight(integerHeight);
|
|
665
|
-
if (typeof this.canvas.calcOffset === "function")
|
|
666
|
-
this.canvas.calcOffset();
|
|
637
|
+
if (typeof this.canvas.calcOffset === "function") this.canvas.calcOffset();
|
|
667
638
|
if (this.canvasElement) {
|
|
668
639
|
this.canvasElement.style.width = integerWidth + "px";
|
|
669
640
|
this.canvasElement.style.height = integerHeight + "px";
|
|
@@ -673,8 +644,7 @@
|
|
|
673
644
|
_ceilCanvasDimension(value) {
|
|
674
645
|
const numericValue = Number(value) || 0;
|
|
675
646
|
const roundedValue = Math.round(numericValue);
|
|
676
|
-
if (Math.abs(numericValue - roundedValue) < 0.01)
|
|
677
|
-
return roundedValue;
|
|
647
|
+
if (Math.abs(numericValue - roundedValue) < 0.01) return roundedValue;
|
|
678
648
|
return Math.ceil(numericValue);
|
|
679
649
|
}
|
|
680
650
|
_getContainerViewportSize() {
|
|
@@ -684,19 +654,31 @@
|
|
|
684
654
|
height: Math.max(1, Math.floor(this.options.canvasHeight || 1))
|
|
685
655
|
};
|
|
686
656
|
}
|
|
657
|
+
let width = Math.max(1, Math.floor(this.containerElement.clientWidth || this.options.canvasWidth || 1));
|
|
658
|
+
let height = Math.max(1, Math.floor(this.containerElement.clientHeight || this.options.canvasHeight || 1));
|
|
687
659
|
if (this._hasFixedContainerScrollbars()) {
|
|
688
|
-
return {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
660
|
+
return { width, height };
|
|
661
|
+
}
|
|
662
|
+
const overflow = this._getContainerOverflowValues();
|
|
663
|
+
const canScrollX = overflow.x.some((value) => value === "auto" || value === "scroll");
|
|
664
|
+
const canScrollY = overflow.y.some((value) => value === "auto" || value === "scroll");
|
|
665
|
+
const hasHorizontalScrollbar = canScrollX && this.containerElement.scrollWidth > this.containerElement.clientWidth;
|
|
666
|
+
const hasVerticalScrollbar = canScrollY && this.containerElement.scrollHeight > this.containerElement.clientHeight;
|
|
667
|
+
if (hasHorizontalScrollbar || hasVerticalScrollbar) {
|
|
668
|
+
const scrollbar = this._getScrollbarSize();
|
|
669
|
+
if (hasVerticalScrollbar) width += scrollbar.width;
|
|
670
|
+
if (hasHorizontalScrollbar) height += scrollbar.height;
|
|
692
671
|
}
|
|
693
|
-
const width = Math.max(1, Math.floor(this.containerElement.clientWidth || this.options.canvasWidth || 1));
|
|
694
|
-
const height = Math.max(1, Math.floor(this.containerElement.clientHeight || this.options.canvasHeight || 1));
|
|
695
672
|
return { width, height };
|
|
696
673
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
674
|
+
/**
|
|
675
|
+
* Reads inline and computed overflow values for both scroll axes.
|
|
676
|
+
*
|
|
677
|
+
* @returns {{x:string[], y:string[]}} Overflow values grouped by axis.
|
|
678
|
+
* @private
|
|
679
|
+
*/
|
|
680
|
+
_getContainerOverflowValues() {
|
|
681
|
+
if (!this.containerElement) return { x: [], y: [] };
|
|
700
682
|
const inlineOverflow = this.containerElement.style.overflow;
|
|
701
683
|
const inlineOverflowX = this.containerElement.style.overflowX;
|
|
702
684
|
const inlineOverflowY = this.containerElement.style.overflowY;
|
|
@@ -709,7 +691,15 @@
|
|
|
709
691
|
computedOverflowX = style.overflowX;
|
|
710
692
|
computedOverflowY = style.overflowY;
|
|
711
693
|
}
|
|
712
|
-
return
|
|
694
|
+
return {
|
|
695
|
+
x: [inlineOverflow, inlineOverflowX, computedOverflow, computedOverflowX],
|
|
696
|
+
y: [inlineOverflow, inlineOverflowY, computedOverflow, computedOverflowY]
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
_hasFixedContainerScrollbars() {
|
|
700
|
+
if (!this.containerElement) return false;
|
|
701
|
+
const overflow = this._getContainerOverflowValues();
|
|
702
|
+
return [...overflow.x, ...overflow.y].some((value) => value === "scroll");
|
|
713
703
|
}
|
|
714
704
|
_getScrollbarSize() {
|
|
715
705
|
if (this._scrollbarSizeCache) {
|
|
@@ -752,15 +742,14 @@
|
|
|
752
742
|
const scrollbar = this._getScrollbarSize();
|
|
753
743
|
let hasVertical = false;
|
|
754
744
|
let hasHorizontal = false;
|
|
755
|
-
let effectiveWidth
|
|
756
|
-
let effectiveHeight
|
|
745
|
+
let effectiveWidth;
|
|
746
|
+
let effectiveHeight;
|
|
757
747
|
for (let i = 0; i < 4; i += 1) {
|
|
758
748
|
effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));
|
|
759
749
|
effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));
|
|
760
750
|
const nextHasVertical = contentHeight > effectiveHeight + 0.5;
|
|
761
751
|
const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;
|
|
762
|
-
if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal)
|
|
763
|
-
break;
|
|
752
|
+
if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;
|
|
764
753
|
hasVertical = nextHasVertical;
|
|
765
754
|
hasHorizontal = nextHasHorizontal;
|
|
766
755
|
}
|
|
@@ -797,8 +786,8 @@
|
|
|
797
786
|
let scale = 1;
|
|
798
787
|
let contentWidth = imageWidth;
|
|
799
788
|
let contentHeight = imageHeight;
|
|
800
|
-
let effectiveWidth
|
|
801
|
-
let effectiveHeight
|
|
789
|
+
let effectiveWidth;
|
|
790
|
+
let effectiveHeight;
|
|
802
791
|
for (let i = 0; i < 4; i += 1) {
|
|
803
792
|
effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));
|
|
804
793
|
effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));
|
|
@@ -807,8 +796,7 @@
|
|
|
807
796
|
contentHeight = imageHeight * scale;
|
|
808
797
|
const nextHasVertical = contentHeight > effectiveHeight + 0.5;
|
|
809
798
|
const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;
|
|
810
|
-
if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal)
|
|
811
|
-
break;
|
|
799
|
+
if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;
|
|
812
800
|
hasVertical = nextHasVertical;
|
|
813
801
|
hasHorizontal = nextHasHorizontal;
|
|
814
802
|
}
|
|
@@ -847,41 +835,48 @@
|
|
|
847
835
|
stroke: mask && mask.originalStroke || "#ccc",
|
|
848
836
|
strokeWidth: Number.isFinite(strokeWidth) ? strokeWidth : 1
|
|
849
837
|
};
|
|
850
|
-
if (Number.isFinite(opacity))
|
|
851
|
-
style.opacity = opacity;
|
|
838
|
+
if (Number.isFinite(opacity)) style.opacity = opacity;
|
|
852
839
|
return style;
|
|
853
840
|
}
|
|
854
841
|
_withNormalizedMaskStyles(callback) {
|
|
855
|
-
if (!this.canvas)
|
|
856
|
-
return callback();
|
|
842
|
+
if (!this.canvas) return callback();
|
|
857
843
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
858
|
-
const maskStyleBackups =
|
|
859
|
-
object: mask,
|
|
860
|
-
stroke: mask.stroke,
|
|
861
|
-
strokeWidth: mask.strokeWidth,
|
|
862
|
-
opacity: mask.opacity
|
|
863
|
-
}));
|
|
844
|
+
const maskStyleBackups = [];
|
|
864
845
|
try {
|
|
865
846
|
masks.forEach((mask) => {
|
|
866
|
-
|
|
847
|
+
const normalStyle = this._getMaskNormalStyle(mask);
|
|
848
|
+
const stylePatch = {};
|
|
849
|
+
Object.keys(normalStyle).forEach((property) => {
|
|
850
|
+
if (mask[property] !== normalStyle[property]) {
|
|
851
|
+
stylePatch[property] = normalStyle[property];
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
const changedProperties = Object.keys(stylePatch);
|
|
855
|
+
if (!changedProperties.length) return;
|
|
856
|
+
const backup = { object: mask };
|
|
857
|
+
changedProperties.forEach((property) => {
|
|
858
|
+
backup[property] = mask[property];
|
|
859
|
+
});
|
|
860
|
+
maskStyleBackups.push(backup);
|
|
861
|
+
mask.set(stylePatch);
|
|
867
862
|
});
|
|
868
863
|
return callback();
|
|
869
864
|
} finally {
|
|
870
865
|
maskStyleBackups.forEach((backup) => {
|
|
871
866
|
try {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
opacity: backup.opacity
|
|
867
|
+
const restorePatch = {};
|
|
868
|
+
Object.keys(backup).forEach((property) => {
|
|
869
|
+
if (property !== "object") restorePatch[property] = backup[property];
|
|
876
870
|
});
|
|
871
|
+
backup.object.set(restorePatch);
|
|
877
872
|
} catch (error) {
|
|
873
|
+
void error;
|
|
878
874
|
}
|
|
879
875
|
});
|
|
880
876
|
}
|
|
881
877
|
}
|
|
882
878
|
_restoreMaskControls(mask) {
|
|
883
|
-
if (!mask)
|
|
884
|
-
return;
|
|
879
|
+
if (!mask) return;
|
|
885
880
|
const cornerSize = Number(mask.cornerSize);
|
|
886
881
|
mask.set({
|
|
887
882
|
selectable: mask.selectable !== false,
|
|
@@ -894,8 +889,7 @@
|
|
|
894
889
|
transparentCorners: mask.transparentCorners === true,
|
|
895
890
|
strokeUniform: mask.strokeUniform !== false
|
|
896
891
|
});
|
|
897
|
-
if (typeof mask.setCoords === "function")
|
|
898
|
-
mask.setCoords();
|
|
892
|
+
if (typeof mask.setCoords === "function") mask.setCoords();
|
|
899
893
|
}
|
|
900
894
|
/**
|
|
901
895
|
* Captures editor-owned runtime state that Fabric does not include in canvas JSON.
|
|
@@ -917,8 +911,7 @@
|
|
|
917
911
|
};
|
|
918
912
|
}
|
|
919
913
|
_serializeCanvasState() {
|
|
920
|
-
if (!this.canvas)
|
|
921
|
-
return null;
|
|
914
|
+
if (!this.canvas) return null;
|
|
922
915
|
return this._withNormalizedMaskStyles(() => {
|
|
923
916
|
const jsonObject = this.canvas.toJSON(this._getStateProperties());
|
|
924
917
|
if (Array.isArray(jsonObject.objects)) {
|
|
@@ -937,8 +930,7 @@
|
|
|
937
930
|
*/
|
|
938
931
|
_normalizeQuality(quality) {
|
|
939
932
|
const numericQuality = Number(quality);
|
|
940
|
-
if (!Number.isFinite(numericQuality))
|
|
941
|
-
return this.options.downsampleQuality ?? 0.92;
|
|
933
|
+
if (!Number.isFinite(numericQuality)) return this.options.downsampleQuality ?? 0.92;
|
|
942
934
|
return Math.max(0, Math.min(1, numericQuality));
|
|
943
935
|
}
|
|
944
936
|
/**
|
|
@@ -1011,8 +1003,7 @@
|
|
|
1011
1003
|
const safeTimeoutMs = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 3e4;
|
|
1012
1004
|
let timerId;
|
|
1013
1005
|
const settle = (callback) => {
|
|
1014
|
-
if (isSettled)
|
|
1015
|
-
return;
|
|
1006
|
+
if (isSettled) return;
|
|
1016
1007
|
isSettled = true;
|
|
1017
1008
|
clearTimeout(timerId);
|
|
1018
1009
|
imageElement.onload = null;
|
|
@@ -1024,6 +1015,7 @@
|
|
|
1024
1015
|
try {
|
|
1025
1016
|
imageElement.src = "";
|
|
1026
1017
|
} catch (error) {
|
|
1018
|
+
void error;
|
|
1027
1019
|
}
|
|
1028
1020
|
}, safeTimeoutMs);
|
|
1029
1021
|
imageElement.onload = () => {
|
|
@@ -1037,8 +1029,7 @@
|
|
|
1037
1029
|
offscreenCanvas.width = scaledSourceWidth;
|
|
1038
1030
|
offscreenCanvas.height = scaledSourceHeight;
|
|
1039
1031
|
const context = offscreenCanvas.getContext("2d");
|
|
1040
|
-
if (!context)
|
|
1041
|
-
throw new Error("2D canvas context is unavailable");
|
|
1032
|
+
if (!context) throw new Error("2D canvas context is unavailable");
|
|
1042
1033
|
context.drawImage(imageElement, scaledSourceX, scaledSourceY, scaledSourceWidth, scaledSourceHeight, 0, 0, scaledSourceWidth, scaledSourceHeight);
|
|
1043
1034
|
settle(() => resolve(offscreenCanvas.toDataURL(`image/${format}`, quality)));
|
|
1044
1035
|
} catch (error) {
|
|
@@ -1081,12 +1072,10 @@
|
|
|
1081
1072
|
* @private
|
|
1082
1073
|
*/
|
|
1083
1074
|
_getObjectTopLeftPoint(fabricObject) {
|
|
1084
|
-
if (!fabricObject)
|
|
1085
|
-
return { x: 0, y: 0 };
|
|
1075
|
+
if (!fabricObject) return { x: 0, y: 0 };
|
|
1086
1076
|
fabricObject.setCoords();
|
|
1087
1077
|
const coords = typeof fabricObject.getCoords === "function" ? fabricObject.getCoords() : null;
|
|
1088
|
-
if (coords && coords.length)
|
|
1089
|
-
return coords[0];
|
|
1078
|
+
if (coords && coords.length) return coords[0];
|
|
1090
1079
|
const boundingRect = fabricObject.getBoundingRect(true, true);
|
|
1091
1080
|
return { x: boundingRect.left, y: boundingRect.top };
|
|
1092
1081
|
}
|
|
@@ -1100,8 +1089,7 @@
|
|
|
1100
1089
|
* @private
|
|
1101
1090
|
*/
|
|
1102
1091
|
_setObjectOriginKeepingPosition(fabricObject, originX, originY, refPoint) {
|
|
1103
|
-
if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin)
|
|
1104
|
-
return;
|
|
1092
|
+
if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin) return;
|
|
1105
1093
|
fabricObject.set({ originX, originY });
|
|
1106
1094
|
fabricObject.setPositionByOrigin(refPoint, originX, originY);
|
|
1107
1095
|
fabricObject.setCoords();
|
|
@@ -1113,8 +1101,7 @@
|
|
|
1113
1101
|
* @private
|
|
1114
1102
|
*/
|
|
1115
1103
|
_alignObjectBoundingBoxToCanvasTopLeft(fabricObject) {
|
|
1116
|
-
if (!fabricObject)
|
|
1117
|
-
return;
|
|
1104
|
+
if (!fabricObject) return;
|
|
1118
1105
|
fabricObject.setCoords();
|
|
1119
1106
|
const boundingRect = fabricObject.getBoundingRect(true, true);
|
|
1120
1107
|
const deltaX = boundingRect.left;
|
|
@@ -1129,8 +1116,7 @@
|
|
|
1129
1116
|
* @private
|
|
1130
1117
|
*/
|
|
1131
1118
|
_updateCanvasSizeToImageBounds() {
|
|
1132
|
-
if (!this.originalImage)
|
|
1133
|
-
return;
|
|
1119
|
+
if (!this.originalImage) return;
|
|
1134
1120
|
this.originalImage.setCoords();
|
|
1135
1121
|
const imageBounds = this.originalImage.getBoundingRect(true, true);
|
|
1136
1122
|
const size = this._getScrollableCanvasSize(imageBounds.width, imageBounds.height);
|
|
@@ -1154,16 +1140,13 @@
|
|
|
1154
1140
|
* @private
|
|
1155
1141
|
*/
|
|
1156
1142
|
_expandCanvasToFitObjects(fabricObjects, padding = 10) {
|
|
1157
|
-
if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds())
|
|
1158
|
-
return;
|
|
1143
|
+
if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds()) return;
|
|
1159
1144
|
try {
|
|
1160
1145
|
let requiredWidth = this.canvas.getWidth();
|
|
1161
1146
|
let requiredHeight = this.canvas.getHeight();
|
|
1162
1147
|
fabricObjects.forEach((fabricObject) => {
|
|
1163
|
-
if (!fabricObject)
|
|
1164
|
-
|
|
1165
|
-
if (typeof fabricObject.setCoords === "function")
|
|
1166
|
-
fabricObject.setCoords();
|
|
1148
|
+
if (!fabricObject) return;
|
|
1149
|
+
if (typeof fabricObject.setCoords === "function") fabricObject.setCoords();
|
|
1167
1150
|
const boundingRect = fabricObject.getBoundingRect(true, true);
|
|
1168
1151
|
requiredWidth = Math.max(requiredWidth, Math.ceil(boundingRect.left + boundingRect.width + padding));
|
|
1169
1152
|
requiredHeight = Math.max(requiredHeight, Math.ceil(boundingRect.top + boundingRect.height + padding));
|
|
@@ -1208,10 +1191,8 @@
|
|
|
1208
1191
|
* @private
|
|
1209
1192
|
*/
|
|
1210
1193
|
_scaleImageImpl(factor, options = {}) {
|
|
1211
|
-
if (!this.originalImage)
|
|
1212
|
-
|
|
1213
|
-
if (this.isAnimating)
|
|
1214
|
-
return Promise.resolve();
|
|
1194
|
+
if (!this.originalImage) return Promise.resolve();
|
|
1195
|
+
if (this.isAnimating) return Promise.resolve();
|
|
1215
1196
|
const saveHistory = options.saveHistory !== false;
|
|
1216
1197
|
factor = Math.max(this.options.minScale, Math.min(this.options.maxScale, factor));
|
|
1217
1198
|
this.currentScale = factor;
|
|
@@ -1242,14 +1223,12 @@
|
|
|
1242
1223
|
}
|
|
1243
1224
|
this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);
|
|
1244
1225
|
this.canvas.getObjects().forEach((object) => {
|
|
1245
|
-
if (object.maskId)
|
|
1246
|
-
this._syncMaskLabel(object);
|
|
1226
|
+
if (object.maskId) this._syncMaskLabel(object);
|
|
1247
1227
|
});
|
|
1248
1228
|
this.isAnimating = false;
|
|
1249
1229
|
this._updateInputs();
|
|
1250
1230
|
this._updateUI();
|
|
1251
|
-
if (saveHistory)
|
|
1252
|
-
this.saveState();
|
|
1231
|
+
if (saveHistory) this.saveState();
|
|
1253
1232
|
}).catch(() => {
|
|
1254
1233
|
this.isAnimating = false;
|
|
1255
1234
|
this._updateUI();
|
|
@@ -1273,12 +1252,9 @@
|
|
|
1273
1252
|
* @private
|
|
1274
1253
|
*/
|
|
1275
1254
|
_rotateImageImpl(degrees, options = {}) {
|
|
1276
|
-
if (!this.originalImage)
|
|
1277
|
-
|
|
1278
|
-
if (
|
|
1279
|
-
return Promise.resolve();
|
|
1280
|
-
if (isNaN(degrees))
|
|
1281
|
-
return Promise.resolve();
|
|
1255
|
+
if (!this.originalImage) return Promise.resolve();
|
|
1256
|
+
if (this.isAnimating) return Promise.resolve();
|
|
1257
|
+
if (isNaN(degrees)) return Promise.resolve();
|
|
1282
1258
|
const saveHistory = options.saveHistory !== false;
|
|
1283
1259
|
this.currentRotation = degrees;
|
|
1284
1260
|
this.isAnimating = true;
|
|
@@ -1302,14 +1278,12 @@
|
|
|
1302
1278
|
const newTopLeft = this._getObjectTopLeftPoint(this.originalImage);
|
|
1303
1279
|
this._setObjectOriginKeepingPosition(this.originalImage, "left", "top", newTopLeft);
|
|
1304
1280
|
this.canvas.getObjects().forEach((object) => {
|
|
1305
|
-
if (object.maskId)
|
|
1306
|
-
this._syncMaskLabel(object);
|
|
1281
|
+
if (object.maskId) this._syncMaskLabel(object);
|
|
1307
1282
|
});
|
|
1308
1283
|
this.isAnimating = false;
|
|
1309
1284
|
this._updateInputs();
|
|
1310
1285
|
this._updateUI();
|
|
1311
|
-
if (saveHistory)
|
|
1312
|
-
this.saveState();
|
|
1286
|
+
if (saveHistory) this.saveState();
|
|
1313
1287
|
}).catch(() => {
|
|
1314
1288
|
this.isAnimating = false;
|
|
1315
1289
|
this._updateUI();
|
|
@@ -1322,8 +1296,7 @@
|
|
|
1322
1296
|
* @public
|
|
1323
1297
|
*/
|
|
1324
1298
|
resetImageTransform() {
|
|
1325
|
-
if (!this.originalImage)
|
|
1326
|
-
return Promise.resolve();
|
|
1299
|
+
if (!this.originalImage) return Promise.resolve();
|
|
1327
1300
|
return this.animationQueue.add(async () => {
|
|
1328
1301
|
const before = this._lastSnapshot || this._serializeCanvasState();
|
|
1329
1302
|
await this._scaleImageImpl(1, { saveHistory: false });
|
|
@@ -1351,8 +1324,7 @@
|
|
|
1351
1324
|
* @public
|
|
1352
1325
|
*/
|
|
1353
1326
|
loadFromState(serializedState) {
|
|
1354
|
-
if (!serializedState || !this.canvas)
|
|
1355
|
-
return Promise.resolve();
|
|
1327
|
+
if (!serializedState || !this.canvas) return Promise.resolve();
|
|
1356
1328
|
return new Promise((resolve) => {
|
|
1357
1329
|
try {
|
|
1358
1330
|
const state = typeof serializedState === "string" ? JSON.parse(serializedState) : serializedState;
|
|
@@ -1428,14 +1400,12 @@
|
|
|
1428
1400
|
* @public
|
|
1429
1401
|
*/
|
|
1430
1402
|
saveState() {
|
|
1431
|
-
if (!this.canvas)
|
|
1432
|
-
return;
|
|
1403
|
+
if (!this.canvas) return;
|
|
1433
1404
|
const activeObject = this.canvas.getActiveObject();
|
|
1434
1405
|
try {
|
|
1435
1406
|
const after = this._serializeCanvasState();
|
|
1436
1407
|
const before = this._lastSnapshot || after;
|
|
1437
|
-
if (after === before)
|
|
1438
|
-
return;
|
|
1408
|
+
if (after === before) return;
|
|
1439
1409
|
let executedOnce = false;
|
|
1440
1410
|
const command = new Command(
|
|
1441
1411
|
() => {
|
|
@@ -1470,12 +1440,9 @@
|
|
|
1470
1440
|
* @private
|
|
1471
1441
|
*/
|
|
1472
1442
|
_pushStateTransition(before, after) {
|
|
1473
|
-
if (!before || !after)
|
|
1474
|
-
|
|
1475
|
-
if (
|
|
1476
|
-
return;
|
|
1477
|
-
if (!this.historyManager)
|
|
1478
|
-
this.historyManager = new HistoryManager(this.maxHistorySize || 50);
|
|
1443
|
+
if (!before || !after) return;
|
|
1444
|
+
if (before === after) return;
|
|
1445
|
+
if (!this.historyManager) this.historyManager = new HistoryManager(this.maxHistorySize || 50);
|
|
1479
1446
|
const command = new Command(
|
|
1480
1447
|
() => this.loadFromState(after),
|
|
1481
1448
|
() => this.loadFromState(before)
|
|
@@ -1511,26 +1478,24 @@
|
|
|
1511
1478
|
});
|
|
1512
1479
|
}
|
|
1513
1480
|
_rebindMaskEvents(mask) {
|
|
1514
|
-
if (!mask)
|
|
1515
|
-
return;
|
|
1481
|
+
if (!mask) return;
|
|
1516
1482
|
if (mask.__imageEditorMaskHandlers) {
|
|
1517
1483
|
try {
|
|
1518
1484
|
mask.off("mouseover", mask.__imageEditorMaskHandlers.mouseover);
|
|
1519
1485
|
mask.off("mouseout", mask.__imageEditorMaskHandlers.mouseout);
|
|
1520
1486
|
} catch (error) {
|
|
1487
|
+
void error;
|
|
1521
1488
|
}
|
|
1522
1489
|
}
|
|
1523
1490
|
const metadata = {};
|
|
1524
1491
|
if (!Number.isFinite(Number(mask.originalAlpha))) {
|
|
1525
1492
|
metadata.originalAlpha = Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : 0.5;
|
|
1526
1493
|
}
|
|
1527
|
-
if (!mask.originalStroke)
|
|
1528
|
-
metadata.originalStroke = mask.stroke || "#ccc";
|
|
1494
|
+
if (!mask.originalStroke) metadata.originalStroke = mask.stroke || "#ccc";
|
|
1529
1495
|
if (!Number.isFinite(Number(mask.originalStrokeWidth))) {
|
|
1530
1496
|
metadata.originalStrokeWidth = Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1;
|
|
1531
1497
|
}
|
|
1532
|
-
if (Object.keys(metadata).length)
|
|
1533
|
-
mask.set(metadata);
|
|
1498
|
+
if (Object.keys(metadata).length) mask.set(metadata);
|
|
1534
1499
|
const normalStyle = {
|
|
1535
1500
|
stroke: mask.originalStroke || "#ccc",
|
|
1536
1501
|
strokeWidth: mask.originalStrokeWidth,
|
|
@@ -1543,13 +1508,11 @@
|
|
|
1543
1508
|
};
|
|
1544
1509
|
const mouseover = () => {
|
|
1545
1510
|
mask.set(hoverStyle);
|
|
1546
|
-
if (mask.canvas)
|
|
1547
|
-
mask.canvas.requestRenderAll();
|
|
1511
|
+
if (mask.canvas) mask.canvas.requestRenderAll();
|
|
1548
1512
|
};
|
|
1549
1513
|
const mouseout = () => {
|
|
1550
1514
|
mask.set(normalStyle);
|
|
1551
|
-
if (mask.canvas)
|
|
1552
|
-
mask.canvas.requestRenderAll();
|
|
1515
|
+
if (mask.canvas) mask.canvas.requestRenderAll();
|
|
1553
1516
|
};
|
|
1554
1517
|
mask.on("mouseover", mouseover);
|
|
1555
1518
|
mask.on("mouseout", mouseout);
|
|
@@ -1584,8 +1547,7 @@
|
|
|
1584
1547
|
* @public
|
|
1585
1548
|
*/
|
|
1586
1549
|
createMask(config = {}) {
|
|
1587
|
-
if (!this.canvas)
|
|
1588
|
-
return null;
|
|
1550
|
+
if (!this.canvas) return null;
|
|
1589
1551
|
const shapeType = config.shape || "rect";
|
|
1590
1552
|
const maskConfig = {
|
|
1591
1553
|
shape: shapeType,
|
|
@@ -1601,14 +1563,22 @@
|
|
|
1601
1563
|
...config
|
|
1602
1564
|
};
|
|
1603
1565
|
const firstOffset = 10;
|
|
1604
|
-
let left
|
|
1605
|
-
let top
|
|
1606
|
-
const
|
|
1566
|
+
let left;
|
|
1567
|
+
let top;
|
|
1568
|
+
const getCanvasBasis = (axis) => {
|
|
1569
|
+
const canvasWidth = this.canvas ? this.canvas.getWidth() : 0;
|
|
1570
|
+
const canvasHeight = this.canvas ? this.canvas.getHeight() : 0;
|
|
1571
|
+
if (axis === "height") return canvasHeight;
|
|
1572
|
+
if (axis === "min") return Math.min(canvasWidth, canvasHeight);
|
|
1573
|
+
return canvasWidth;
|
|
1574
|
+
};
|
|
1575
|
+
const resolveValue = (value, fallback, axis = "width") => {
|
|
1607
1576
|
if (typeof value === "function")
|
|
1608
1577
|
return value(this.canvas, this.options);
|
|
1609
1578
|
if (typeof value === "string" && value.endsWith("%")) {
|
|
1610
|
-
const percent = parseFloat(value) / 100;
|
|
1611
|
-
|
|
1579
|
+
const percent = Number.parseFloat(value) / 100;
|
|
1580
|
+
if (!Number.isFinite(percent)) return fallback;
|
|
1581
|
+
return Math.floor(getCanvasBasis(axis) * percent);
|
|
1612
1582
|
}
|
|
1613
1583
|
return value != null ? value : fallback;
|
|
1614
1584
|
};
|
|
@@ -1623,11 +1593,11 @@
|
|
|
1623
1593
|
left = Math.round(previousMaskRight + maskConfig.gap);
|
|
1624
1594
|
top = previousMask.top ?? firstOffset;
|
|
1625
1595
|
} else {
|
|
1626
|
-
left = resolveValue(maskConfig.left, firstOffset);
|
|
1627
|
-
top = resolveValue(maskConfig.top, firstOffset);
|
|
1596
|
+
left = resolveValue(maskConfig.left, firstOffset, "width");
|
|
1597
|
+
top = resolveValue(maskConfig.top, firstOffset, "height");
|
|
1628
1598
|
}
|
|
1629
|
-
maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth);
|
|
1630
|
-
maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight);
|
|
1599
|
+
maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width");
|
|
1600
|
+
maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight, "height");
|
|
1631
1601
|
maskConfig.left = left;
|
|
1632
1602
|
maskConfig.top = top;
|
|
1633
1603
|
let mask;
|
|
@@ -1639,7 +1609,7 @@
|
|
|
1639
1609
|
mask = new fabric.Circle({
|
|
1640
1610
|
left,
|
|
1641
1611
|
top,
|
|
1642
|
-
radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2),
|
|
1612
|
+
radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2, "min"),
|
|
1643
1613
|
fill: maskConfig.color,
|
|
1644
1614
|
opacity: maskConfig.alpha,
|
|
1645
1615
|
angle: maskConfig.angle,
|
|
@@ -1650,8 +1620,8 @@
|
|
|
1650
1620
|
mask = new fabric.Ellipse({
|
|
1651
1621
|
left,
|
|
1652
1622
|
top,
|
|
1653
|
-
rx: resolveValue(maskConfig.rx, maskConfig.width / 2),
|
|
1654
|
-
ry: resolveValue(maskConfig.ry, maskConfig.height / 2),
|
|
1623
|
+
rx: resolveValue(maskConfig.rx, maskConfig.width / 2, "width"),
|
|
1624
|
+
ry: resolveValue(maskConfig.ry, maskConfig.height / 2, "height"),
|
|
1655
1625
|
fill: maskConfig.color,
|
|
1656
1626
|
opacity: maskConfig.alpha,
|
|
1657
1627
|
angle: maskConfig.angle,
|
|
@@ -1678,8 +1648,8 @@
|
|
|
1678
1648
|
mask = new fabric.Rect({
|
|
1679
1649
|
left,
|
|
1680
1650
|
top,
|
|
1681
|
-
width: resolveValue(maskConfig.width, this.options.defaultMaskWidth),
|
|
1682
|
-
height: resolveValue(maskConfig.height, this.options.defaultMaskHeight),
|
|
1651
|
+
width: resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width"),
|
|
1652
|
+
height: resolveValue(maskConfig.height, this.options.defaultMaskHeight, "height"),
|
|
1683
1653
|
fill: maskConfig.color,
|
|
1684
1654
|
opacity: maskConfig.alpha,
|
|
1685
1655
|
angle: maskConfig.angle,
|
|
@@ -1704,8 +1674,7 @@
|
|
|
1704
1674
|
opacity: hasStyle("opacity") ? styles.opacity : maskConfig.alpha,
|
|
1705
1675
|
strokeUniform: "strokeUniform" in maskConfig ? maskConfig.strokeUniform : hasStyle("strokeUniform") ? styles.strokeUniform : true
|
|
1706
1676
|
};
|
|
1707
|
-
if (hasStyle("strokeDashArray"))
|
|
1708
|
-
maskSettings.strokeDashArray = styles.strokeDashArray;
|
|
1677
|
+
if (hasStyle("strokeDashArray")) maskSettings.strokeDashArray = styles.strokeDashArray;
|
|
1709
1678
|
mask.set(maskSettings);
|
|
1710
1679
|
mask.setCoords();
|
|
1711
1680
|
mask.set({
|
|
@@ -1717,7 +1686,7 @@
|
|
|
1717
1686
|
this._expandCanvasToFitObject(mask);
|
|
1718
1687
|
this._lastMaskInitialLeft = left;
|
|
1719
1688
|
this._lastMaskInitialTop = top;
|
|
1720
|
-
this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth);
|
|
1689
|
+
this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width");
|
|
1721
1690
|
const maskId = ++this.maskCounter;
|
|
1722
1691
|
mask.set({
|
|
1723
1692
|
maskId,
|
|
@@ -1726,15 +1695,13 @@
|
|
|
1726
1695
|
this._lastMask = mask;
|
|
1727
1696
|
this.canvas.add(mask);
|
|
1728
1697
|
this.canvas.bringToFront(mask);
|
|
1729
|
-
if (maskConfig.selectable)
|
|
1730
|
-
this.canvas.setActiveObject(mask);
|
|
1698
|
+
if (maskConfig.selectable) this.canvas.setActiveObject(mask);
|
|
1731
1699
|
this._handleSelectionChanged([mask]);
|
|
1732
1700
|
this._updateMaskList();
|
|
1733
1701
|
this._updateUI();
|
|
1734
1702
|
this.canvas.renderAll();
|
|
1735
1703
|
this.saveState();
|
|
1736
|
-
if (typeof maskConfig.onCreate === "function")
|
|
1737
|
-
maskConfig.onCreate(mask, this.canvas);
|
|
1704
|
+
if (typeof maskConfig.onCreate === "function") maskConfig.onCreate(mask, this.canvas);
|
|
1738
1705
|
return mask;
|
|
1739
1706
|
}
|
|
1740
1707
|
/**
|
|
@@ -1754,8 +1721,7 @@
|
|
|
1754
1721
|
removeSelectedMask() {
|
|
1755
1722
|
const activeObject = this.canvas.getActiveObject();
|
|
1756
1723
|
const selectedMasks = this._getModifiedMasks(activeObject);
|
|
1757
|
-
if (!selectedMasks.length)
|
|
1758
|
-
return;
|
|
1724
|
+
if (!selectedMasks.length) return;
|
|
1759
1725
|
this.canvas.discardActiveObject();
|
|
1760
1726
|
selectedMasks.forEach((mask) => {
|
|
1761
1727
|
this._removeLabelForMask(mask);
|
|
@@ -1790,8 +1756,7 @@
|
|
|
1790
1756
|
this._updateMaskList();
|
|
1791
1757
|
this._updateUI();
|
|
1792
1758
|
this.canvas.renderAll();
|
|
1793
|
-
if (saveHistory)
|
|
1794
|
-
this.saveState();
|
|
1759
|
+
if (saveHistory) this.saveState();
|
|
1795
1760
|
}
|
|
1796
1761
|
/**
|
|
1797
1762
|
* Removes the label associated with the specified mask object, if it exists.
|
|
@@ -1800,8 +1765,7 @@
|
|
|
1800
1765
|
* @private
|
|
1801
1766
|
*/
|
|
1802
1767
|
_removeLabelForMask(mask) {
|
|
1803
|
-
if (!mask || !this.canvas)
|
|
1804
|
-
return;
|
|
1768
|
+
if (!mask || !this.canvas) return;
|
|
1805
1769
|
if (mask.__label) {
|
|
1806
1770
|
try {
|
|
1807
1771
|
const canvasObjects = this.canvas.getObjects();
|
|
@@ -1809,10 +1773,12 @@
|
|
|
1809
1773
|
this.canvas.remove(mask.__label);
|
|
1810
1774
|
}
|
|
1811
1775
|
} catch (error) {
|
|
1776
|
+
void error;
|
|
1812
1777
|
}
|
|
1813
1778
|
try {
|
|
1814
1779
|
delete mask.__label;
|
|
1815
1780
|
} catch (error) {
|
|
1781
|
+
void error;
|
|
1816
1782
|
}
|
|
1817
1783
|
}
|
|
1818
1784
|
}
|
|
@@ -1828,8 +1794,7 @@
|
|
|
1828
1794
|
*/
|
|
1829
1795
|
_getMaskCreationIndex(mask) {
|
|
1830
1796
|
const maskId = Number(mask && mask.maskId);
|
|
1831
|
-
if (Number.isFinite(maskId) && maskId > 0)
|
|
1832
|
-
return Math.floor(maskId) - 1;
|
|
1797
|
+
if (Number.isFinite(maskId) && maskId > 0) return Math.floor(maskId) - 1;
|
|
1833
1798
|
const masks = this.canvas ? this.canvas.getObjects().filter((object) => object.maskId) : [];
|
|
1834
1799
|
return Math.max(0, masks.indexOf(mask));
|
|
1835
1800
|
}
|
|
@@ -1841,8 +1806,7 @@
|
|
|
1841
1806
|
* @private
|
|
1842
1807
|
*/
|
|
1843
1808
|
_createLabelForMask(mask) {
|
|
1844
|
-
if (!mask || !this.options.maskLabelOnSelect)
|
|
1845
|
-
return;
|
|
1809
|
+
if (!mask || !this.options.maskLabelOnSelect) return;
|
|
1846
1810
|
this._removeLabelForMask(mask);
|
|
1847
1811
|
let textObject = null;
|
|
1848
1812
|
if (this.options.label && typeof this.options.label.create === "function") {
|
|
@@ -1884,15 +1848,14 @@
|
|
|
1884
1848
|
* @private
|
|
1885
1849
|
*/
|
|
1886
1850
|
_hideAllMaskLabels() {
|
|
1887
|
-
if (!this.canvas)
|
|
1888
|
-
return;
|
|
1851
|
+
if (!this.canvas) return;
|
|
1889
1852
|
const canvasObjects = this.canvas.getObjects();
|
|
1890
1853
|
const labels = canvasObjects.filter((object) => object.maskLabel);
|
|
1891
1854
|
labels.forEach((label) => {
|
|
1892
1855
|
try {
|
|
1893
|
-
if (canvasObjects.includes(label))
|
|
1894
|
-
this.canvas.remove(label);
|
|
1856
|
+
if (canvasObjects.includes(label)) this.canvas.remove(label);
|
|
1895
1857
|
} catch (error) {
|
|
1858
|
+
void error;
|
|
1896
1859
|
}
|
|
1897
1860
|
});
|
|
1898
1861
|
canvasObjects.forEach((object) => {
|
|
@@ -1900,6 +1863,7 @@
|
|
|
1900
1863
|
try {
|
|
1901
1864
|
delete object.__label;
|
|
1902
1865
|
} catch (error) {
|
|
1866
|
+
void error;
|
|
1903
1867
|
}
|
|
1904
1868
|
}
|
|
1905
1869
|
});
|
|
@@ -1911,15 +1875,11 @@
|
|
|
1911
1875
|
* @private
|
|
1912
1876
|
*/
|
|
1913
1877
|
_syncMaskLabel(mask) {
|
|
1914
|
-
if (!mask)
|
|
1915
|
-
|
|
1916
|
-
if (!
|
|
1917
|
-
return;
|
|
1918
|
-
if (!mask.__label)
|
|
1919
|
-
return;
|
|
1878
|
+
if (!mask) return;
|
|
1879
|
+
if (!this.options.maskLabelOnSelect) return;
|
|
1880
|
+
if (!mask.__label) return;
|
|
1920
1881
|
const coords = mask.getCoords ? mask.getCoords() : null;
|
|
1921
|
-
if (!coords || coords.length < 4)
|
|
1922
|
-
return;
|
|
1882
|
+
if (!coords || coords.length < 4) return;
|
|
1923
1883
|
const tl = coords[0];
|
|
1924
1884
|
const center = mask.getCenterPoint();
|
|
1925
1885
|
const vx = center.x - tl.x;
|
|
@@ -1952,12 +1912,9 @@
|
|
|
1952
1912
|
* @private
|
|
1953
1913
|
*/
|
|
1954
1914
|
_showLabelForMask(mask) {
|
|
1955
|
-
if (!mask)
|
|
1956
|
-
|
|
1957
|
-
if (!this.
|
|
1958
|
-
return;
|
|
1959
|
-
if (!mask.__label)
|
|
1960
|
-
this._createLabelForMask(mask);
|
|
1915
|
+
if (!mask) return;
|
|
1916
|
+
if (!this.options.maskLabelOnSelect) return;
|
|
1917
|
+
if (!mask.__label) this._createLabelForMask(mask);
|
|
1961
1918
|
mask.__label.set({ visible: true });
|
|
1962
1919
|
this._syncMaskLabel(mask);
|
|
1963
1920
|
}
|
|
@@ -1977,6 +1934,7 @@
|
|
|
1977
1934
|
try {
|
|
1978
1935
|
this.canvas.remove(mask.__label);
|
|
1979
1936
|
} catch (error) {
|
|
1937
|
+
void error;
|
|
1980
1938
|
}
|
|
1981
1939
|
delete mask.__label;
|
|
1982
1940
|
}
|
|
@@ -1989,8 +1947,7 @@
|
|
|
1989
1947
|
mask.set({ stroke: "#ff0000", strokeWidth: 1 });
|
|
1990
1948
|
}
|
|
1991
1949
|
});
|
|
1992
|
-
if (selectedMask)
|
|
1993
|
-
this._showLabelForMask(selectedMask);
|
|
1950
|
+
if (selectedMask) this._showLabelForMask(selectedMask);
|
|
1994
1951
|
this._updateMaskListSelection(selectedMask);
|
|
1995
1952
|
this.canvas.renderAll();
|
|
1996
1953
|
this._updateUI();
|
|
@@ -2002,8 +1959,7 @@
|
|
|
2002
1959
|
*/
|
|
2003
1960
|
_updateMaskList() {
|
|
2004
1961
|
const maskListElement = document.getElementById(this.elements.maskList);
|
|
2005
|
-
if (!maskListElement)
|
|
2006
|
-
return;
|
|
1962
|
+
if (!maskListElement) return;
|
|
2007
1963
|
maskListElement.innerHTML = "";
|
|
2008
1964
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
2009
1965
|
masks.forEach((mask) => {
|
|
@@ -2025,8 +1981,7 @@
|
|
|
2025
1981
|
*/
|
|
2026
1982
|
_updateMaskListSelection(selectedMask) {
|
|
2027
1983
|
const maskListElement = document.getElementById(this.elements.maskList);
|
|
2028
|
-
if (!maskListElement)
|
|
2029
|
-
return;
|
|
1984
|
+
if (!maskListElement) return;
|
|
2030
1985
|
const maskItems = maskListElement.querySelectorAll(".mask-item");
|
|
2031
1986
|
maskItems.forEach((item) => {
|
|
2032
1987
|
const isSelected = !!selectedMask && item.textContent === selectedMask.maskName;
|
|
@@ -2044,11 +1999,9 @@
|
|
|
2044
1999
|
* @public
|
|
2045
2000
|
*/
|
|
2046
2001
|
async mergeMasks() {
|
|
2047
|
-
if (!this.originalImage)
|
|
2048
|
-
return;
|
|
2002
|
+
if (!this.originalImage) return;
|
|
2049
2003
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
2050
|
-
if (!masks.length)
|
|
2051
|
-
return;
|
|
2004
|
+
if (!masks.length) return;
|
|
2052
2005
|
this.canvas.discardActiveObject();
|
|
2053
2006
|
this.canvas.renderAll();
|
|
2054
2007
|
try {
|
|
@@ -2080,8 +2033,7 @@
|
|
|
2080
2033
|
* @public
|
|
2081
2034
|
*/
|
|
2082
2035
|
downloadImage(fileName = this.options.defaultDownloadFileName) {
|
|
2083
|
-
if (!this.originalImage)
|
|
2084
|
-
return;
|
|
2036
|
+
if (!this.originalImage) return;
|
|
2085
2037
|
const exportImageArea = this.options.exportImageAreaByDefault;
|
|
2086
2038
|
this.exportImageBase64({ exportImageArea, multiplier: this.options.exportMultiplier }).then((imageBase64) => {
|
|
2087
2039
|
const link = document.createElement("a");
|
|
@@ -2109,8 +2061,7 @@
|
|
|
2109
2061
|
* @public
|
|
2110
2062
|
*/
|
|
2111
2063
|
async exportImageBase64(options = {}) {
|
|
2112
|
-
if (!this.originalImage)
|
|
2113
|
-
throw new Error("No image loaded");
|
|
2064
|
+
if (!this.originalImage) throw new Error("No image loaded");
|
|
2114
2065
|
const exportImageArea = typeof options.exportImageArea === "boolean" ? options.exportImageArea : this.options.exportImageAreaByDefault;
|
|
2115
2066
|
const multiplier = options.multiplier || this.options.exportMultiplier || 1;
|
|
2116
2067
|
const quality = this._normalizeQuality(options.quality ?? this.options.downsampleQuality);
|
|
@@ -2138,6 +2089,7 @@
|
|
|
2138
2089
|
try {
|
|
2139
2090
|
backup.object.set({ visible: backup.visible });
|
|
2140
2091
|
} catch (error) {
|
|
2092
|
+
void error;
|
|
2141
2093
|
}
|
|
2142
2094
|
});
|
|
2143
2095
|
this.canvas.renderAll();
|
|
@@ -2185,6 +2137,7 @@
|
|
|
2185
2137
|
});
|
|
2186
2138
|
backup.object.setCoords();
|
|
2187
2139
|
} catch (error) {
|
|
2140
|
+
void error;
|
|
2188
2141
|
}
|
|
2189
2142
|
});
|
|
2190
2143
|
this.canvas.renderAll();
|
|
@@ -2220,8 +2173,7 @@
|
|
|
2220
2173
|
* const file = await this.exportImageFile({ mergeMask: false, fileType: 'png' });
|
|
2221
2174
|
*/
|
|
2222
2175
|
async exportImageFile(options = {}) {
|
|
2223
|
-
if (!this.originalImage)
|
|
2224
|
-
throw new Error("No image loaded");
|
|
2176
|
+
if (!this.originalImage) throw new Error("No image loaded");
|
|
2225
2177
|
const {
|
|
2226
2178
|
mergeMask = true,
|
|
2227
2179
|
fileType = "jpeg",
|
|
@@ -2285,8 +2237,7 @@
|
|
|
2285
2237
|
}
|
|
2286
2238
|
async _restoreStateAfterCropFailure(beforeJson, message, error) {
|
|
2287
2239
|
this._reportError(message, error);
|
|
2288
|
-
if (this._cropRect && this.canvas)
|
|
2289
|
-
this._removeCropRect();
|
|
2240
|
+
if (this._cropRect && this.canvas) this._removeCropRect();
|
|
2290
2241
|
this._cropRect = null;
|
|
2291
2242
|
this._cropMode = false;
|
|
2292
2243
|
if (this.canvas && this._prevSelectionSetting !== void 0) {
|
|
@@ -2301,8 +2252,7 @@
|
|
|
2301
2252
|
}
|
|
2302
2253
|
}
|
|
2303
2254
|
this._updateUI();
|
|
2304
|
-
if (this.canvas)
|
|
2305
|
-
this.canvas.renderAll();
|
|
2255
|
+
if (this.canvas) this.canvas.renderAll();
|
|
2306
2256
|
}
|
|
2307
2257
|
_restoreCropObjectState() {
|
|
2308
2258
|
if (Array.isArray(this._cropPrevEvented)) {
|
|
@@ -2314,14 +2264,14 @@
|
|
|
2314
2264
|
visible: state.visible
|
|
2315
2265
|
});
|
|
2316
2266
|
} catch (error) {
|
|
2267
|
+
void error;
|
|
2317
2268
|
}
|
|
2318
2269
|
});
|
|
2319
2270
|
}
|
|
2320
2271
|
this._cropPrevEvented = null;
|
|
2321
2272
|
}
|
|
2322
2273
|
_removeCropRect() {
|
|
2323
|
-
if (!this._cropRect)
|
|
2324
|
-
return;
|
|
2274
|
+
if (!this._cropRect) return;
|
|
2325
2275
|
try {
|
|
2326
2276
|
if (this._cropHandlers && this._cropHandlers.length) {
|
|
2327
2277
|
this._cropHandlers.forEach((targetHandlers) => {
|
|
@@ -2331,10 +2281,12 @@
|
|
|
2331
2281
|
});
|
|
2332
2282
|
}
|
|
2333
2283
|
} catch (error) {
|
|
2284
|
+
void error;
|
|
2334
2285
|
}
|
|
2335
2286
|
try {
|
|
2336
2287
|
this.canvas.remove(this._cropRect);
|
|
2337
2288
|
} catch (error) {
|
|
2289
|
+
void error;
|
|
2338
2290
|
}
|
|
2339
2291
|
this._cropRect = null;
|
|
2340
2292
|
this._cropHandlers = [];
|
|
@@ -2349,10 +2301,8 @@
|
|
|
2349
2301
|
* @public
|
|
2350
2302
|
*/
|
|
2351
2303
|
enterCropMode() {
|
|
2352
|
-
if (!this.canvas || !this.originalImage || this._cropMode)
|
|
2353
|
-
|
|
2354
|
-
if (!this.isImageLoaded())
|
|
2355
|
-
return;
|
|
2304
|
+
if (!this.canvas || !this.originalImage || this._cropMode) return;
|
|
2305
|
+
if (!this.isImageLoaded()) return;
|
|
2356
2306
|
this._cropMode = true;
|
|
2357
2307
|
this._prevSelectionSetting = this.canvas.selection;
|
|
2358
2308
|
this.canvas.selection = false;
|
|
@@ -2404,10 +2354,10 @@
|
|
|
2404
2354
|
evented: false,
|
|
2405
2355
|
selectable: false
|
|
2406
2356
|
};
|
|
2407
|
-
if (shouldHideMasks && (object.maskId || object.maskLabel))
|
|
2408
|
-
updates.visible = false;
|
|
2357
|
+
if (shouldHideMasks && (object.maskId || object.maskLabel)) updates.visible = false;
|
|
2409
2358
|
object.set(updates);
|
|
2410
2359
|
} catch (error) {
|
|
2360
|
+
void error;
|
|
2411
2361
|
}
|
|
2412
2362
|
}
|
|
2413
2363
|
});
|
|
@@ -2421,6 +2371,7 @@
|
|
|
2421
2371
|
cropRect.setCoords();
|
|
2422
2372
|
this.canvas.requestRenderAll();
|
|
2423
2373
|
} catch (error) {
|
|
2374
|
+
void error;
|
|
2424
2375
|
}
|
|
2425
2376
|
};
|
|
2426
2377
|
cropRect.on("modified", handleCropRectModified);
|
|
@@ -2444,8 +2395,7 @@
|
|
|
2444
2395
|
* @public
|
|
2445
2396
|
*/
|
|
2446
2397
|
cancelCrop() {
|
|
2447
|
-
if (!this.canvas || !this._cropMode)
|
|
2448
|
-
return;
|
|
2398
|
+
if (!this.canvas || !this._cropMode) return;
|
|
2449
2399
|
this._removeCropRect();
|
|
2450
2400
|
this._restoreCropObjectState();
|
|
2451
2401
|
this._cropMode = false;
|
|
@@ -2467,14 +2417,13 @@
|
|
|
2467
2417
|
* @public
|
|
2468
2418
|
*/
|
|
2469
2419
|
async applyCrop() {
|
|
2470
|
-
if (!this.canvas || !this._cropMode || !this._cropRect)
|
|
2471
|
-
return;
|
|
2420
|
+
if (!this.canvas || !this._cropMode || !this._cropRect) return;
|
|
2472
2421
|
this._cropRect.setCoords();
|
|
2473
2422
|
const rectBounds = this._cropRect.getBoundingRect(true, true);
|
|
2474
|
-
const cropRegion = this._getClampedCanvasRegion(rectBounds);
|
|
2423
|
+
const cropRegion = this._getClampedCanvasRegion(rectBounds, { includePartialPixels: false });
|
|
2475
2424
|
const shouldPreserveMasks = !!(this.options.crop && this.options.crop.preserveMasksAfterCrop);
|
|
2476
2425
|
this._restoreCropObjectState();
|
|
2477
|
-
let beforeJson
|
|
2426
|
+
let beforeJson;
|
|
2478
2427
|
try {
|
|
2479
2428
|
beforeJson = this._serializeCanvasState();
|
|
2480
2429
|
} catch (error) {
|
|
@@ -2545,7 +2494,7 @@
|
|
|
2545
2494
|
await this._restoreStateAfterCropFailure(beforeJson, "applyCrop: loadImage(croppedBase64) failed", error);
|
|
2546
2495
|
return;
|
|
2547
2496
|
}
|
|
2548
|
-
let afterJson
|
|
2497
|
+
let afterJson;
|
|
2549
2498
|
try {
|
|
2550
2499
|
afterJson = this._serializeCanvasState();
|
|
2551
2500
|
} catch (error) {
|
|
@@ -2568,8 +2517,7 @@
|
|
|
2568
2517
|
*/
|
|
2569
2518
|
_updateInputs() {
|
|
2570
2519
|
const scaleInputElement = document.getElementById(this.elements.scaleRate);
|
|
2571
|
-
if (scaleInputElement)
|
|
2572
|
-
scaleInputElement.value = Math.round(this.currentScale * 100);
|
|
2520
|
+
if (scaleInputElement) scaleInputElement.value = Math.round(this.currentScale * 100);
|
|
2573
2521
|
}
|
|
2574
2522
|
/**
|
|
2575
2523
|
* Updates the enabled/disabled state of various UI controls (buttons)
|
|
@@ -2589,8 +2537,7 @@
|
|
|
2589
2537
|
if (isInCropMode) {
|
|
2590
2538
|
for (const key of Object.keys(this.elements || {})) {
|
|
2591
2539
|
const element = document.getElementById(this.elements[key]);
|
|
2592
|
-
if (!element)
|
|
2593
|
-
continue;
|
|
2540
|
+
if (!element) continue;
|
|
2594
2541
|
if (key === "applyCropBtn" || key === "cancelCropBtn") {
|
|
2595
2542
|
this._setDisabled(key, false);
|
|
2596
2543
|
} else {
|
|
@@ -2626,8 +2573,7 @@
|
|
|
2626
2573
|
*/
|
|
2627
2574
|
_setDisabled(key, disabled) {
|
|
2628
2575
|
const element = document.getElementById(this.elements[key]);
|
|
2629
|
-
if (!element)
|
|
2630
|
-
return;
|
|
2576
|
+
if (!element) return;
|
|
2631
2577
|
if ("disabled" in element) {
|
|
2632
2578
|
element.disabled = !!disabled;
|
|
2633
2579
|
return;
|
|
@@ -2641,10 +2587,8 @@
|
|
|
2641
2587
|
}
|
|
2642
2588
|
}
|
|
2643
2589
|
_isElementDisabled(element) {
|
|
2644
|
-
if (!element)
|
|
2645
|
-
|
|
2646
|
-
if ("disabled" in element)
|
|
2647
|
-
return !!element.disabled;
|
|
2590
|
+
if (!element) return false;
|
|
2591
|
+
if ("disabled" in element) return !!element.disabled;
|
|
2648
2592
|
return element.getAttribute("aria-disabled") === "true";
|
|
2649
2593
|
}
|
|
2650
2594
|
/**
|
|
@@ -2652,8 +2596,7 @@
|
|
|
2652
2596
|
* @private
|
|
2653
2597
|
*/
|
|
2654
2598
|
_updatePlaceholderStatus() {
|
|
2655
|
-
if (!this.options.showPlaceholder)
|
|
2656
|
-
return;
|
|
2599
|
+
if (!this.options.showPlaceholder) return;
|
|
2657
2600
|
this._setPlaceholderVisible(!this.originalImage);
|
|
2658
2601
|
}
|
|
2659
2602
|
/**
|
|
@@ -2663,17 +2606,23 @@
|
|
|
2663
2606
|
* @private
|
|
2664
2607
|
*/
|
|
2665
2608
|
_setPlaceholderVisible(show) {
|
|
2666
|
-
if (!this.placeholderElement || !this.containerElement)
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2609
|
+
if (!this.placeholderElement || !this.containerElement) return;
|
|
2610
|
+
this._setElementVisible(this.placeholderElement, show);
|
|
2611
|
+
this._setElementVisible(this.containerElement, !show);
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Updates element visibility.
|
|
2615
|
+
*
|
|
2616
|
+
* @param {HTMLElement} element - Element whose visibility should be updated.
|
|
2617
|
+
* @param {boolean} isVisible - If true, removes the hidden state.
|
|
2618
|
+
* @returns {void}
|
|
2619
|
+
* @private
|
|
2620
|
+
*/
|
|
2621
|
+
_setElementVisible(element, isVisible) {
|
|
2622
|
+
if (!element) return;
|
|
2623
|
+
element.hidden = !isVisible;
|
|
2624
|
+
element.setAttribute("aria-hidden", isVisible ? "false" : "true");
|
|
2625
|
+
if (isVisible && element.classList) element.classList.remove("d-none");
|
|
2677
2626
|
}
|
|
2678
2627
|
/**
|
|
2679
2628
|
* Cleans up and disposes of the canvas and related references.
|
|
@@ -2685,21 +2634,23 @@
|
|
|
2685
2634
|
for (const key in this._handlersByElementKey || {}) {
|
|
2686
2635
|
const handlers = this._handlersByElementKey[key] || [];
|
|
2687
2636
|
const element = document.getElementById(this.elements[key]);
|
|
2688
|
-
if (!element)
|
|
2689
|
-
continue;
|
|
2637
|
+
if (!element) continue;
|
|
2690
2638
|
handlers.forEach((handlerRecord) => {
|
|
2691
2639
|
try {
|
|
2692
2640
|
element.removeEventListener(handlerRecord.eventName, handlerRecord.handler);
|
|
2693
2641
|
} catch (error) {
|
|
2642
|
+
void error;
|
|
2694
2643
|
}
|
|
2695
2644
|
});
|
|
2696
2645
|
}
|
|
2697
2646
|
} catch (error) {
|
|
2647
|
+
void error;
|
|
2698
2648
|
}
|
|
2699
2649
|
if (this._cropRect) {
|
|
2700
2650
|
try {
|
|
2701
2651
|
this.canvas.remove(this._cropRect);
|
|
2702
2652
|
} catch (error) {
|
|
2653
|
+
void error;
|
|
2703
2654
|
}
|
|
2704
2655
|
this._cropRect = null;
|
|
2705
2656
|
}
|
|
@@ -2707,12 +2658,14 @@
|
|
|
2707
2658
|
try {
|
|
2708
2659
|
this.containerElement.style.overflow = this._containerOriginalOverflow;
|
|
2709
2660
|
} catch (error) {
|
|
2661
|
+
void error;
|
|
2710
2662
|
}
|
|
2711
2663
|
}
|
|
2712
2664
|
if (this.canvas) {
|
|
2713
2665
|
try {
|
|
2714
2666
|
this.canvas.dispose();
|
|
2715
2667
|
} catch (error) {
|
|
2668
|
+
void error;
|
|
2716
2669
|
}
|
|
2717
2670
|
this.canvas = null;
|
|
2718
2671
|
this.canvasElement = null;
|