@leapfrog/interactive-canvas 2.0.25-beta-4 → 2.0.25-beta-6
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/abstract-interactive-canvas.d.ts +1 -0
- package/dist/interactive-canvas.es.js +85 -41
- package/dist/interactive-canvas.js +85 -41
- package/dist/layout/advanced-layout-manager.d.ts +1 -0
- package/dist/layout/standard-layout-manager.d.ts +1 -0
- package/dist/object/abstract-canvas-object.d.ts +1 -1
- package/dist/object/impl/image.d.ts +2 -2
- package/dist/object/impl/text.d.ts +2 -0
- package/package.json +1 -1
|
@@ -340,6 +340,7 @@ export declare abstract class AbstractInteractiveCanvas implements IInteractiveC
|
|
|
340
340
|
* @inheritDoc
|
|
341
341
|
*/
|
|
342
342
|
cloneObject(sourceObject: FabricObject, objectData: CanvasObjectData): Promise<void>;
|
|
343
|
+
private redrawScheduled;
|
|
343
344
|
/**
|
|
344
345
|
* Redraw the canvas.
|
|
345
346
|
*/
|
|
@@ -232,9 +232,18 @@ class ResizeCanvasLayoutFunction {
|
|
|
232
232
|
const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
|
|
233
233
|
if (currentDimensions.height !== targetHeight.height) {
|
|
234
234
|
canvas.setDimensions(targetHeight);
|
|
235
|
+
// Only update coords for objects affected by height change (pinned to bottom, etc.)
|
|
236
|
+
// This is much faster than updating all objects on slow machines
|
|
237
|
+
canvas.getObjectsInternal().forEach((object) => {
|
|
238
|
+
var _a;
|
|
239
|
+
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
240
|
+
// Only update coords for objects that are positioned relative to canvas bottom or that might be affected
|
|
241
|
+
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
242
|
+
object.setCoords();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
canvas.redraw();
|
|
235
246
|
}
|
|
236
|
-
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
237
|
-
canvas.redraw();
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
249
|
|
|
@@ -262,13 +271,22 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
262
271
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
263
272
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
264
273
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
274
|
+
this.dimensionsLayoutScheduled = false;
|
|
265
275
|
}
|
|
266
276
|
onCanvasDimensionsChange() {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this.
|
|
277
|
+
// Batch dimension changes to avoid redundant layout passes
|
|
278
|
+
if (this.dimensionsLayoutScheduled) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.dimensionsLayoutScheduled = true;
|
|
282
|
+
Promise.resolve().then(() => {
|
|
283
|
+
this.pinToBottom.execute(this.canvas);
|
|
284
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
285
|
+
this.repositionBorders.execute(this.canvas);
|
|
286
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
287
|
+
this.redrawAll.execute(this.canvas);
|
|
288
|
+
this.dimensionsLayoutScheduled = false;
|
|
289
|
+
});
|
|
272
290
|
}
|
|
273
291
|
onTextChange() {
|
|
274
292
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -296,7 +314,9 @@ class StackObjectsLayoutFunction {
|
|
|
296
314
|
const footerObjects = objects[0];
|
|
297
315
|
const bodyObjects = objects[1];
|
|
298
316
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
299
|
-
canvas.redraw();
|
|
317
|
+
//canvas.redraw();
|
|
318
|
+
// No need to redraw here - it will be called by the layout manager after all layout functions complete
|
|
319
|
+
// This reduces redundant render passes significantly
|
|
300
320
|
//Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
|
|
301
321
|
this.applyWidths(allObjects, bodyWidth);
|
|
302
322
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -383,7 +403,11 @@ class StackObjectsLayoutFunction {
|
|
|
383
403
|
object.setPosition({
|
|
384
404
|
width: actualWidth
|
|
385
405
|
});
|
|
386
|
-
|
|
406
|
+
_(objects).forEach((object) => {
|
|
407
|
+
if (object.getType() !== ObjectType.VERTICAL_RULE) {
|
|
408
|
+
object.updateDimensions();
|
|
409
|
+
}
|
|
410
|
+
});
|
|
387
411
|
});
|
|
388
412
|
}
|
|
389
413
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -456,6 +480,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
456
480
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
457
481
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
458
482
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
483
|
+
this.layoutBatchScheduled = false;
|
|
459
484
|
}
|
|
460
485
|
/**
|
|
461
486
|
* @override
|
|
@@ -497,12 +522,21 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
497
522
|
this.stackAndRedraw();
|
|
498
523
|
}
|
|
499
524
|
stackAndRedraw() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.
|
|
505
|
-
|
|
525
|
+
// Batch multiple layout requests into a single pass
|
|
526
|
+
if (this.layoutBatchScheduled) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
this.layoutBatchScheduled = true;
|
|
530
|
+
// Use microtask to batch synchronous calls, but still execute before next frame
|
|
531
|
+
Promise.resolve().then(() => {
|
|
532
|
+
this.stackObjects.execute(this.canvas);
|
|
533
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
534
|
+
this.repositionBorders.execute(this.canvas);
|
|
535
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
536
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
537
|
+
this.redrawAll.execute(this.canvas);
|
|
538
|
+
this.layoutBatchScheduled = false;
|
|
539
|
+
});
|
|
506
540
|
}
|
|
507
541
|
}
|
|
508
542
|
|
|
@@ -1449,19 +1483,14 @@ class HorizontalRule extends AbstractCanvasObject {
|
|
|
1449
1483
|
|
|
1450
1484
|
class Image extends AbstractCanvasObject {
|
|
1451
1485
|
constructor(canvas, fabricObject, persistedField) {
|
|
1452
|
-
var _a;
|
|
1453
1486
|
super(ObjectType.IMAGE, canvas, fabricObject, persistedField);
|
|
1454
1487
|
this.DEFAULT_RESTRICTIONS = {
|
|
1455
1488
|
minWidth: 1,
|
|
1456
1489
|
minHeight: 1
|
|
1457
1490
|
};
|
|
1458
1491
|
this.canvas = canvas;
|
|
1459
|
-
console.log("fabric object", this.fabricObject);
|
|
1460
|
-
console.log("fabric src", (_a = this.fabricObject) === null || _a === void 0 ? void 0 : _a.getSrc());
|
|
1461
1492
|
this.imageUrl$ = new BehaviorSubject(this.fabricObject.getSrc());
|
|
1462
|
-
console.log("imageUrl$", this.imageUrl$);
|
|
1463
1493
|
this.data.imagePath = this.imageUrl$.value;
|
|
1464
|
-
console.log("this.data.imagePath", this.data.imagePath);
|
|
1465
1494
|
this.layoutWidth = this.getBoundingBox().width;
|
|
1466
1495
|
}
|
|
1467
1496
|
/**
|
|
@@ -1785,6 +1814,9 @@ var ObjectType;
|
|
|
1785
1814
|
class Text extends AbstractCanvasObject {
|
|
1786
1815
|
constructor(canvas, fabricObject, persistedField) {
|
|
1787
1816
|
super(ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1817
|
+
//private notifyCanvasTimeout: NodeJS.Timeout | null = null;
|
|
1818
|
+
this.notifyCanvasTimeout = null;
|
|
1819
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1788
1820
|
this.canvas = canvas;
|
|
1789
1821
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1790
1822
|
this.baseComponentHeight = this.fabricObject.height;
|
|
@@ -2532,28 +2564,42 @@ class Text extends AbstractCanvasObject {
|
|
|
2532
2564
|
onTextChange() {
|
|
2533
2565
|
this.data.userText = this.fabricObject.text;
|
|
2534
2566
|
this.text$.next(this.fabricObject.text);
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2567
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2568
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2569
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2570
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2571
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2572
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2573
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2574
|
+
this.redraw();
|
|
2575
|
+
}
|
|
2538
2576
|
}
|
|
2539
2577
|
}
|
|
2540
2578
|
onDelete() {
|
|
2541
2579
|
this.removeBullets();
|
|
2542
2580
|
}
|
|
2543
2581
|
onSelectionChange() {
|
|
2544
|
-
this.updateTextSelection();
|
|
2582
|
+
this.updateTextSelection(false);
|
|
2545
2583
|
}
|
|
2546
2584
|
hasTextSelection() {
|
|
2547
2585
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2548
2586
|
}
|
|
2549
|
-
updateTextSelection() {
|
|
2587
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2550
2588
|
if (this.canvas.headless)
|
|
2551
2589
|
return;
|
|
2552
2590
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2553
|
-
|
|
2591
|
+
if (notifyCanvas) {
|
|
2592
|
+
this.notifyCanvasOfTextChanges();
|
|
2593
|
+
}
|
|
2554
2594
|
}
|
|
2555
2595
|
notifyCanvasOfTextChanges() {
|
|
2556
|
-
this.
|
|
2596
|
+
if (this.notifyCanvasTimeout) {
|
|
2597
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2598
|
+
}
|
|
2599
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2600
|
+
this.canvas.notifyTextChanged();
|
|
2601
|
+
this.notifyCanvasTimeout = null;
|
|
2602
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2557
2603
|
}
|
|
2558
2604
|
}
|
|
2559
2605
|
Text.NEW_LINE = "\n";
|
|
@@ -2719,6 +2765,7 @@ class AbstractInteractiveCanvas {
|
|
|
2719
2765
|
this.devicePixelRatio = devicePixelRatio;
|
|
2720
2766
|
this.fontLibrary = new FontLibrary();
|
|
2721
2767
|
this.dimensionChangeRejections$ = new Subject();
|
|
2768
|
+
this.redrawScheduled = false;
|
|
2722
2769
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2723
2770
|
/**
|
|
2724
2771
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -3066,25 +3113,12 @@ class AbstractInteractiveCanvas {
|
|
|
3066
3113
|
originY: "top",
|
|
3067
3114
|
src: imageUrl,
|
|
3068
3115
|
crossOrigin: "anonymous",
|
|
3069
|
-
/*toObject: function () {
|
|
3070
|
-
return {
|
|
3071
|
-
...fabric.Object.prototype.toObject.call(this),
|
|
3072
|
-
crossOrigin: this.crossOrigin,
|
|
3073
|
-
src: this.src,
|
|
3074
|
-
id: id
|
|
3075
|
-
};
|
|
3076
|
-
},*/
|
|
3077
3116
|
});
|
|
3078
|
-
// fabricImage.toDataURL({ format: 'png' });
|
|
3079
3117
|
yield fabricImage.setSrc(imageUrl, options);
|
|
3080
3118
|
this.fabricCanvas.add(fabricImage);
|
|
3081
3119
|
const canvasObjectData = new CanvasObjectData(ObjectType.IMAGE, id);
|
|
3082
3120
|
canvasObjectData.imagePath = imageUrl;
|
|
3083
|
-
console.log("Assigned imagePath:", canvasObjectData.imagePath);
|
|
3084
3121
|
const object = new Image(this, fabricImage, canvasObjectData);
|
|
3085
|
-
console.log("Created Image object with imagePath:", object);
|
|
3086
|
-
object.data.imagePath = imageUrl;
|
|
3087
|
-
object.imageUrl$.next(imageUrl);
|
|
3088
3122
|
this.trackNewObject(object);
|
|
3089
3123
|
this.syncObjectList();
|
|
3090
3124
|
return object;
|
|
@@ -3142,6 +3176,7 @@ class AbstractInteractiveCanvas {
|
|
|
3142
3176
|
fontFamily: profile.defaultFont,
|
|
3143
3177
|
textAlign: profile.defaultTextAlignment,
|
|
3144
3178
|
hoverCursor: "move",
|
|
3179
|
+
moveCursor: 'move',
|
|
3145
3180
|
multiLine: true,
|
|
3146
3181
|
lineHeight: profile.defaultLineHeight,
|
|
3147
3182
|
charSpacing: profile.defaultCharacterSpacing,
|
|
@@ -3596,7 +3631,16 @@ class AbstractInteractiveCanvas {
|
|
|
3596
3631
|
* Redraw the canvas.
|
|
3597
3632
|
*/
|
|
3598
3633
|
redraw() {
|
|
3599
|
-
this.fabricCanvas.requestRenderAll();
|
|
3634
|
+
//this.fabricCanvas.requestRenderAll();
|
|
3635
|
+
if (this.redrawScheduled) {
|
|
3636
|
+
return; // Already scheduled, don't schedule again
|
|
3637
|
+
}
|
|
3638
|
+
this.redrawScheduled = true;
|
|
3639
|
+
// Use requestAnimationFrame for smoother rendering and automatic batching
|
|
3640
|
+
requestAnimationFrame(() => {
|
|
3641
|
+
this.fabricCanvas.requestRenderAll();
|
|
3642
|
+
this.redrawScheduled = false;
|
|
3643
|
+
});
|
|
3600
3644
|
}
|
|
3601
3645
|
syncObjectList() {
|
|
3602
3646
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -253,9 +253,18 @@ class ResizeCanvasLayoutFunction {
|
|
|
253
253
|
const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
|
|
254
254
|
if (currentDimensions.height !== targetHeight.height) {
|
|
255
255
|
canvas.setDimensions(targetHeight);
|
|
256
|
+
// Only update coords for objects affected by height change (pinned to bottom, etc.)
|
|
257
|
+
// This is much faster than updating all objects on slow machines
|
|
258
|
+
canvas.getObjectsInternal().forEach((object) => {
|
|
259
|
+
var _a;
|
|
260
|
+
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
261
|
+
// Only update coords for objects that are positioned relative to canvas bottom or that might be affected
|
|
262
|
+
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
263
|
+
object.setCoords();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
canvas.redraw();
|
|
256
267
|
}
|
|
257
|
-
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
258
|
-
canvas.redraw();
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
270
|
|
|
@@ -283,13 +292,22 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
283
292
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
284
293
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
285
294
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
295
|
+
this.dimensionsLayoutScheduled = false;
|
|
286
296
|
}
|
|
287
297
|
onCanvasDimensionsChange() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.
|
|
298
|
+
// Batch dimension changes to avoid redundant layout passes
|
|
299
|
+
if (this.dimensionsLayoutScheduled) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.dimensionsLayoutScheduled = true;
|
|
303
|
+
Promise.resolve().then(() => {
|
|
304
|
+
this.pinToBottom.execute(this.canvas);
|
|
305
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
306
|
+
this.repositionBorders.execute(this.canvas);
|
|
307
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
308
|
+
this.redrawAll.execute(this.canvas);
|
|
309
|
+
this.dimensionsLayoutScheduled = false;
|
|
310
|
+
});
|
|
293
311
|
}
|
|
294
312
|
onTextChange() {
|
|
295
313
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -317,7 +335,9 @@ class StackObjectsLayoutFunction {
|
|
|
317
335
|
const footerObjects = objects[0];
|
|
318
336
|
const bodyObjects = objects[1];
|
|
319
337
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
320
|
-
canvas.redraw();
|
|
338
|
+
//canvas.redraw();
|
|
339
|
+
// No need to redraw here - it will be called by the layout manager after all layout functions complete
|
|
340
|
+
// This reduces redundant render passes significantly
|
|
321
341
|
//Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
|
|
322
342
|
this.applyWidths(allObjects, bodyWidth);
|
|
323
343
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -404,7 +424,11 @@ class StackObjectsLayoutFunction {
|
|
|
404
424
|
object.setPosition({
|
|
405
425
|
width: actualWidth
|
|
406
426
|
});
|
|
407
|
-
|
|
427
|
+
_(objects).forEach((object) => {
|
|
428
|
+
if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
|
|
429
|
+
object.updateDimensions();
|
|
430
|
+
}
|
|
431
|
+
});
|
|
408
432
|
});
|
|
409
433
|
}
|
|
410
434
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -477,6 +501,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
477
501
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
478
502
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
479
503
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
504
|
+
this.layoutBatchScheduled = false;
|
|
480
505
|
}
|
|
481
506
|
/**
|
|
482
507
|
* @override
|
|
@@ -518,12 +543,21 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
518
543
|
this.stackAndRedraw();
|
|
519
544
|
}
|
|
520
545
|
stackAndRedraw() {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
this.
|
|
526
|
-
|
|
546
|
+
// Batch multiple layout requests into a single pass
|
|
547
|
+
if (this.layoutBatchScheduled) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
this.layoutBatchScheduled = true;
|
|
551
|
+
// Use microtask to batch synchronous calls, but still execute before next frame
|
|
552
|
+
Promise.resolve().then(() => {
|
|
553
|
+
this.stackObjects.execute(this.canvas);
|
|
554
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
555
|
+
this.repositionBorders.execute(this.canvas);
|
|
556
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
557
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
558
|
+
this.redrawAll.execute(this.canvas);
|
|
559
|
+
this.layoutBatchScheduled = false;
|
|
560
|
+
});
|
|
527
561
|
}
|
|
528
562
|
}
|
|
529
563
|
|
|
@@ -1470,19 +1504,14 @@ class HorizontalRule extends AbstractCanvasObject {
|
|
|
1470
1504
|
|
|
1471
1505
|
class Image extends AbstractCanvasObject {
|
|
1472
1506
|
constructor(canvas, fabricObject, persistedField) {
|
|
1473
|
-
var _a;
|
|
1474
1507
|
super(exports.ObjectType.IMAGE, canvas, fabricObject, persistedField);
|
|
1475
1508
|
this.DEFAULT_RESTRICTIONS = {
|
|
1476
1509
|
minWidth: 1,
|
|
1477
1510
|
minHeight: 1
|
|
1478
1511
|
};
|
|
1479
1512
|
this.canvas = canvas;
|
|
1480
|
-
console.log("fabric object", this.fabricObject);
|
|
1481
|
-
console.log("fabric src", (_a = this.fabricObject) === null || _a === void 0 ? void 0 : _a.getSrc());
|
|
1482
1513
|
this.imageUrl$ = new rxjs.BehaviorSubject(this.fabricObject.getSrc());
|
|
1483
|
-
console.log("imageUrl$", this.imageUrl$);
|
|
1484
1514
|
this.data.imagePath = this.imageUrl$.value;
|
|
1485
|
-
console.log("this.data.imagePath", this.data.imagePath);
|
|
1486
1515
|
this.layoutWidth = this.getBoundingBox().width;
|
|
1487
1516
|
}
|
|
1488
1517
|
/**
|
|
@@ -1806,6 +1835,9 @@ exports.ObjectType = void 0;
|
|
|
1806
1835
|
class Text extends AbstractCanvasObject {
|
|
1807
1836
|
constructor(canvas, fabricObject, persistedField) {
|
|
1808
1837
|
super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1838
|
+
//private notifyCanvasTimeout: NodeJS.Timeout | null = null;
|
|
1839
|
+
this.notifyCanvasTimeout = null;
|
|
1840
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1809
1841
|
this.canvas = canvas;
|
|
1810
1842
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1811
1843
|
this.baseComponentHeight = this.fabricObject.height;
|
|
@@ -2553,28 +2585,42 @@ class Text extends AbstractCanvasObject {
|
|
|
2553
2585
|
onTextChange() {
|
|
2554
2586
|
this.data.userText = this.fabricObject.text;
|
|
2555
2587
|
this.text$.next(this.fabricObject.text);
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2588
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2589
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2590
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2591
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2592
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2593
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2594
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2595
|
+
this.redraw();
|
|
2596
|
+
}
|
|
2559
2597
|
}
|
|
2560
2598
|
}
|
|
2561
2599
|
onDelete() {
|
|
2562
2600
|
this.removeBullets();
|
|
2563
2601
|
}
|
|
2564
2602
|
onSelectionChange() {
|
|
2565
|
-
this.updateTextSelection();
|
|
2603
|
+
this.updateTextSelection(false);
|
|
2566
2604
|
}
|
|
2567
2605
|
hasTextSelection() {
|
|
2568
2606
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2569
2607
|
}
|
|
2570
|
-
updateTextSelection() {
|
|
2608
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2571
2609
|
if (this.canvas.headless)
|
|
2572
2610
|
return;
|
|
2573
2611
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2574
|
-
|
|
2612
|
+
if (notifyCanvas) {
|
|
2613
|
+
this.notifyCanvasOfTextChanges();
|
|
2614
|
+
}
|
|
2575
2615
|
}
|
|
2576
2616
|
notifyCanvasOfTextChanges() {
|
|
2577
|
-
this.
|
|
2617
|
+
if (this.notifyCanvasTimeout) {
|
|
2618
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2619
|
+
}
|
|
2620
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2621
|
+
this.canvas.notifyTextChanged();
|
|
2622
|
+
this.notifyCanvasTimeout = null;
|
|
2623
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2578
2624
|
}
|
|
2579
2625
|
}
|
|
2580
2626
|
Text.NEW_LINE = "\n";
|
|
@@ -2740,6 +2786,7 @@ class AbstractInteractiveCanvas {
|
|
|
2740
2786
|
this.devicePixelRatio = devicePixelRatio;
|
|
2741
2787
|
this.fontLibrary = new FontLibrary();
|
|
2742
2788
|
this.dimensionChangeRejections$ = new rxjs.Subject();
|
|
2789
|
+
this.redrawScheduled = false;
|
|
2743
2790
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2744
2791
|
/**
|
|
2745
2792
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -3087,25 +3134,12 @@ class AbstractInteractiveCanvas {
|
|
|
3087
3134
|
originY: "top",
|
|
3088
3135
|
src: imageUrl,
|
|
3089
3136
|
crossOrigin: "anonymous",
|
|
3090
|
-
/*toObject: function () {
|
|
3091
|
-
return {
|
|
3092
|
-
...fabric.Object.prototype.toObject.call(this),
|
|
3093
|
-
crossOrigin: this.crossOrigin,
|
|
3094
|
-
src: this.src,
|
|
3095
|
-
id: id
|
|
3096
|
-
};
|
|
3097
|
-
},*/
|
|
3098
3137
|
});
|
|
3099
|
-
// fabricImage.toDataURL({ format: 'png' });
|
|
3100
3138
|
yield fabricImage.setSrc(imageUrl, options);
|
|
3101
3139
|
this.fabricCanvas.add(fabricImage);
|
|
3102
3140
|
const canvasObjectData = new CanvasObjectData(exports.ObjectType.IMAGE, id);
|
|
3103
3141
|
canvasObjectData.imagePath = imageUrl;
|
|
3104
|
-
console.log("Assigned imagePath:", canvasObjectData.imagePath);
|
|
3105
3142
|
const object = new Image(this, fabricImage, canvasObjectData);
|
|
3106
|
-
console.log("Created Image object with imagePath:", object);
|
|
3107
|
-
object.data.imagePath = imageUrl;
|
|
3108
|
-
object.imageUrl$.next(imageUrl);
|
|
3109
3143
|
this.trackNewObject(object);
|
|
3110
3144
|
this.syncObjectList();
|
|
3111
3145
|
return object;
|
|
@@ -3163,6 +3197,7 @@ class AbstractInteractiveCanvas {
|
|
|
3163
3197
|
fontFamily: profile.defaultFont,
|
|
3164
3198
|
textAlign: profile.defaultTextAlignment,
|
|
3165
3199
|
hoverCursor: "move",
|
|
3200
|
+
moveCursor: 'move',
|
|
3166
3201
|
multiLine: true,
|
|
3167
3202
|
lineHeight: profile.defaultLineHeight,
|
|
3168
3203
|
charSpacing: profile.defaultCharacterSpacing,
|
|
@@ -3617,7 +3652,16 @@ class AbstractInteractiveCanvas {
|
|
|
3617
3652
|
* Redraw the canvas.
|
|
3618
3653
|
*/
|
|
3619
3654
|
redraw() {
|
|
3620
|
-
this.fabricCanvas.requestRenderAll();
|
|
3655
|
+
//this.fabricCanvas.requestRenderAll();
|
|
3656
|
+
if (this.redrawScheduled) {
|
|
3657
|
+
return; // Already scheduled, don't schedule again
|
|
3658
|
+
}
|
|
3659
|
+
this.redrawScheduled = true;
|
|
3660
|
+
// Use requestAnimationFrame for smoother rendering and automatic batching
|
|
3661
|
+
requestAnimationFrame(() => {
|
|
3662
|
+
this.fabricCanvas.requestRenderAll();
|
|
3663
|
+
this.redrawScheduled = false;
|
|
3664
|
+
});
|
|
3621
3665
|
}
|
|
3622
3666
|
syncObjectList() {
|
|
3623
3667
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -9,6 +9,7 @@ export declare class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
9
9
|
private repositionCutoffs;
|
|
10
10
|
private repositionBackgrounds;
|
|
11
11
|
private resizeCanvas;
|
|
12
|
+
private dimensionsLayoutScheduled;
|
|
12
13
|
constructor(canvas: IInteractiveCanvas);
|
|
13
14
|
onCanvasDimensionsChange(): void;
|
|
14
15
|
onTextChange(): void;
|
|
@@ -8,7 +8,7 @@ export declare abstract class AbstractCanvasObject<T extends FabricObject> imple
|
|
|
8
8
|
protected pinToBottomDistance: number;
|
|
9
9
|
protected canvas: IInteractiveCanvas;
|
|
10
10
|
protected fabricObject: T;
|
|
11
|
-
data: CanvasObjectData;
|
|
11
|
+
protected data: CanvasObjectData;
|
|
12
12
|
private readonly dimensions$;
|
|
13
13
|
private readonly opacity$;
|
|
14
14
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IDimensionRestrictions, IInteractiveCanvas, IMutableImage, IMutablePosition, IPosition } from "../..";
|
|
2
2
|
import { ICanvasObject } from "../canvas-object.interface";
|
|
3
3
|
import { AbstractCanvasObject } from "../abstract-canvas-object";
|
|
4
|
-
import {
|
|
4
|
+
import { Observable } from "rxjs";
|
|
5
5
|
import { CanvasObjectData } from "../canvas-object-data";
|
|
6
6
|
import { FabricImage } from "fabric";
|
|
7
7
|
/**
|
|
@@ -12,7 +12,7 @@ export interface IImage extends ICanvasObject, IMutableImage, IMutablePosition {
|
|
|
12
12
|
export declare class Image extends AbstractCanvasObject<FabricImage> implements IImage {
|
|
13
13
|
private readonly DEFAULT_RESTRICTIONS;
|
|
14
14
|
private layoutWidth;
|
|
15
|
-
imageUrl
|
|
15
|
+
private readonly imageUrl$;
|
|
16
16
|
constructor(canvas: IInteractiveCanvas, fabricObject: FabricImage, persistedField?: CanvasObjectData);
|
|
17
17
|
/**
|
|
18
18
|
* @override
|
|
@@ -63,6 +63,8 @@ export declare class Text extends AbstractCanvasObject<ExtendedFabricTextbox> im
|
|
|
63
63
|
private readonly text$;
|
|
64
64
|
private readonly textSelection$;
|
|
65
65
|
static readonly NEW_LINE: string;
|
|
66
|
+
private notifyCanvasTimeout;
|
|
67
|
+
private readonly NOTIFY_DELAY_MS;
|
|
66
68
|
constructor(canvas: IInteractiveCanvas, fabricObject: fabric.Textbox, persistedField?: CanvasObjectData);
|
|
67
69
|
/**
|
|
68
70
|
* @override
|