@leapfrog/interactive-canvas 2.0.25-beta-8 → 2.0.25-beta-9

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,15 @@ 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) => {
236
+ var _a;
237
+ const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
238
+ if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
239
+ object.setCoords();
240
+ }
241
+ });
242
+ canvas.redraw();
235
243
  }
236
- canvas.getObjectsInternal().forEach((object) => object.setCoords());
237
- canvas.redraw();
238
244
  }
239
245
  }
240
246
 
@@ -262,13 +268,21 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
262
268
  this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
263
269
  this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
264
270
  this.resizeCanvas = new ResizeCanvasLayoutFunction();
271
+ this.dimensionsLayoutScheduled = false;
265
272
  }
266
273
  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);
274
+ if (this.dimensionsLayoutScheduled) {
275
+ return;
276
+ }
277
+ this.dimensionsLayoutScheduled = true;
278
+ Promise.resolve().then(() => {
279
+ this.pinToBottom.execute(this.canvas);
280
+ this.repositionCutoffs.execute(this.canvas);
281
+ this.repositionBorders.execute(this.canvas);
282
+ this.repositionBackgrounds.execute(this.canvas);
283
+ this.redrawAll.execute(this.canvas);
284
+ this.dimensionsLayoutScheduled = false;
285
+ });
272
286
  }
273
287
  onTextChange() {
274
288
  this.resizeCanvas.execute(this.canvas);
@@ -296,7 +310,7 @@ class StackObjectsLayoutFunction {
296
310
  const footerObjects = objects[0];
297
311
  const bodyObjects = objects[1];
298
312
  const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
299
- canvas.redraw();
313
+ // canvas.redraw();
300
314
  //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
315
  this.applyWidths(allObjects, bodyWidth);
302
316
  const bodyRows = this.getRows(bodyObjects);
@@ -383,7 +397,12 @@ class StackObjectsLayoutFunction {
383
397
  object.setPosition({
384
398
  width: actualWidth
385
399
  });
386
- object.updateDimensions();
400
+ //object.updateDimensions();
401
+ _(objects).forEach((object) => {
402
+ if (object.getType() !== ObjectType.VERTICAL_RULE) {
403
+ object.updateDimensions();
404
+ }
405
+ });
387
406
  });
388
407
  }
389
408
  adjustCanvasSize(canvas, requiredHeight) {
@@ -456,6 +475,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
456
475
  this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
457
476
  this.stackObjects = new StackObjectsLayoutFunction();
458
477
  this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
478
+ this.layoutBatchScheduled = false;
459
479
  }
460
480
  /**
461
481
  * @override
@@ -497,12 +517,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
497
517
  this.stackAndRedraw();
498
518
  }
499
519
  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);
520
+ if (this.layoutBatchScheduled) {
521
+ return;
522
+ }
523
+ this.layoutBatchScheduled = true;
524
+ Promise.resolve().then(() => {
525
+ this.stackObjects.execute(this.canvas);
526
+ this.repositionCutoffs.execute(this.canvas);
527
+ this.repositionBorders.execute(this.canvas);
528
+ this.repositionBackgrounds.execute(this.canvas);
529
+ this.updateObjectDimensions.execute(this.canvas);
530
+ this.redrawAll.execute(this.canvas);
531
+ this.layoutBatchScheduled = false;
532
+ });
506
533
  }
507
534
  }
508
535
 
@@ -1780,15 +1807,43 @@ var ObjectType;
1780
1807
  class Text extends AbstractCanvasObject {
1781
1808
  constructor(canvas, fabricObject, persistedField) {
1782
1809
  super(ObjectType.TEXT, canvas, fabricObject, persistedField);
1810
+ this.notifyCanvasTimeout = null;
1811
+ this.NOTIFY_DELAY_MS = 150;
1783
1812
  this.canvas = canvas;
1784
1813
  this.fontLibrary = canvas.getFontLibrary();
1785
1814
  this.baseComponentHeight = this.fabricObject.height;
1786
1815
  this.text$ = new BehaviorSubject(this.fabricObject.text);
1787
1816
  this.data.userText = this.fabricObject.text;
1788
1817
  this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1789
- this.fabricObject.set({ strokeWidth: 0 });
1818
+ //this.fabricObject.set({strokeWidth: 0});
1819
+ this.fabricObject.set({
1820
+ strokeWidth: 0,
1821
+ perPixelTargetFind: false // Disable per-pixel detection for this object
1822
+ });
1790
1823
  this.fabricObject.on("changed", () => this.onTextChange());
1791
1824
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
1825
+ this.fabricObject.containsPoint.bind(this.fabricObject);
1826
+ this.fabricObject.containsPoint = function (point) {
1827
+ const coords = this.getCoords();
1828
+ if (coords.length !== 4)
1829
+ return false;
1830
+ let inside = false;
1831
+ for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
1832
+ const xi = coords[i].x, yi = coords[i].y;
1833
+ const xj = coords[j].x, yj = coords[j].y;
1834
+ const intersect = ((yi > point.y) !== (yj > point.y))
1835
+ && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
1836
+ if (intersect)
1837
+ inside = !inside;
1838
+ }
1839
+ return inside;
1840
+ };
1841
+ this.fabricObject.on("mousedown", (e) => {
1842
+ var _a;
1843
+ if (!this.fabricObject.isEditing) {
1844
+ (_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
1845
+ }
1846
+ });
1792
1847
  this.applyBulletListMonkeyPatches();
1793
1848
  }
1794
1849
  /**
@@ -2508,9 +2563,16 @@ class Text extends AbstractCanvasObject {
2508
2563
  const profile = this.canvas.getProfile();
2509
2564
  const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
2510
2565
  this.fabricObject.set({
2566
+ lockMovementX: false,
2567
+ lockMovementY: false,
2511
2568
  lockScalingY: true,
2512
2569
  lockScalingX: this.isPinToBottom(),
2513
- lockScalingFlip: true
2570
+ lockScalingFlip: true,
2571
+ lockRotation: true,
2572
+ selectable: true,
2573
+ editable: false, // Only enable on double-click
2574
+ hoverCursor: "move", // Show move cursor by default
2575
+ moveCursor: "move",
2514
2576
  });
2515
2577
  this.fabricObject.setControlsVisibility({
2516
2578
  tl: false,
@@ -2520,35 +2582,49 @@ class Text extends AbstractCanvasObject {
2520
2582
  ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2521
2583
  mt: false,
2522
2584
  mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2523
- mb: false
2585
+ mb: false,
2586
+ mtr: false
2524
2587
  });
2525
2588
  this.redraw();
2526
2589
  }
2527
2590
  onTextChange() {
2528
2591
  this.data.userText = this.fabricObject.text;
2529
2592
  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();
2593
+ const currentLeft = this.fabricObject.left || 0;
2594
+ const canvasWidth = this.canvas.getDimensions().widthPx;
2595
+ // Skip expensive getBoundingBox if object is clearly within bounds
2596
+ if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2597
+ if (this.getBoundingBox().width > canvasWidth) {
2598
+ this.fabricObject.set({ left: 0, width: canvasWidth });
2599
+ this.redraw();
2600
+ }
2533
2601
  }
2534
2602
  }
2535
2603
  onDelete() {
2536
2604
  this.removeBullets();
2537
2605
  }
2538
2606
  onSelectionChange() {
2539
- this.updateTextSelection();
2607
+ this.updateTextSelection(false);
2540
2608
  }
2541
2609
  hasTextSelection() {
2542
2610
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2543
2611
  }
2544
- updateTextSelection() {
2612
+ updateTextSelection(notifyCanvas = true) {
2545
2613
  if (this.canvas.headless)
2546
2614
  return;
2547
2615
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2548
- this.notifyCanvasOfTextChanges();
2616
+ if (notifyCanvas) {
2617
+ this.notifyCanvasOfTextChanges();
2618
+ }
2549
2619
  }
2550
2620
  notifyCanvasOfTextChanges() {
2551
- this.canvas.notifyTextChanged();
2621
+ if (this.notifyCanvasTimeout) {
2622
+ clearTimeout(this.notifyCanvasTimeout);
2623
+ }
2624
+ this.notifyCanvasTimeout = setTimeout(() => {
2625
+ this.canvas.notifyTextChanged();
2626
+ this.notifyCanvasTimeout = null;
2627
+ }, this.NOTIFY_DELAY_MS);
2552
2628
  }
2553
2629
  }
2554
2630
  Text.NEW_LINE = "\n";
@@ -2714,6 +2790,7 @@ class AbstractInteractiveCanvas {
2714
2790
  this.devicePixelRatio = devicePixelRatio;
2715
2791
  this.fontLibrary = new FontLibrary();
2716
2792
  this.dimensionChangeRejections$ = new Subject();
2793
+ this.redrawScheduled = false;
2717
2794
  this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
2718
2795
  /**
2719
2796
  * Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
@@ -3132,6 +3209,7 @@ class AbstractInteractiveCanvas {
3132
3209
  targetFindTolerance: 4,
3133
3210
  splitByGrapheme: true,
3134
3211
  editable: false, // Only enable on double-click
3212
+ editingBorderColor: '#2196F3',
3135
3213
  padding: 0,
3136
3214
  cornerSize: 8,
3137
3215
  touchCornerSize: 12
@@ -3583,7 +3661,14 @@ class AbstractInteractiveCanvas {
3583
3661
  * Redraw the canvas.
3584
3662
  */
3585
3663
  redraw() {
3586
- this.fabricCanvas.requestRenderAll();
3664
+ if (this.redrawScheduled) {
3665
+ return;
3666
+ }
3667
+ this.redrawScheduled = true;
3668
+ requestAnimationFrame(() => {
3669
+ this.fabricCanvas.requestRenderAll();
3670
+ this.redrawScheduled = false;
3671
+ });
3587
3672
  }
3588
3673
  syncObjectList() {
3589
3674
  const newObjectList = _(this.fabricCanvas.getObjects())
@@ -253,9 +253,15 @@ 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) => {
257
+ var _a;
258
+ const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
259
+ if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
260
+ object.setCoords();
261
+ }
262
+ });
263
+ canvas.redraw();
256
264
  }
257
- canvas.getObjectsInternal().forEach((object) => object.setCoords());
258
- canvas.redraw();
259
265
  }
260
266
  }
261
267
 
@@ -283,13 +289,21 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
283
289
  this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
284
290
  this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
285
291
  this.resizeCanvas = new ResizeCanvasLayoutFunction();
292
+ this.dimensionsLayoutScheduled = false;
286
293
  }
287
294
  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);
295
+ if (this.dimensionsLayoutScheduled) {
296
+ return;
297
+ }
298
+ this.dimensionsLayoutScheduled = true;
299
+ Promise.resolve().then(() => {
300
+ this.pinToBottom.execute(this.canvas);
301
+ this.repositionCutoffs.execute(this.canvas);
302
+ this.repositionBorders.execute(this.canvas);
303
+ this.repositionBackgrounds.execute(this.canvas);
304
+ this.redrawAll.execute(this.canvas);
305
+ this.dimensionsLayoutScheduled = false;
306
+ });
293
307
  }
294
308
  onTextChange() {
295
309
  this.resizeCanvas.execute(this.canvas);
@@ -317,7 +331,7 @@ class StackObjectsLayoutFunction {
317
331
  const footerObjects = objects[0];
318
332
  const bodyObjects = objects[1];
319
333
  const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
320
- canvas.redraw();
334
+ // canvas.redraw();
321
335
  //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
336
  this.applyWidths(allObjects, bodyWidth);
323
337
  const bodyRows = this.getRows(bodyObjects);
@@ -404,7 +418,12 @@ class StackObjectsLayoutFunction {
404
418
  object.setPosition({
405
419
  width: actualWidth
406
420
  });
407
- object.updateDimensions();
421
+ //object.updateDimensions();
422
+ _(objects).forEach((object) => {
423
+ if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
424
+ object.updateDimensions();
425
+ }
426
+ });
408
427
  });
409
428
  }
410
429
  adjustCanvasSize(canvas, requiredHeight) {
@@ -477,6 +496,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
477
496
  this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
478
497
  this.stackObjects = new StackObjectsLayoutFunction();
479
498
  this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
499
+ this.layoutBatchScheduled = false;
480
500
  }
481
501
  /**
482
502
  * @override
@@ -518,12 +538,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
518
538
  this.stackAndRedraw();
519
539
  }
520
540
  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);
541
+ if (this.layoutBatchScheduled) {
542
+ return;
543
+ }
544
+ this.layoutBatchScheduled = true;
545
+ Promise.resolve().then(() => {
546
+ this.stackObjects.execute(this.canvas);
547
+ this.repositionCutoffs.execute(this.canvas);
548
+ this.repositionBorders.execute(this.canvas);
549
+ this.repositionBackgrounds.execute(this.canvas);
550
+ this.updateObjectDimensions.execute(this.canvas);
551
+ this.redrawAll.execute(this.canvas);
552
+ this.layoutBatchScheduled = false;
553
+ });
527
554
  }
528
555
  }
529
556
 
@@ -1801,15 +1828,43 @@ exports.ObjectType = void 0;
1801
1828
  class Text extends AbstractCanvasObject {
1802
1829
  constructor(canvas, fabricObject, persistedField) {
1803
1830
  super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
1831
+ this.notifyCanvasTimeout = null;
1832
+ this.NOTIFY_DELAY_MS = 150;
1804
1833
  this.canvas = canvas;
1805
1834
  this.fontLibrary = canvas.getFontLibrary();
1806
1835
  this.baseComponentHeight = this.fabricObject.height;
1807
1836
  this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
1808
1837
  this.data.userText = this.fabricObject.text;
1809
1838
  this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1810
- this.fabricObject.set({ strokeWidth: 0 });
1839
+ //this.fabricObject.set({strokeWidth: 0});
1840
+ this.fabricObject.set({
1841
+ strokeWidth: 0,
1842
+ perPixelTargetFind: false // Disable per-pixel detection for this object
1843
+ });
1811
1844
  this.fabricObject.on("changed", () => this.onTextChange());
1812
1845
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
1846
+ this.fabricObject.containsPoint.bind(this.fabricObject);
1847
+ this.fabricObject.containsPoint = function (point) {
1848
+ const coords = this.getCoords();
1849
+ if (coords.length !== 4)
1850
+ return false;
1851
+ let inside = false;
1852
+ for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
1853
+ const xi = coords[i].x, yi = coords[i].y;
1854
+ const xj = coords[j].x, yj = coords[j].y;
1855
+ const intersect = ((yi > point.y) !== (yj > point.y))
1856
+ && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
1857
+ if (intersect)
1858
+ inside = !inside;
1859
+ }
1860
+ return inside;
1861
+ };
1862
+ this.fabricObject.on("mousedown", (e) => {
1863
+ var _a;
1864
+ if (!this.fabricObject.isEditing) {
1865
+ (_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
1866
+ }
1867
+ });
1813
1868
  this.applyBulletListMonkeyPatches();
1814
1869
  }
1815
1870
  /**
@@ -2529,9 +2584,16 @@ class Text extends AbstractCanvasObject {
2529
2584
  const profile = this.canvas.getProfile();
2530
2585
  const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
2531
2586
  this.fabricObject.set({
2587
+ lockMovementX: false,
2588
+ lockMovementY: false,
2532
2589
  lockScalingY: true,
2533
2590
  lockScalingX: this.isPinToBottom(),
2534
- lockScalingFlip: true
2591
+ lockScalingFlip: true,
2592
+ lockRotation: true,
2593
+ selectable: true,
2594
+ editable: false, // Only enable on double-click
2595
+ hoverCursor: "move", // Show move cursor by default
2596
+ moveCursor: "move",
2535
2597
  });
2536
2598
  this.fabricObject.setControlsVisibility({
2537
2599
  tl: false,
@@ -2541,35 +2603,49 @@ class Text extends AbstractCanvasObject {
2541
2603
  ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2542
2604
  mt: false,
2543
2605
  mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2544
- mb: false
2606
+ mb: false,
2607
+ mtr: false
2545
2608
  });
2546
2609
  this.redraw();
2547
2610
  }
2548
2611
  onTextChange() {
2549
2612
  this.data.userText = this.fabricObject.text;
2550
2613
  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();
2614
+ const currentLeft = this.fabricObject.left || 0;
2615
+ const canvasWidth = this.canvas.getDimensions().widthPx;
2616
+ // Skip expensive getBoundingBox if object is clearly within bounds
2617
+ if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2618
+ if (this.getBoundingBox().width > canvasWidth) {
2619
+ this.fabricObject.set({ left: 0, width: canvasWidth });
2620
+ this.redraw();
2621
+ }
2554
2622
  }
2555
2623
  }
2556
2624
  onDelete() {
2557
2625
  this.removeBullets();
2558
2626
  }
2559
2627
  onSelectionChange() {
2560
- this.updateTextSelection();
2628
+ this.updateTextSelection(false);
2561
2629
  }
2562
2630
  hasTextSelection() {
2563
2631
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2564
2632
  }
2565
- updateTextSelection() {
2633
+ updateTextSelection(notifyCanvas = true) {
2566
2634
  if (this.canvas.headless)
2567
2635
  return;
2568
2636
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2569
- this.notifyCanvasOfTextChanges();
2637
+ if (notifyCanvas) {
2638
+ this.notifyCanvasOfTextChanges();
2639
+ }
2570
2640
  }
2571
2641
  notifyCanvasOfTextChanges() {
2572
- this.canvas.notifyTextChanged();
2642
+ if (this.notifyCanvasTimeout) {
2643
+ clearTimeout(this.notifyCanvasTimeout);
2644
+ }
2645
+ this.notifyCanvasTimeout = setTimeout(() => {
2646
+ this.canvas.notifyTextChanged();
2647
+ this.notifyCanvasTimeout = null;
2648
+ }, this.NOTIFY_DELAY_MS);
2573
2649
  }
2574
2650
  }
2575
2651
  Text.NEW_LINE = "\n";
@@ -2735,6 +2811,7 @@ class AbstractInteractiveCanvas {
2735
2811
  this.devicePixelRatio = devicePixelRatio;
2736
2812
  this.fontLibrary = new FontLibrary();
2737
2813
  this.dimensionChangeRejections$ = new rxjs.Subject();
2814
+ this.redrawScheduled = false;
2738
2815
  this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
2739
2816
  /**
2740
2817
  * Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
@@ -3153,6 +3230,7 @@ class AbstractInteractiveCanvas {
3153
3230
  targetFindTolerance: 4,
3154
3231
  splitByGrapheme: true,
3155
3232
  editable: false, // Only enable on double-click
3233
+ editingBorderColor: '#2196F3',
3156
3234
  padding: 0,
3157
3235
  cornerSize: 8,
3158
3236
  touchCornerSize: 12
@@ -3604,7 +3682,14 @@ class AbstractInteractiveCanvas {
3604
3682
  * Redraw the canvas.
3605
3683
  */
3606
3684
  redraw() {
3607
- this.fabricCanvas.requestRenderAll();
3685
+ if (this.redrawScheduled) {
3686
+ return;
3687
+ }
3688
+ this.redrawScheduled = true;
3689
+ requestAnimationFrame(() => {
3690
+ this.fabricCanvas.requestRenderAll();
3691
+ this.redrawScheduled = false;
3692
+ });
3608
3693
  }
3609
3694
  syncObjectList() {
3610
3695
  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;
@@ -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-8",
3
+ "version": "2.0.25-beta-9",
4
4
  "description": "Leapfrog interactive canvas",
5
5
  "files": [
6
6
  "dist"