@leapfrog/interactive-canvas 2.0.25-beta-5 → 2.0.25-beta-7

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.
@@ -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
- canvas.getObjectsInternal().forEach((object) => object.setCoords());
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();
236
246
  }
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
- this.pinToBottom.execute(this.canvas);
268
- this.repositionCutoffs.execute(this.canvas);
269
- this.repositionBorders.execute(this.canvas);
270
- this.repositionBackgrounds.execute(this.canvas);
271
- this.redrawAll.execute(this.canvas);
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
- object.updateDimensions();
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
- this.stackObjects.execute(this.canvas);
501
- this.repositionCutoffs.execute(this.canvas);
502
- this.repositionBorders.execute(this.canvas);
503
- this.repositionBackgrounds.execute(this.canvas);
504
- this.updateObjectDimensions.execute(this.canvas);
505
- this.redrawAll.execute(this.canvas);
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
 
@@ -1780,12 +1814,16 @@ var ObjectType;
1780
1814
  class Text extends AbstractCanvasObject {
1781
1815
  constructor(canvas, fabricObject, persistedField) {
1782
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
1783
1820
  this.canvas = canvas;
1784
1821
  this.fontLibrary = canvas.getFontLibrary();
1785
1822
  this.baseComponentHeight = this.fabricObject.height;
1786
1823
  this.text$ = new BehaviorSubject(this.fabricObject.text);
1787
1824
  this.data.userText = this.fabricObject.text;
1788
1825
  this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1826
+ console.log(`inside constructor in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
1789
1827
  this.fabricObject.set({ strokeWidth: 0 });
1790
1828
  this.fabricObject.on("changed", () => this.onTextChange());
1791
1829
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
@@ -2525,32 +2563,54 @@ class Text extends AbstractCanvasObject {
2525
2563
  this.redraw();
2526
2564
  }
2527
2565
  onTextChange() {
2566
+ console.log(`inside onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2528
2567
  this.data.userText = this.fabricObject.text;
2529
2568
  this.text$.next(this.fabricObject.text);
2530
- if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
2531
- this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
2532
- this.redraw();
2569
+ // Only check width bounds if text is close to canvas edge (performance optimization)
2570
+ const currentLeft = this.fabricObject.left || 0;
2571
+ const canvasWidth = this.canvas.getDimensions().widthPx;
2572
+ // Skip expensive getBoundingBox if object is clearly within bounds
2573
+ if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2574
+ console.log(`inside if onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2575
+ if (this.getBoundingBox().width > canvasWidth) {
2576
+ this.fabricObject.set({ left: 0, width: canvasWidth });
2577
+ this.redraw();
2578
+ }
2533
2579
  }
2580
+ console.log(`end of onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2534
2581
  }
2535
2582
  onDelete() {
2536
2583
  this.removeBullets();
2537
2584
  }
2538
2585
  onSelectionChange() {
2586
+ console.log(`start of onSelectionChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2539
2587
  this.updateTextSelection(false);
2540
2588
  }
2541
2589
  hasTextSelection() {
2542
2590
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2543
2591
  }
2544
2592
  updateTextSelection(notifyCanvas = true) {
2593
+ console.log(`start of updateTextSelection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2545
2594
  if (this.canvas.headless)
2546
2595
  return;
2547
2596
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2548
2597
  if (notifyCanvas) {
2598
+ console.log(`inside if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2549
2599
  this.notifyCanvasOfTextChanges();
2550
2600
  }
2551
2601
  }
2552
2602
  notifyCanvasOfTextChanges() {
2553
- this.canvas.notifyTextChanged();
2603
+ console.log(`start of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2604
+ if (this.notifyCanvasTimeout) {
2605
+ console.log(`inside timeout if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2606
+ clearTimeout(this.notifyCanvasTimeout);
2607
+ }
2608
+ this.notifyCanvasTimeout = setTimeout(() => {
2609
+ console.log(`start of setTimeout in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2610
+ this.canvas.notifyTextChanged();
2611
+ this.notifyCanvasTimeout = null;
2612
+ }, this.NOTIFY_DELAY_MS);
2613
+ console.log(`end of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2554
2614
  }
2555
2615
  }
2556
2616
  Text.NEW_LINE = "\n";
@@ -2558,6 +2618,7 @@ class TextSelection {
2558
2618
  constructor(fabricObject, fontLibrary) {
2559
2619
  this.fabricObject = fabricObject;
2560
2620
  this.fontLibrary = fontLibrary;
2621
+ console.log(`constructor of textselection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2561
2622
  this.start = this.fabricObject.selectionStart;
2562
2623
  this.end = this.fabricObject.selectionEnd;
2563
2624
  this.length = this.end - this.start;
@@ -2716,6 +2777,7 @@ class AbstractInteractiveCanvas {
2716
2777
  this.devicePixelRatio = devicePixelRatio;
2717
2778
  this.fontLibrary = new FontLibrary();
2718
2779
  this.dimensionChangeRejections$ = new Subject();
2780
+ this.redrawScheduled = false;
2719
2781
  this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
2720
2782
  /**
2721
2783
  * Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
@@ -3581,7 +3643,16 @@ class AbstractInteractiveCanvas {
3581
3643
  * Redraw the canvas.
3582
3644
  */
3583
3645
  redraw() {
3584
- this.fabricCanvas.requestRenderAll();
3646
+ //this.fabricCanvas.requestRenderAll();
3647
+ if (this.redrawScheduled) {
3648
+ return; // Already scheduled, don't schedule again
3649
+ }
3650
+ this.redrawScheduled = true;
3651
+ // Use requestAnimationFrame for smoother rendering and automatic batching
3652
+ requestAnimationFrame(() => {
3653
+ this.fabricCanvas.requestRenderAll();
3654
+ this.redrawScheduled = false;
3655
+ });
3585
3656
  }
3586
3657
  syncObjectList() {
3587
3658
  const newObjectList = _(this.fabricCanvas.getObjects())
@@ -3897,6 +3968,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3897
3968
  });
3898
3969
  }
3899
3970
  onFabricSelectionChanged(event) {
3971
+ console.log(`start of onfabricselectionchange in ic.ts - ${MouseEvent}- ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3900
3972
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3901
3973
  const selection = new SelectionData(selected);
3902
3974
  if (event) {
@@ -3907,6 +3979,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3907
3979
  }
3908
3980
  enterTextSelectionModeIfRequired() {
3909
3981
  var _a, _b;
3982
+ console.log(`start of enterTextSelectionModeIfRequired in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3910
3983
  if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
3911
3984
  return;
3912
3985
  if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== ObjectType.TEXT)
@@ -3915,6 +3988,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3915
3988
  text.enterEditing();
3916
3989
  }
3917
3990
  onTextEditing() {
3991
+ console.log(`start of onTextEditing in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3918
3992
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3919
3993
  this.selection$.next(new SelectionData(selected));
3920
3994
  }
@@ -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
- canvas.getObjectsInternal().forEach((object) => object.setCoords());
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();
257
267
  }
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
- this.pinToBottom.execute(this.canvas);
289
- this.repositionCutoffs.execute(this.canvas);
290
- this.repositionBorders.execute(this.canvas);
291
- this.repositionBackgrounds.execute(this.canvas);
292
- this.redrawAll.execute(this.canvas);
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
- object.updateDimensions();
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
- this.stackObjects.execute(this.canvas);
522
- this.repositionCutoffs.execute(this.canvas);
523
- this.repositionBorders.execute(this.canvas);
524
- this.repositionBackgrounds.execute(this.canvas);
525
- this.updateObjectDimensions.execute(this.canvas);
526
- this.redrawAll.execute(this.canvas);
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
 
@@ -1801,12 +1835,16 @@ exports.ObjectType = void 0;
1801
1835
  class Text extends AbstractCanvasObject {
1802
1836
  constructor(canvas, fabricObject, persistedField) {
1803
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
1804
1841
  this.canvas = canvas;
1805
1842
  this.fontLibrary = canvas.getFontLibrary();
1806
1843
  this.baseComponentHeight = this.fabricObject.height;
1807
1844
  this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
1808
1845
  this.data.userText = this.fabricObject.text;
1809
1846
  this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1847
+ console.log(`inside constructor in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
1810
1848
  this.fabricObject.set({ strokeWidth: 0 });
1811
1849
  this.fabricObject.on("changed", () => this.onTextChange());
1812
1850
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
@@ -2546,32 +2584,54 @@ class Text extends AbstractCanvasObject {
2546
2584
  this.redraw();
2547
2585
  }
2548
2586
  onTextChange() {
2587
+ console.log(`inside onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2549
2588
  this.data.userText = this.fabricObject.text;
2550
2589
  this.text$.next(this.fabricObject.text);
2551
- if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
2552
- this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
2553
- this.redraw();
2590
+ // Only check width bounds if text is close to canvas edge (performance optimization)
2591
+ const currentLeft = this.fabricObject.left || 0;
2592
+ const canvasWidth = this.canvas.getDimensions().widthPx;
2593
+ // Skip expensive getBoundingBox if object is clearly within bounds
2594
+ if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2595
+ console.log(`inside if onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2596
+ if (this.getBoundingBox().width > canvasWidth) {
2597
+ this.fabricObject.set({ left: 0, width: canvasWidth });
2598
+ this.redraw();
2599
+ }
2554
2600
  }
2601
+ console.log(`end of onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2555
2602
  }
2556
2603
  onDelete() {
2557
2604
  this.removeBullets();
2558
2605
  }
2559
2606
  onSelectionChange() {
2607
+ console.log(`start of onSelectionChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2560
2608
  this.updateTextSelection(false);
2561
2609
  }
2562
2610
  hasTextSelection() {
2563
2611
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2564
2612
  }
2565
2613
  updateTextSelection(notifyCanvas = true) {
2614
+ console.log(`start of updateTextSelection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2566
2615
  if (this.canvas.headless)
2567
2616
  return;
2568
2617
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2569
2618
  if (notifyCanvas) {
2619
+ console.log(`inside if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2570
2620
  this.notifyCanvasOfTextChanges();
2571
2621
  }
2572
2622
  }
2573
2623
  notifyCanvasOfTextChanges() {
2574
- this.canvas.notifyTextChanged();
2624
+ console.log(`start of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2625
+ if (this.notifyCanvasTimeout) {
2626
+ console.log(`inside timeout if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2627
+ clearTimeout(this.notifyCanvasTimeout);
2628
+ }
2629
+ this.notifyCanvasTimeout = setTimeout(() => {
2630
+ console.log(`start of setTimeout in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2631
+ this.canvas.notifyTextChanged();
2632
+ this.notifyCanvasTimeout = null;
2633
+ }, this.NOTIFY_DELAY_MS);
2634
+ console.log(`end of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2575
2635
  }
2576
2636
  }
2577
2637
  Text.NEW_LINE = "\n";
@@ -2579,6 +2639,7 @@ class TextSelection {
2579
2639
  constructor(fabricObject, fontLibrary) {
2580
2640
  this.fabricObject = fabricObject;
2581
2641
  this.fontLibrary = fontLibrary;
2642
+ console.log(`constructor of textselection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2582
2643
  this.start = this.fabricObject.selectionStart;
2583
2644
  this.end = this.fabricObject.selectionEnd;
2584
2645
  this.length = this.end - this.start;
@@ -2737,6 +2798,7 @@ class AbstractInteractiveCanvas {
2737
2798
  this.devicePixelRatio = devicePixelRatio;
2738
2799
  this.fontLibrary = new FontLibrary();
2739
2800
  this.dimensionChangeRejections$ = new rxjs.Subject();
2801
+ this.redrawScheduled = false;
2740
2802
  this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
2741
2803
  /**
2742
2804
  * Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
@@ -3602,7 +3664,16 @@ class AbstractInteractiveCanvas {
3602
3664
  * Redraw the canvas.
3603
3665
  */
3604
3666
  redraw() {
3605
- this.fabricCanvas.requestRenderAll();
3667
+ //this.fabricCanvas.requestRenderAll();
3668
+ if (this.redrawScheduled) {
3669
+ return; // Already scheduled, don't schedule again
3670
+ }
3671
+ this.redrawScheduled = true;
3672
+ // Use requestAnimationFrame for smoother rendering and automatic batching
3673
+ requestAnimationFrame(() => {
3674
+ this.fabricCanvas.requestRenderAll();
3675
+ this.redrawScheduled = false;
3676
+ });
3606
3677
  }
3607
3678
  syncObjectList() {
3608
3679
  const newObjectList = _(this.fabricCanvas.getObjects())
@@ -3918,6 +3989,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3918
3989
  });
3919
3990
  }
3920
3991
  onFabricSelectionChanged(event) {
3992
+ console.log(`start of onfabricselectionchange in ic.ts - ${MouseEvent}- ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3921
3993
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3922
3994
  const selection = new SelectionData(selected);
3923
3995
  if (event) {
@@ -3928,6 +4000,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3928
4000
  }
3929
4001
  enterTextSelectionModeIfRequired() {
3930
4002
  var _a, _b;
4003
+ console.log(`start of enterTextSelectionModeIfRequired in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3931
4004
  if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
3932
4005
  return;
3933
4006
  if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== exports.ObjectType.TEXT)
@@ -3936,6 +4009,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3936
4009
  text.enterEditing();
3937
4010
  }
3938
4011
  onTextEditing() {
4012
+ console.log(`start of onTextEditing in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3939
4013
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3940
4014
  this.selection$.next(new SelectionData(selected));
3941
4015
  }
@@ -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;
@@ -36,5 +36,6 @@ export declare class StandardLayoutManager extends AbstractLayoutManager {
36
36
  * @override
37
37
  */
38
38
  onMarginChange(): void;
39
+ private layoutBatchScheduled;
39
40
  private stackAndRedraw;
40
41
  }
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leapfrog/interactive-canvas",
3
- "version": "2.0.25-beta-5",
3
+ "version": "2.0.25-beta-7",
4
4
  "description": "Leapfrog interactive canvas",
5
5
  "files": [
6
6
  "dist"