@bwp-web/canvas 0.4.1 → 0.4.3
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/Canvas/Canvas.d.ts +11 -1
- package/dist/Canvas/Canvas.d.ts.map +1 -1
- package/dist/background.d.ts +15 -3
- package/dist/background.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/shared.d.ts +3 -2
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/hooks/useCanvasClick.d.ts +27 -0
- package/dist/hooks/useCanvasClick.d.ts.map +1 -0
- package/dist/hooks/useCanvasEvents.d.ts +35 -0
- package/dist/hooks/useCanvasEvents.d.ts.map +1 -0
- package/dist/hooks/useCanvasTooltip.d.ts +45 -0
- package/dist/hooks/useCanvasTooltip.d.ts.map +1 -0
- package/dist/hooks/useEditCanvas.d.ts +18 -1
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useObjectOverlay.d.ts +49 -0
- package/dist/hooks/useObjectOverlay.d.ts.map +1 -0
- package/dist/hooks/useViewCanvas.d.ts +3 -1
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +471 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +399 -32
- package/dist/index.js.map +1 -1
- package/dist/interactions/dragToCreate.d.ts +2 -0
- package/dist/interactions/dragToCreate.d.ts.map +1 -1
- package/dist/interactions/drawToCreate.d.ts +7 -1
- package/dist/interactions/drawToCreate.d.ts.map +1 -1
- package/dist/serialization.d.ts +12 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/viewport.d.ts +12 -1
- package/dist/viewport.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,12 @@ __export(index_exports, {
|
|
|
27
27
|
DEFAULT_DRAG_SHAPE_STYLE: () => DEFAULT_DRAG_SHAPE_STYLE,
|
|
28
28
|
DEFAULT_GUIDELINE_SHAPE_STYLE: () => DEFAULT_GUIDELINE_SHAPE_STYLE,
|
|
29
29
|
DEFAULT_SHAPE_STYLE: () => DEFAULT_SHAPE_STYLE,
|
|
30
|
+
FabricCanvas: () => import_fabric20.Canvas,
|
|
31
|
+
FabricImage: () => import_fabric20.FabricImage,
|
|
32
|
+
FabricObject: () => import_fabric20.FabricObject,
|
|
33
|
+
Point: () => import_fabric20.Point,
|
|
34
|
+
Polygon: () => import_fabric20.Polygon,
|
|
35
|
+
Rect: () => import_fabric20.Rect,
|
|
30
36
|
createCircle: () => createCircle,
|
|
31
37
|
createCircleAtPoint: () => createCircleAtPoint,
|
|
32
38
|
createPolygon: () => createPolygon,
|
|
@@ -51,6 +57,7 @@ __export(index_exports, {
|
|
|
51
57
|
fitViewportToBackground: () => fitViewportToBackground,
|
|
52
58
|
getBackgroundInverted: () => getBackgroundInverted,
|
|
53
59
|
getBackgroundOpacity: () => getBackgroundOpacity,
|
|
60
|
+
getBackgroundSrc: () => getBackgroundSrc,
|
|
54
61
|
getBaseStrokeWidth: () => getBaseStrokeWidth,
|
|
55
62
|
getSnapPoints: () => getSnapPoints,
|
|
56
63
|
loadCanvas: () => loadCanvas,
|
|
@@ -62,13 +69,21 @@ __export(index_exports, {
|
|
|
62
69
|
setBackgroundInverted: () => setBackgroundInverted,
|
|
63
70
|
setBackgroundOpacity: () => setBackgroundOpacity,
|
|
64
71
|
snapCursorPoint: () => snapCursorPoint,
|
|
72
|
+
useCanvasClick: () => useCanvasClick,
|
|
73
|
+
useCanvasEvents: () => useCanvasEvents,
|
|
74
|
+
useCanvasTooltip: () => useCanvasTooltip,
|
|
65
75
|
useEditCanvas: () => useEditCanvas,
|
|
66
|
-
|
|
76
|
+
useObjectOverlay: () => useObjectOverlay,
|
|
77
|
+
useViewCanvas: () => useViewCanvas,
|
|
78
|
+
util: () => import_fabric20.util
|
|
67
79
|
});
|
|
68
80
|
module.exports = __toCommonJS(index_exports);
|
|
69
81
|
|
|
70
|
-
// src/
|
|
82
|
+
// src/fabricAugmentation.ts
|
|
71
83
|
var import_fabric = require("fabric");
|
|
84
|
+
|
|
85
|
+
// src/Canvas/Canvas.tsx
|
|
86
|
+
var import_fabric2 = require("fabric");
|
|
72
87
|
var import_react = require("react");
|
|
73
88
|
|
|
74
89
|
// src/keyboard.ts
|
|
@@ -95,35 +110,64 @@ function enableKeyboardShortcuts(canvas) {
|
|
|
95
110
|
// src/Canvas/Canvas.tsx
|
|
96
111
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
97
112
|
function Canvas({
|
|
98
|
-
width
|
|
99
|
-
height
|
|
113
|
+
width,
|
|
114
|
+
height,
|
|
100
115
|
className,
|
|
101
116
|
style,
|
|
102
117
|
onReady
|
|
103
118
|
}) {
|
|
104
119
|
const canvasRef = (0, import_react.useRef)(null);
|
|
120
|
+
const wrapperRef = (0, import_react.useRef)(null);
|
|
121
|
+
const isFixedSize = width !== void 0 && height !== void 0;
|
|
105
122
|
(0, import_react.useEffect)(() => {
|
|
106
123
|
const el = canvasRef.current;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
124
|
+
const wrapper = wrapperRef.current;
|
|
125
|
+
if (!el || !wrapper) return;
|
|
126
|
+
const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;
|
|
127
|
+
const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;
|
|
128
|
+
const fabricCanvas = new import_fabric2.Canvas(el, {
|
|
129
|
+
width: initialWidth,
|
|
130
|
+
height: initialHeight
|
|
131
|
+
});
|
|
111
132
|
onReady?.(fabricCanvas);
|
|
112
133
|
const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);
|
|
134
|
+
let observer;
|
|
135
|
+
let rafId = 0;
|
|
136
|
+
if (!isFixedSize) {
|
|
137
|
+
let currentWidth = initialWidth;
|
|
138
|
+
let currentHeight = initialHeight;
|
|
139
|
+
observer = new ResizeObserver((entries) => {
|
|
140
|
+
cancelAnimationFrame(rafId);
|
|
141
|
+
rafId = requestAnimationFrame(() => {
|
|
142
|
+
const entry = entries[0];
|
|
143
|
+
if (!entry) return;
|
|
144
|
+
const { width: newWidth, height: newHeight } = entry.contentRect;
|
|
145
|
+
if (newWidth > 0 && newHeight > 0 && (newWidth !== currentWidth || newHeight !== currentHeight)) {
|
|
146
|
+
currentWidth = newWidth;
|
|
147
|
+
currentHeight = newHeight;
|
|
148
|
+
fabricCanvas.setDimensions({ width: newWidth, height: newHeight });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
observer.observe(wrapper);
|
|
153
|
+
}
|
|
113
154
|
return () => {
|
|
155
|
+
cancelAnimationFrame(rafId);
|
|
156
|
+
observer?.disconnect();
|
|
114
157
|
cleanupShortcuts();
|
|
115
158
|
fabricCanvas.dispose();
|
|
116
159
|
};
|
|
117
160
|
}, []);
|
|
118
|
-
|
|
161
|
+
const wrapperStyle = isFixedSize ? { ...style } : { width: "100%", height: "100%", ...style };
|
|
162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: wrapperRef, className, style: wrapperStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("canvas", { ref: canvasRef }) });
|
|
119
163
|
}
|
|
120
164
|
|
|
121
165
|
// src/hooks/useEditCanvas.ts
|
|
122
166
|
var import_react2 = require("react");
|
|
123
|
-
var
|
|
167
|
+
var import_fabric17 = require("fabric");
|
|
124
168
|
|
|
125
169
|
// src/viewport.ts
|
|
126
|
-
var
|
|
170
|
+
var import_fabric3 = require("fabric");
|
|
127
171
|
|
|
128
172
|
// src/constants.ts
|
|
129
173
|
var DEFAULT_MIN_ZOOM = 0.2;
|
|
@@ -158,7 +202,7 @@ function touchDistance(touches) {
|
|
|
158
202
|
}
|
|
159
203
|
function touchMidpoint(touches, el) {
|
|
160
204
|
const rect = el.getBoundingClientRect();
|
|
161
|
-
return new
|
|
205
|
+
return new import_fabric3.Point(
|
|
162
206
|
(touches[0].clientX + touches[1].clientX) / 2 - rect.left,
|
|
163
207
|
(touches[0].clientY + touches[1].clientY) / 2 - rect.top
|
|
164
208
|
);
|
|
@@ -173,7 +217,7 @@ function setupWheelZoom(canvas, bounds, zoomFactor, isEnabled) {
|
|
|
173
217
|
let zoom = canvas.getZoom();
|
|
174
218
|
zoom = delta < 0 ? zoom * zoomFactor : zoom / zoomFactor;
|
|
175
219
|
zoom = Math.min(Math.max(zoom, bounds.minZoom), bounds.maxZoom);
|
|
176
|
-
canvas.zoomToPoint(new
|
|
220
|
+
canvas.zoomToPoint(new import_fabric3.Point(e.offsetX, e.offsetY), zoom);
|
|
177
221
|
};
|
|
178
222
|
canvas.on("mouse:wheel", handleWheel);
|
|
179
223
|
return handleWheel;
|
|
@@ -211,7 +255,7 @@ function setupMousePan(canvas, getMode, isEnabled) {
|
|
|
211
255
|
const dy = pos.y - lastPanY;
|
|
212
256
|
lastPanX = pos.x;
|
|
213
257
|
lastPanY = pos.y;
|
|
214
|
-
canvas.relativePan(new
|
|
258
|
+
canvas.relativePan(new import_fabric3.Point(dx, dy));
|
|
215
259
|
canvas.setCursor("grab");
|
|
216
260
|
};
|
|
217
261
|
const handleMouseUp = () => {
|
|
@@ -314,17 +358,65 @@ function enablePanAndZoom(canvas, options) {
|
|
|
314
358
|
zoomIn(step = DEFAULT_ZOOM_STEP) {
|
|
315
359
|
const z = Math.min(canvas.getZoom() + step, bounds.maxZoom);
|
|
316
360
|
canvas.zoomToPoint(
|
|
317
|
-
new
|
|
361
|
+
new import_fabric3.Point(canvas.getWidth() / 2, canvas.getHeight() / 2),
|
|
318
362
|
z
|
|
319
363
|
);
|
|
320
364
|
},
|
|
321
365
|
zoomOut(step = DEFAULT_ZOOM_STEP) {
|
|
322
366
|
const z = Math.max(canvas.getZoom() - step, bounds.minZoom);
|
|
323
367
|
canvas.zoomToPoint(
|
|
324
|
-
new
|
|
368
|
+
new import_fabric3.Point(canvas.getWidth() / 2, canvas.getHeight() / 2),
|
|
325
369
|
z
|
|
326
370
|
);
|
|
327
371
|
},
|
|
372
|
+
panToObject(object, panOpts) {
|
|
373
|
+
const zoom = canvas.getZoom();
|
|
374
|
+
const objectCenter = object.getCenterPoint();
|
|
375
|
+
const canvasCenterX = canvas.getWidth() / 2;
|
|
376
|
+
const canvasCenterY = canvas.getHeight() / 2;
|
|
377
|
+
const targetX = canvasCenterX - objectCenter.x * zoom;
|
|
378
|
+
const targetY = canvasCenterY - objectCenter.y * zoom;
|
|
379
|
+
if (!panOpts?.animate) {
|
|
380
|
+
const vt2 = canvas.viewportTransform;
|
|
381
|
+
if (!vt2) return;
|
|
382
|
+
canvas.setViewportTransform([
|
|
383
|
+
vt2[0],
|
|
384
|
+
vt2[1],
|
|
385
|
+
vt2[2],
|
|
386
|
+
vt2[3],
|
|
387
|
+
targetX,
|
|
388
|
+
targetY
|
|
389
|
+
]);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const duration = panOpts.duration ?? 300;
|
|
393
|
+
const vt = canvas.viewportTransform;
|
|
394
|
+
if (!vt) return;
|
|
395
|
+
const startX = vt[4];
|
|
396
|
+
const startY = vt[5];
|
|
397
|
+
const startTime = performance.now();
|
|
398
|
+
function step(now) {
|
|
399
|
+
const elapsed = now - startTime;
|
|
400
|
+
const t = Math.min(elapsed / duration, 1);
|
|
401
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
402
|
+
const currentX = startX + (targetX - startX) * eased;
|
|
403
|
+
const currentY = startY + (targetY - startY) * eased;
|
|
404
|
+
const currentVt = canvas.viewportTransform;
|
|
405
|
+
if (!currentVt) return;
|
|
406
|
+
canvas.setViewportTransform([
|
|
407
|
+
currentVt[0],
|
|
408
|
+
currentVt[1],
|
|
409
|
+
currentVt[2],
|
|
410
|
+
currentVt[3],
|
|
411
|
+
currentX,
|
|
412
|
+
currentY
|
|
413
|
+
]);
|
|
414
|
+
if (t < 1) {
|
|
415
|
+
requestAnimationFrame(step);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
requestAnimationFrame(step);
|
|
419
|
+
},
|
|
328
420
|
cleanup() {
|
|
329
421
|
canvas.off("mouse:wheel", handleWheel);
|
|
330
422
|
canvas.off("mouse:down", panHandlers.handleMouseDown);
|
|
@@ -339,10 +431,15 @@ function resetViewport(canvas) {
|
|
|
339
431
|
}
|
|
340
432
|
|
|
341
433
|
// src/background.ts
|
|
342
|
-
var
|
|
434
|
+
var import_fabric4 = require("fabric");
|
|
343
435
|
function getBackgroundImage(canvas) {
|
|
344
436
|
return canvas.backgroundImage;
|
|
345
437
|
}
|
|
438
|
+
function getBackgroundSrc(canvas) {
|
|
439
|
+
const bg = getBackgroundImage(canvas);
|
|
440
|
+
if (!bg) return null;
|
|
441
|
+
return bg.getSrc() || null;
|
|
442
|
+
}
|
|
346
443
|
function fitViewportToBackground(canvas, options) {
|
|
347
444
|
const bg = getBackgroundImage(canvas);
|
|
348
445
|
if (!bg) return;
|
|
@@ -379,12 +476,12 @@ function setBackgroundInverted(canvas, inverted) {
|
|
|
379
476
|
const bg = getBackgroundImage(canvas);
|
|
380
477
|
if (!bg) return;
|
|
381
478
|
const currentFilters = bg.filters ?? [];
|
|
382
|
-
const hasInvert = currentFilters.some((f) => f instanceof
|
|
479
|
+
const hasInvert = currentFilters.some((f) => f instanceof import_fabric4.filters.Invert);
|
|
383
480
|
if (inverted && !hasInvert) {
|
|
384
|
-
bg.filters = [...currentFilters, new
|
|
481
|
+
bg.filters = [...currentFilters, new import_fabric4.filters.Invert()];
|
|
385
482
|
bg.applyFilters();
|
|
386
483
|
} else if (!inverted && hasInvert) {
|
|
387
|
-
bg.filters = currentFilters.filter((f) => !(f instanceof
|
|
484
|
+
bg.filters = currentFilters.filter((f) => !(f instanceof import_fabric4.filters.Invert));
|
|
388
485
|
bg.applyFilters();
|
|
389
486
|
}
|
|
390
487
|
canvas.requestRenderAll();
|
|
@@ -392,7 +489,7 @@ function setBackgroundInverted(canvas, inverted) {
|
|
|
392
489
|
function getBackgroundInverted(canvas) {
|
|
393
490
|
const bg = getBackgroundImage(canvas);
|
|
394
491
|
if (!bg?.filters) return false;
|
|
395
|
-
return bg.filters.some((f) => f instanceof
|
|
492
|
+
return bg.filters.some((f) => f instanceof import_fabric4.filters.Invert);
|
|
396
493
|
}
|
|
397
494
|
function resizeImageUrl(url, options) {
|
|
398
495
|
const maxSize = options?.maxSize ?? DEFAULT_IMAGE_MAX_SIZE;
|
|
@@ -438,14 +535,18 @@ function resizeImageUrl(url, options) {
|
|
|
438
535
|
img.src = url;
|
|
439
536
|
});
|
|
440
537
|
}
|
|
441
|
-
async function setBackgroundImage(canvas, url,
|
|
538
|
+
async function setBackgroundImage(canvas, url, options) {
|
|
539
|
+
const prevOpacity = options?.preserveOpacity ? getBackgroundOpacity(canvas) : void 0;
|
|
442
540
|
let imageUrl = url;
|
|
443
|
-
if (
|
|
444
|
-
const result = await resizeImageUrl(url,
|
|
541
|
+
if (options !== void 0) {
|
|
542
|
+
const result = await resizeImageUrl(url, options);
|
|
445
543
|
imageUrl = result.url;
|
|
446
544
|
}
|
|
447
|
-
const img = await
|
|
545
|
+
const img = await import_fabric4.FabricImage.fromURL(imageUrl, { crossOrigin: "anonymous" });
|
|
448
546
|
canvas.backgroundImage = img;
|
|
547
|
+
if (prevOpacity !== void 0 && prevOpacity !== 1) {
|
|
548
|
+
img.set("opacity", prevOpacity);
|
|
549
|
+
}
|
|
449
550
|
canvas.requestRenderAll();
|
|
450
551
|
return img;
|
|
451
552
|
}
|
|
@@ -474,7 +575,10 @@ function createViewportActions(canvasRef, viewportRef, setZoom) {
|
|
|
474
575
|
viewportRef.current?.zoomOut(step);
|
|
475
576
|
syncZoom(canvasRef, setZoom);
|
|
476
577
|
};
|
|
477
|
-
|
|
578
|
+
const panToObject = (object, panOpts) => {
|
|
579
|
+
viewportRef.current?.panToObject(object, panOpts);
|
|
580
|
+
};
|
|
581
|
+
return { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject };
|
|
478
582
|
}
|
|
479
583
|
function resolveAlignmentEnabled(enableAlignment, alignmentProp) {
|
|
480
584
|
if (enableAlignment !== void 0) return enableAlignment;
|
|
@@ -482,22 +586,22 @@ function resolveAlignmentEnabled(enableAlignment, alignmentProp) {
|
|
|
482
586
|
}
|
|
483
587
|
|
|
484
588
|
// src/alignment/snapPoints.ts
|
|
485
|
-
var
|
|
589
|
+
var import_fabric6 = require("fabric");
|
|
486
590
|
|
|
487
591
|
// src/alignment/objectAlignmentUtils.ts
|
|
488
|
-
var
|
|
592
|
+
var import_fabric5 = require("fabric");
|
|
489
593
|
function getStrokeFreeCoords(obj) {
|
|
490
594
|
const m = obj.calcTransformMatrix();
|
|
491
595
|
const w = obj.width / 2;
|
|
492
596
|
const h = obj.height / 2;
|
|
493
597
|
return [
|
|
494
|
-
new
|
|
598
|
+
new import_fabric5.Point(-w, -h).transform(m),
|
|
495
599
|
// tl
|
|
496
|
-
new
|
|
600
|
+
new import_fabric5.Point(w, -h).transform(m),
|
|
497
601
|
// tr
|
|
498
|
-
new
|
|
602
|
+
new import_fabric5.Point(w, h).transform(m),
|
|
499
603
|
// br
|
|
500
|
-
new
|
|
604
|
+
new import_fabric5.Point(-w, h).transform(m)
|
|
501
605
|
// bl
|
|
502
606
|
];
|
|
503
607
|
}
|
|
@@ -549,17 +653,17 @@ function getAlignmentTargets(target) {
|
|
|
549
653
|
const objects = /* @__PURE__ */ new Set();
|
|
550
654
|
const canvas = target.canvas;
|
|
551
655
|
if (!canvas) return objects;
|
|
552
|
-
const children = target instanceof
|
|
656
|
+
const children = target instanceof import_fabric5.ActiveSelection ? target.getObjects() : [target];
|
|
553
657
|
canvas.forEachObject((o) => {
|
|
554
658
|
if (!o.isOnScreen() || !o.visible) return;
|
|
555
|
-
if (o.constructor ===
|
|
659
|
+
if (o.constructor === import_fabric5.Group) {
|
|
556
660
|
collectGroupChildren(objects, o);
|
|
557
661
|
return;
|
|
558
662
|
}
|
|
559
663
|
objects.add(o);
|
|
560
664
|
});
|
|
561
665
|
for (const child of children) {
|
|
562
|
-
if (child.constructor ===
|
|
666
|
+
if (child.constructor === import_fabric5.Group) {
|
|
563
667
|
for (const gc of child.getObjects()) objects.delete(gc);
|
|
564
668
|
} else {
|
|
565
669
|
objects.delete(child);
|
|
@@ -570,7 +674,7 @@ function getAlignmentTargets(target) {
|
|
|
570
674
|
function collectGroupChildren(objects, group) {
|
|
571
675
|
for (const child of group.getObjects()) {
|
|
572
676
|
if (!child.visible) continue;
|
|
573
|
-
if (child.constructor ===
|
|
677
|
+
if (child.constructor === import_fabric5.Group) {
|
|
574
678
|
collectGroupChildren(objects, child);
|
|
575
679
|
} else {
|
|
576
680
|
objects.add(child);
|
|
@@ -596,7 +700,7 @@ function getDefaultSnapPoints(object) {
|
|
|
596
700
|
return [...coords, object.getCenterPoint()];
|
|
597
701
|
}
|
|
598
702
|
registerSnapPointExtractor(
|
|
599
|
-
(obj) => obj instanceof
|
|
703
|
+
(obj) => obj instanceof import_fabric6.Rect,
|
|
600
704
|
(obj) => {
|
|
601
705
|
const [tl, tr, br, bl] = getStrokeFreeCoords(obj);
|
|
602
706
|
const mt = tl.add(tr).scalarDivide(2);
|
|
@@ -607,16 +711,16 @@ registerSnapPointExtractor(
|
|
|
607
711
|
}
|
|
608
712
|
);
|
|
609
713
|
registerSnapPointExtractor(
|
|
610
|
-
(obj) => obj instanceof
|
|
714
|
+
(obj) => obj instanceof import_fabric6.Polygon,
|
|
611
715
|
(obj) => {
|
|
612
716
|
const polygon = obj;
|
|
613
717
|
const matrix = polygon.calcTransformMatrix();
|
|
614
718
|
const points = polygon.points.map((pt) => {
|
|
615
|
-
const local = new
|
|
719
|
+
const local = new import_fabric6.Point(
|
|
616
720
|
pt.x - polygon.pathOffset.x,
|
|
617
721
|
pt.y - polygon.pathOffset.y
|
|
618
722
|
);
|
|
619
|
-
return
|
|
723
|
+
return import_fabric6.util.transformPoint(local, matrix);
|
|
620
724
|
});
|
|
621
725
|
points.push(polygon.getCenterPoint());
|
|
622
726
|
return points;
|
|
@@ -624,7 +728,7 @@ registerSnapPointExtractor(
|
|
|
624
728
|
);
|
|
625
729
|
|
|
626
730
|
// src/alignment/objectAlignment.ts
|
|
627
|
-
var
|
|
731
|
+
var import_fabric7 = require("fabric");
|
|
628
732
|
|
|
629
733
|
// src/alignment/objectAlignmentRendering.ts
|
|
630
734
|
function drawAlignmentLine(config, origin, target) {
|
|
@@ -927,7 +1031,7 @@ var ObjectAlignmentGuides = class {
|
|
|
927
1031
|
const isCenter = e.transform.original.originX === "center" && e.transform.original.originY === "center";
|
|
928
1032
|
if (isCenter) {
|
|
929
1033
|
const p = target.group ? point.transform(
|
|
930
|
-
|
|
1034
|
+
import_fabric7.util.invertTransform(target.group.calcTransformMatrix())
|
|
931
1035
|
) : point;
|
|
932
1036
|
diagonalPoint = diagonalPoint.add(p).scalarDivide(2);
|
|
933
1037
|
}
|
|
@@ -996,7 +1100,7 @@ function enableRotationSnap(canvas, options) {
|
|
|
996
1100
|
}
|
|
997
1101
|
|
|
998
1102
|
// src/alignment/cursorSnapping.ts
|
|
999
|
-
var
|
|
1103
|
+
var import_fabric8 = require("fabric");
|
|
1000
1104
|
function snapCursorPoint(canvas, rawPoint, options) {
|
|
1001
1105
|
const zoom = canvas.getZoom();
|
|
1002
1106
|
const scaleWithSize = options?.scaleWithCanvasSize !== false;
|
|
@@ -1039,7 +1143,7 @@ function snapCursorPoint(canvas, rawPoint, options) {
|
|
|
1039
1143
|
const snapX = bestDx <= margin && snapTargetsX.length > 0;
|
|
1040
1144
|
const snapY = bestDy <= margin && snapTargetsY.length > 0;
|
|
1041
1145
|
return {
|
|
1042
|
-
point: new
|
|
1146
|
+
point: new import_fabric8.Point(
|
|
1043
1147
|
snapX ? snapTargetsX[0].x : rawPoint.x,
|
|
1044
1148
|
snapY ? snapTargetsY[0].y : rawPoint.y
|
|
1045
1149
|
),
|
|
@@ -1071,7 +1175,7 @@ function drawCursorGuidelines(canvas, snapResult, style) {
|
|
|
1071
1175
|
ctx.stroke();
|
|
1072
1176
|
drawXMarker2(ctx, from, xSize);
|
|
1073
1177
|
}
|
|
1074
|
-
drawXMarker2(ctx, new
|
|
1178
|
+
drawXMarker2(ctx, new import_fabric8.Point(to.x, to.y), xSize);
|
|
1075
1179
|
}
|
|
1076
1180
|
if (snapResult.snapY && snapResult.alignTargetsY) {
|
|
1077
1181
|
const to = snapResult.point;
|
|
@@ -1082,7 +1186,7 @@ function drawCursorGuidelines(canvas, snapResult, style) {
|
|
|
1082
1186
|
ctx.stroke();
|
|
1083
1187
|
drawXMarker2(ctx, from, xSize);
|
|
1084
1188
|
}
|
|
1085
|
-
drawXMarker2(ctx, new
|
|
1189
|
+
drawXMarker2(ctx, new import_fabric8.Point(to.x, to.y), xSize);
|
|
1086
1190
|
}
|
|
1087
1191
|
ctx.restore();
|
|
1088
1192
|
}
|
|
@@ -1144,7 +1248,7 @@ function enableClickToCreate(canvas, factory, options) {
|
|
|
1144
1248
|
}
|
|
1145
1249
|
|
|
1146
1250
|
// src/interactions/dragToCreate.ts
|
|
1147
|
-
var
|
|
1251
|
+
var import_fabric10 = require("fabric");
|
|
1148
1252
|
|
|
1149
1253
|
// src/styles.ts
|
|
1150
1254
|
var import_styles = require("@mui/material/styles");
|
|
@@ -1191,7 +1295,7 @@ var DEFAULT_ALIGNMENT_STYLE = {
|
|
|
1191
1295
|
};
|
|
1192
1296
|
|
|
1193
1297
|
// src/interactions/interactionSnapping.ts
|
|
1194
|
-
var
|
|
1298
|
+
var import_fabric9 = require("fabric");
|
|
1195
1299
|
var canvasAlignmentState = /* @__PURE__ */ new WeakMap();
|
|
1196
1300
|
function setCanvasAlignmentEnabled(canvas, enabled) {
|
|
1197
1301
|
canvasAlignmentState.set(canvas, enabled);
|
|
@@ -1239,7 +1343,7 @@ function createInteractionSnapping(canvas, options, getAdditionalTargets) {
|
|
|
1239
1343
|
}
|
|
1240
1344
|
function snap(rawX, rawY) {
|
|
1241
1345
|
if (!snapEnabled) return { x: rawX, y: rawY };
|
|
1242
|
-
const result = snapCursorPoint(canvas, new
|
|
1346
|
+
const result = snapCursorPoint(canvas, new import_fabric9.Point(rawX, rawY), {
|
|
1243
1347
|
margin: snapMargin,
|
|
1244
1348
|
exclude: excludeSet,
|
|
1245
1349
|
targetPoints: getAllTargetPoints()
|
|
@@ -1251,7 +1355,7 @@ function createInteractionSnapping(canvas, options, getAdditionalTargets) {
|
|
|
1251
1355
|
lastSnapResult = null;
|
|
1252
1356
|
return { x: rawX, y: rawY };
|
|
1253
1357
|
}
|
|
1254
|
-
lastSnapResult = snapCursorPoint(canvas, new
|
|
1358
|
+
lastSnapResult = snapCursorPoint(canvas, new import_fabric9.Point(rawX, rawY), {
|
|
1255
1359
|
margin: snapMargin,
|
|
1256
1360
|
exclude: excludeSet,
|
|
1257
1361
|
targetPoints: getAllTargetPoints()
|
|
@@ -1327,7 +1431,7 @@ function enableDragToCreate(canvas, factory, options) {
|
|
|
1327
1431
|
lastEndY = startY;
|
|
1328
1432
|
previousSelection = canvas.selection;
|
|
1329
1433
|
canvas.selection = false;
|
|
1330
|
-
previewRect = new
|
|
1434
|
+
previewRect = new import_fabric10.Rect({
|
|
1331
1435
|
...DEFAULT_GUIDELINE_SHAPE_STYLE,
|
|
1332
1436
|
left: startX,
|
|
1333
1437
|
top: startY,
|
|
@@ -1366,37 +1470,55 @@ function enableDragToCreate(canvas, factory, options) {
|
|
|
1366
1470
|
options?.onCreated?.(obj);
|
|
1367
1471
|
previewRect = null;
|
|
1368
1472
|
};
|
|
1473
|
+
const handleKeyDown = (e) => {
|
|
1474
|
+
if (e.key === "Escape") {
|
|
1475
|
+
e.stopImmediatePropagation();
|
|
1476
|
+
e.preventDefault();
|
|
1477
|
+
cleanup("cancel");
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1369
1480
|
canvas.on("mouse:down", handleMouseDown);
|
|
1370
1481
|
canvas.on("mouse:move", handleMouseMove);
|
|
1371
1482
|
canvas.on("mouse:up", handleMouseUp);
|
|
1372
|
-
|
|
1483
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
1484
|
+
let exited = false;
|
|
1485
|
+
function cleanup(reason) {
|
|
1486
|
+
if (exited) return;
|
|
1487
|
+
exited = true;
|
|
1373
1488
|
canvas.off("mouse:down", handleMouseDown);
|
|
1374
1489
|
canvas.off("mouse:move", handleMouseMove);
|
|
1375
1490
|
canvas.off("mouse:up", handleMouseUp);
|
|
1491
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
1376
1492
|
shiftTracker.cleanup();
|
|
1377
1493
|
snapping.cleanup();
|
|
1378
1494
|
if (isDrawing && previewRect) {
|
|
1495
|
+
snapping.excludeSet.delete(previewRect);
|
|
1379
1496
|
canvas.remove(previewRect);
|
|
1497
|
+
canvas.selection = previousSelection;
|
|
1380
1498
|
canvas.requestRenderAll();
|
|
1381
1499
|
}
|
|
1382
1500
|
restoreViewport(options?.viewport);
|
|
1383
|
-
|
|
1501
|
+
if (reason === "cancel") {
|
|
1502
|
+
options?.onCancel?.();
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return () => cleanup();
|
|
1384
1506
|
}
|
|
1385
1507
|
|
|
1386
1508
|
// src/interactions/drawToCreate.ts
|
|
1387
|
-
var
|
|
1509
|
+
var import_fabric14 = require("fabric");
|
|
1388
1510
|
|
|
1389
1511
|
// src/shapes/rectangle.ts
|
|
1390
|
-
var
|
|
1512
|
+
var import_fabric11 = require("fabric");
|
|
1391
1513
|
function createRectangle(canvas, options) {
|
|
1392
|
-
const rect = new
|
|
1514
|
+
const rect = new import_fabric11.Rect({ ...DEFAULT_SHAPE_STYLE, ...options });
|
|
1393
1515
|
canvas.add(rect);
|
|
1394
1516
|
canvas.requestRenderAll();
|
|
1395
1517
|
return rect;
|
|
1396
1518
|
}
|
|
1397
1519
|
function createRectangleAtPoint(canvas, point, options) {
|
|
1398
1520
|
const { width, height, ...style } = options;
|
|
1399
|
-
const rect = new
|
|
1521
|
+
const rect = new import_fabric11.Rect({
|
|
1400
1522
|
...DEFAULT_SHAPE_STYLE,
|
|
1401
1523
|
left: point.x,
|
|
1402
1524
|
top: point.y,
|
|
@@ -1415,7 +1537,7 @@ function editRectangle(canvas, rect, changes) {
|
|
|
1415
1537
|
}
|
|
1416
1538
|
|
|
1417
1539
|
// src/shapes/circle.ts
|
|
1418
|
-
var
|
|
1540
|
+
var import_fabric12 = require("fabric");
|
|
1419
1541
|
var CIRCLE_CONSTRAINTS = {
|
|
1420
1542
|
lockRotation: true,
|
|
1421
1543
|
lockUniScaling: true
|
|
@@ -1437,7 +1559,7 @@ function restoreCircleConstraints(rect) {
|
|
|
1437
1559
|
}
|
|
1438
1560
|
function createCircle(canvas, options) {
|
|
1439
1561
|
const { size, ...rest } = options;
|
|
1440
|
-
const rect = new
|
|
1562
|
+
const rect = new import_fabric12.Rect({
|
|
1441
1563
|
...DEFAULT_CIRCLE_STYLE,
|
|
1442
1564
|
...CIRCLE_CONSTRAINTS,
|
|
1443
1565
|
width: size,
|
|
@@ -1453,7 +1575,7 @@ function createCircle(canvas, options) {
|
|
|
1453
1575
|
}
|
|
1454
1576
|
function createCircleAtPoint(canvas, point, options) {
|
|
1455
1577
|
const { size, ...style } = options;
|
|
1456
|
-
const rect = new
|
|
1578
|
+
const rect = new import_fabric12.Rect({
|
|
1457
1579
|
...DEFAULT_CIRCLE_STYLE,
|
|
1458
1580
|
...CIRCLE_CONSTRAINTS,
|
|
1459
1581
|
left: point.x,
|
|
@@ -1487,17 +1609,17 @@ function editCircle(canvas, rect, changes) {
|
|
|
1487
1609
|
}
|
|
1488
1610
|
|
|
1489
1611
|
// src/shapes/polygon.ts
|
|
1490
|
-
var
|
|
1612
|
+
var import_fabric13 = require("fabric");
|
|
1491
1613
|
function createPolygon(canvas, options) {
|
|
1492
1614
|
const { points, ...rest } = options;
|
|
1493
|
-
const polygon = new
|
|
1615
|
+
const polygon = new import_fabric13.Polygon(points, { ...DEFAULT_SHAPE_STYLE, ...rest });
|
|
1494
1616
|
canvas.add(polygon);
|
|
1495
1617
|
canvas.requestRenderAll();
|
|
1496
1618
|
return polygon;
|
|
1497
1619
|
}
|
|
1498
1620
|
function createPolygonAtPoint(canvas, point, options) {
|
|
1499
1621
|
const { width, height, ...style } = options;
|
|
1500
|
-
const polygon = new
|
|
1622
|
+
const polygon = new import_fabric13.Polygon(
|
|
1501
1623
|
[
|
|
1502
1624
|
{ x: 0, y: 0 },
|
|
1503
1625
|
{ x: width, y: 0 },
|
|
@@ -1515,7 +1637,7 @@ function createPolygonFromDrag(canvas, start, end, style) {
|
|
|
1515
1637
|
const height = Math.abs(end.y - start.y);
|
|
1516
1638
|
const left = Math.min(start.x, end.x) + width / 2;
|
|
1517
1639
|
const top = Math.min(start.y, end.y) + height / 2;
|
|
1518
|
-
const polygon = new
|
|
1640
|
+
const polygon = new import_fabric13.Polygon(
|
|
1519
1641
|
[
|
|
1520
1642
|
{ x: 0, y: 0 },
|
|
1521
1643
|
{ x: width, y: 0 },
|
|
@@ -1529,7 +1651,7 @@ function createPolygonFromDrag(canvas, start, end, style) {
|
|
|
1529
1651
|
return polygon;
|
|
1530
1652
|
}
|
|
1531
1653
|
function createPolygonFromVertices(canvas, points, style) {
|
|
1532
|
-
const polygon = new
|
|
1654
|
+
const polygon = new import_fabric13.Polygon(
|
|
1533
1655
|
points.map((p) => ({ x: p.x, y: p.y })),
|
|
1534
1656
|
{ ...DEFAULT_SHAPE_STYLE, ...style }
|
|
1535
1657
|
);
|
|
@@ -1607,7 +1729,7 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1607
1729
|
const snapping = createInteractionSnapping(
|
|
1608
1730
|
canvas,
|
|
1609
1731
|
options,
|
|
1610
|
-
() => points.map((p) => new
|
|
1732
|
+
() => points.map((p) => new import_fabric14.Point(p.x, p.y))
|
|
1611
1733
|
);
|
|
1612
1734
|
function trackPreviewElement(obj) {
|
|
1613
1735
|
snapping.excludeSet.add(obj);
|
|
@@ -1656,6 +1778,9 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1656
1778
|
removePreviewElements();
|
|
1657
1779
|
snapping.clearSnapResult();
|
|
1658
1780
|
const polygon = createPolygonFromVertices(canvas, points, options?.style);
|
|
1781
|
+
if (options?.data) {
|
|
1782
|
+
polygon.data = options.data;
|
|
1783
|
+
}
|
|
1659
1784
|
canvas.selection = previousSelection;
|
|
1660
1785
|
canvas.requestRenderAll();
|
|
1661
1786
|
restoreViewport(options?.viewport);
|
|
@@ -1689,7 +1814,7 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1689
1814
|
canvas.selection = false;
|
|
1690
1815
|
}
|
|
1691
1816
|
points.push({ x, y });
|
|
1692
|
-
const marker = new
|
|
1817
|
+
const marker = new import_fabric14.Circle({
|
|
1693
1818
|
left: x,
|
|
1694
1819
|
top: y,
|
|
1695
1820
|
radius: 4,
|
|
@@ -1705,7 +1830,7 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1705
1830
|
canvas.add(marker);
|
|
1706
1831
|
if (points.length >= 2) {
|
|
1707
1832
|
const prev = points[points.length - 2];
|
|
1708
|
-
const edge = new
|
|
1833
|
+
const edge = new import_fabric14.Line([prev.x, prev.y, x, y], lineStyle);
|
|
1709
1834
|
edgeLines.push(edge);
|
|
1710
1835
|
trackPreviewElement(edge);
|
|
1711
1836
|
canvas.add(edge);
|
|
@@ -1734,7 +1859,7 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1734
1859
|
untrackPreviewElement(trackingLine);
|
|
1735
1860
|
canvas.remove(trackingLine);
|
|
1736
1861
|
}
|
|
1737
|
-
trackingLine = new
|
|
1862
|
+
trackingLine = new import_fabric14.Line([lastPoint.x, lastPoint.y, x, y], {
|
|
1738
1863
|
...guideLineStyle,
|
|
1739
1864
|
strokeDashArray: [5, 5]
|
|
1740
1865
|
});
|
|
@@ -1746,7 +1871,7 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1746
1871
|
closingLine = null;
|
|
1747
1872
|
}
|
|
1748
1873
|
if (points.length >= 3) {
|
|
1749
|
-
closingLine = new
|
|
1874
|
+
closingLine = new import_fabric14.Line([x, y, points[0].x, points[0].y], {
|
|
1750
1875
|
...guideLineStyle,
|
|
1751
1876
|
strokeDashArray: [5, 5]
|
|
1752
1877
|
});
|
|
@@ -1788,30 +1913,30 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1788
1913
|
}
|
|
1789
1914
|
|
|
1790
1915
|
// src/interactions/vertexEdit.ts
|
|
1791
|
-
var
|
|
1916
|
+
var import_fabric15 = require("fabric");
|
|
1792
1917
|
function localPointToScene(polygon, point) {
|
|
1793
1918
|
const matrix = polygon.calcTransformMatrix();
|
|
1794
|
-
const localPoint = new
|
|
1919
|
+
const localPoint = new import_fabric15.Point(
|
|
1795
1920
|
point.x - polygon.pathOffset.x,
|
|
1796
1921
|
point.y - polygon.pathOffset.y
|
|
1797
1922
|
);
|
|
1798
|
-
return
|
|
1923
|
+
return import_fabric15.util.transformPoint(localPoint, matrix);
|
|
1799
1924
|
}
|
|
1800
1925
|
function scenePointToLocal(polygon, scenePoint) {
|
|
1801
1926
|
const matrix = polygon.calcTransformMatrix();
|
|
1802
|
-
const invMatrix =
|
|
1803
|
-
const localPoint =
|
|
1927
|
+
const invMatrix = import_fabric15.util.invertTransform(matrix);
|
|
1928
|
+
const localPoint = import_fabric15.util.transformPoint(scenePoint, invMatrix);
|
|
1804
1929
|
return {
|
|
1805
1930
|
x: localPoint.x + polygon.pathOffset.x,
|
|
1806
1931
|
y: localPoint.y + polygon.pathOffset.y
|
|
1807
1932
|
};
|
|
1808
1933
|
}
|
|
1809
1934
|
function sceneToScreen(scenePoint, canvas) {
|
|
1810
|
-
return
|
|
1935
|
+
return import_fabric15.util.transformPoint(scenePoint, canvas.viewportTransform);
|
|
1811
1936
|
}
|
|
1812
1937
|
function screenToScene(screenX, screenY, canvas) {
|
|
1813
|
-
const inv =
|
|
1814
|
-
return
|
|
1938
|
+
const inv = import_fabric15.util.invertTransform(canvas.viewportTransform);
|
|
1939
|
+
return import_fabric15.util.transformPoint(new import_fabric15.Point(screenX, screenY), inv);
|
|
1815
1940
|
}
|
|
1816
1941
|
function createHandleElement(radius, fill, stroke, strokeWidth) {
|
|
1817
1942
|
const el = document.createElement("div");
|
|
@@ -1901,7 +2026,7 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
|
|
|
1901
2026
|
const canvasRelY = e.clientY - wrapperRect.top;
|
|
1902
2027
|
const rawScene = screenToScene(canvasRelX, canvasRelY, canvas);
|
|
1903
2028
|
const snapped = snapping.snapWithGuidelines(rawScene.x, rawScene.y);
|
|
1904
|
-
const scenePoint = new
|
|
2029
|
+
const scenePoint = new import_fabric15.Point(snapped.x, snapped.y);
|
|
1905
2030
|
const anchorIdx = i === 0 ? 1 : 0;
|
|
1906
2031
|
const anchorBefore = localPointToScene(
|
|
1907
2032
|
polygon,
|
|
@@ -1974,7 +2099,7 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
|
|
|
1974
2099
|
}
|
|
1975
2100
|
|
|
1976
2101
|
// src/serialization.ts
|
|
1977
|
-
var
|
|
2102
|
+
var import_fabric16 = require("fabric");
|
|
1978
2103
|
var strokeBaseMap = /* @__PURE__ */ new WeakMap();
|
|
1979
2104
|
function enableScaledStrokes(canvas) {
|
|
1980
2105
|
function applyScaledStrokes() {
|
|
@@ -2031,15 +2156,23 @@ function serializeCanvas(canvas, options) {
|
|
|
2031
2156
|
});
|
|
2032
2157
|
return json;
|
|
2033
2158
|
}
|
|
2034
|
-
async function loadCanvas(canvas, json) {
|
|
2159
|
+
async function loadCanvas(canvas, json, options) {
|
|
2035
2160
|
await canvas.loadFromJSON(json);
|
|
2161
|
+
if (options?.filter) {
|
|
2162
|
+
const toRemove = [];
|
|
2163
|
+
canvas.forEachObject((obj) => {
|
|
2164
|
+
if (!options.filter(obj)) toRemove.push(obj);
|
|
2165
|
+
});
|
|
2166
|
+
for (const obj of toRemove) canvas.remove(obj);
|
|
2167
|
+
}
|
|
2036
2168
|
canvas.forEachObject((obj) => {
|
|
2037
2169
|
obj.set(DEFAULT_CONTROL_STYLE);
|
|
2038
|
-
if (obj.shapeType === "circle" && obj instanceof
|
|
2170
|
+
if (obj.shapeType === "circle" && obj instanceof import_fabric16.Rect) {
|
|
2039
2171
|
restoreCircleConstraints(obj);
|
|
2040
2172
|
}
|
|
2041
2173
|
});
|
|
2042
2174
|
canvas.requestRenderAll();
|
|
2175
|
+
return canvas.getObjects();
|
|
2043
2176
|
}
|
|
2044
2177
|
|
|
2045
2178
|
// src/hooks/useEditCanvas.ts
|
|
@@ -2055,6 +2188,7 @@ function useEditCanvas(options) {
|
|
|
2055
2188
|
const [selected, setSelected] = (0, import_react2.useState)([]);
|
|
2056
2189
|
const [viewportMode, setViewportModeState] = (0, import_react2.useState)("select");
|
|
2057
2190
|
const [isEditingVertices, setIsEditingVertices] = (0, import_react2.useState)(false);
|
|
2191
|
+
const [isDirty, setIsDirty] = (0, import_react2.useState)(false);
|
|
2058
2192
|
const setMode = (0, import_react2.useCallback)((setup) => {
|
|
2059
2193
|
vertexEditCleanupRef.current?.();
|
|
2060
2194
|
vertexEditCleanupRef.current = null;
|
|
@@ -2125,10 +2259,15 @@ function useEditCanvas(options) {
|
|
|
2125
2259
|
canvas.on("selection:cleared", () => {
|
|
2126
2260
|
setSelected([]);
|
|
2127
2261
|
});
|
|
2262
|
+
if (options?.trackChanges) {
|
|
2263
|
+
canvas.on("object:added", () => setIsDirty(true));
|
|
2264
|
+
canvas.on("object:removed", () => setIsDirty(true));
|
|
2265
|
+
canvas.on("object:modified", () => setIsDirty(true));
|
|
2266
|
+
}
|
|
2128
2267
|
if (options?.vertexEdit !== false) {
|
|
2129
2268
|
const vertexOpts = typeof options?.vertexEdit === "object" ? options.vertexEdit : void 0;
|
|
2130
2269
|
canvas.on("mouse:dblclick", (e) => {
|
|
2131
|
-
if (e.target && e.target instanceof
|
|
2270
|
+
if (e.target && e.target instanceof import_fabric17.Polygon) {
|
|
2132
2271
|
vertexEditCleanupRef.current?.();
|
|
2133
2272
|
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2134
2273
|
canvas,
|
|
@@ -2179,22 +2318,26 @@ function useEditCanvas(options) {
|
|
|
2179
2318
|
viewportRef.current?.setMode(mode);
|
|
2180
2319
|
setViewportModeState(mode);
|
|
2181
2320
|
}, []);
|
|
2182
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut } = createViewportActions(
|
|
2321
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
|
|
2183
2322
|
canvasRef,
|
|
2184
2323
|
viewportRef,
|
|
2185
2324
|
setZoom
|
|
2186
2325
|
);
|
|
2187
|
-
const setBackground = (0, import_react2.useCallback)(
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2326
|
+
const setBackground = (0, import_react2.useCallback)(
|
|
2327
|
+
async (url, bgOpts) => {
|
|
2328
|
+
const canvas = canvasRef.current;
|
|
2329
|
+
if (!canvas) throw new Error("Canvas not ready");
|
|
2330
|
+
const resizeOpts = options?.backgroundResize !== false ? typeof options?.backgroundResize === "object" ? { ...options.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveOpacity ? { preserveOpacity: true } : void 0;
|
|
2331
|
+
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2332
|
+
if (options?.autoFitToBackground !== false) {
|
|
2333
|
+
fitViewportToBackground(canvas);
|
|
2334
|
+
syncZoom(canvasRef, setZoom);
|
|
2335
|
+
}
|
|
2336
|
+
return img;
|
|
2337
|
+
},
|
|
2338
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2339
|
+
[]
|
|
2340
|
+
);
|
|
2198
2341
|
return {
|
|
2199
2342
|
/** Pass this to `<Canvas onReady={...} />` */
|
|
2200
2343
|
onReady,
|
|
@@ -2215,7 +2358,9 @@ function useEditCanvas(options) {
|
|
|
2215
2358
|
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2216
2359
|
zoomIn,
|
|
2217
2360
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2218
|
-
zoomOut
|
|
2361
|
+
zoomOut,
|
|
2362
|
+
/** Pan the viewport to center on a specific object. */
|
|
2363
|
+
panToObject
|
|
2219
2364
|
},
|
|
2220
2365
|
/** Whether vertex edit mode is currently active (reactive). */
|
|
2221
2366
|
isEditingVertices,
|
|
@@ -2239,21 +2384,28 @@ function useEditCanvas(options) {
|
|
|
2239
2384
|
* Set a background image from a URL. Automatically resizes if the image
|
|
2240
2385
|
* exceeds the configured limits (opt out via `backgroundResize: false`),
|
|
2241
2386
|
* and fits the viewport after setting if `autoFitToBackground` is enabled.
|
|
2387
|
+
*
|
|
2388
|
+
* Pass `{ preserveOpacity: true }` to keep the current background opacity
|
|
2389
|
+
* when replacing the image.
|
|
2242
2390
|
*/
|
|
2243
|
-
setBackground
|
|
2391
|
+
setBackground,
|
|
2392
|
+
/** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
|
|
2393
|
+
isDirty,
|
|
2394
|
+
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2395
|
+
resetDirty: (0, import_react2.useCallback)(() => setIsDirty(false), [])
|
|
2244
2396
|
};
|
|
2245
2397
|
}
|
|
2246
2398
|
|
|
2247
2399
|
// src/hooks/useViewCanvas.ts
|
|
2248
2400
|
var import_react3 = require("react");
|
|
2249
|
-
var
|
|
2401
|
+
var import_fabric18 = require("fabric");
|
|
2250
2402
|
var VIEW_BORDER_RADIUS = 4;
|
|
2251
2403
|
function lockCanvas(canvas) {
|
|
2252
2404
|
canvas.selection = false;
|
|
2253
2405
|
canvas.forEachObject((obj) => {
|
|
2254
2406
|
obj.selectable = false;
|
|
2255
2407
|
obj.evented = false;
|
|
2256
|
-
if (obj instanceof
|
|
2408
|
+
if (obj instanceof import_fabric18.Rect && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
|
|
2257
2409
|
const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);
|
|
2258
2410
|
const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);
|
|
2259
2411
|
obj.set({ rx, ry });
|
|
@@ -2298,7 +2450,7 @@ function useViewCanvas(options) {
|
|
|
2298
2450
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2299
2451
|
[]
|
|
2300
2452
|
);
|
|
2301
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut } = createViewportActions(
|
|
2453
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
|
|
2302
2454
|
canvasRef,
|
|
2303
2455
|
viewportRef,
|
|
2304
2456
|
setZoom
|
|
@@ -2364,7 +2516,9 @@ function useViewCanvas(options) {
|
|
|
2364
2516
|
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2365
2517
|
zoomIn,
|
|
2366
2518
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2367
|
-
zoomOut
|
|
2519
|
+
zoomOut,
|
|
2520
|
+
/** Pan the viewport to center on a specific object. */
|
|
2521
|
+
panToObject
|
|
2368
2522
|
},
|
|
2369
2523
|
/** Update a single object's visual style by its `data.id`. */
|
|
2370
2524
|
setObjectStyle,
|
|
@@ -2374,6 +2528,209 @@ function useViewCanvas(options) {
|
|
|
2374
2528
|
setObjectStyleByType
|
|
2375
2529
|
};
|
|
2376
2530
|
}
|
|
2531
|
+
|
|
2532
|
+
// src/hooks/useCanvasEvents.ts
|
|
2533
|
+
var import_react4 = require("react");
|
|
2534
|
+
function useCanvasEvents(canvasRef, events) {
|
|
2535
|
+
const eventsRef = (0, import_react4.useRef)(events);
|
|
2536
|
+
eventsRef.current = events;
|
|
2537
|
+
(0, import_react4.useEffect)(() => {
|
|
2538
|
+
const canvas = canvasRef.current;
|
|
2539
|
+
if (!canvas) return;
|
|
2540
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
2541
|
+
for (const key of Object.keys(eventsRef.current)) {
|
|
2542
|
+
if (!eventsRef.current[key]) continue;
|
|
2543
|
+
const wrapped = (options) => {
|
|
2544
|
+
eventsRef.current[key]?.(options);
|
|
2545
|
+
};
|
|
2546
|
+
canvas.on(key, wrapped);
|
|
2547
|
+
wrappers.set(key, wrapped);
|
|
2548
|
+
}
|
|
2549
|
+
return () => {
|
|
2550
|
+
wrappers.forEach((handler, name) => {
|
|
2551
|
+
canvas.off(name, handler);
|
|
2552
|
+
});
|
|
2553
|
+
};
|
|
2554
|
+
}, [canvasRef]);
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
// src/hooks/useCanvasTooltip.ts
|
|
2558
|
+
var import_react5 = require("react");
|
|
2559
|
+
function useCanvasTooltip(canvasRef, options) {
|
|
2560
|
+
const [state, setState] = (0, import_react5.useState)({
|
|
2561
|
+
visible: false,
|
|
2562
|
+
content: null,
|
|
2563
|
+
position: { x: 0, y: 0 }
|
|
2564
|
+
});
|
|
2565
|
+
const hoveredObjectRef = (0, import_react5.useRef)(null);
|
|
2566
|
+
const optionsRef = (0, import_react5.useRef)(options);
|
|
2567
|
+
optionsRef.current = options;
|
|
2568
|
+
(0, import_react5.useEffect)(() => {
|
|
2569
|
+
const canvas = canvasRef.current;
|
|
2570
|
+
if (!canvas) return;
|
|
2571
|
+
function calculatePosition(target) {
|
|
2572
|
+
const bounds = target.getBoundingRect();
|
|
2573
|
+
const zoom = canvas.getZoom();
|
|
2574
|
+
const vt = canvas.viewportTransform;
|
|
2575
|
+
if (!vt) return null;
|
|
2576
|
+
return {
|
|
2577
|
+
x: (bounds.left + bounds.width / 2) * zoom + vt[4],
|
|
2578
|
+
y: bounds.top * zoom + vt[5] - 10
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
const handleMouseOver = (e) => {
|
|
2582
|
+
const target = e.target;
|
|
2583
|
+
if (!target) return;
|
|
2584
|
+
const content = optionsRef.current.getContent(target);
|
|
2585
|
+
if (content === null) return;
|
|
2586
|
+
if (hoveredObjectRef.current !== target) {
|
|
2587
|
+
hoveredObjectRef.current = target;
|
|
2588
|
+
const position = calculatePosition(target);
|
|
2589
|
+
if (position) {
|
|
2590
|
+
setState({ visible: true, content, position });
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
};
|
|
2594
|
+
const handleMouseOut = (e) => {
|
|
2595
|
+
if (e.target && hoveredObjectRef.current === e.target) {
|
|
2596
|
+
setState((prev) => ({ ...prev, visible: false }));
|
|
2597
|
+
hoveredObjectRef.current = null;
|
|
2598
|
+
}
|
|
2599
|
+
};
|
|
2600
|
+
const updatePosition = () => {
|
|
2601
|
+
if (!hoveredObjectRef.current) return;
|
|
2602
|
+
const position = calculatePosition(hoveredObjectRef.current);
|
|
2603
|
+
if (position) {
|
|
2604
|
+
setState((prev) => prev.visible ? { ...prev, position } : prev);
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
canvas.on("mouse:over", handleMouseOver);
|
|
2608
|
+
canvas.on("mouse:out", handleMouseOut);
|
|
2609
|
+
canvas.on("after:render", updatePosition);
|
|
2610
|
+
canvas.on("mouse:wheel", updatePosition);
|
|
2611
|
+
return () => {
|
|
2612
|
+
canvas.off("mouse:over", handleMouseOver);
|
|
2613
|
+
canvas.off("mouse:out", handleMouseOut);
|
|
2614
|
+
canvas.off("after:render", updatePosition);
|
|
2615
|
+
canvas.off("mouse:wheel", updatePosition);
|
|
2616
|
+
};
|
|
2617
|
+
}, [canvasRef]);
|
|
2618
|
+
return state;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// src/hooks/useCanvasClick.ts
|
|
2622
|
+
var import_react6 = require("react");
|
|
2623
|
+
function useCanvasClick(canvasRef, onClick, options) {
|
|
2624
|
+
const onClickRef = (0, import_react6.useRef)(onClick);
|
|
2625
|
+
onClickRef.current = onClick;
|
|
2626
|
+
const optionsRef = (0, import_react6.useRef)(options);
|
|
2627
|
+
optionsRef.current = options;
|
|
2628
|
+
(0, import_react6.useEffect)(() => {
|
|
2629
|
+
const canvas = canvasRef.current;
|
|
2630
|
+
if (!canvas) return;
|
|
2631
|
+
let mouseDown = null;
|
|
2632
|
+
let isPanning = false;
|
|
2633
|
+
const handleMouseDown = (e) => {
|
|
2634
|
+
const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;
|
|
2635
|
+
if (native) {
|
|
2636
|
+
mouseDown = { x: native.clientX, y: native.clientY, time: Date.now() };
|
|
2637
|
+
isPanning = false;
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2640
|
+
const handleMouseMove = (e) => {
|
|
2641
|
+
if (!mouseDown) return;
|
|
2642
|
+
const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;
|
|
2643
|
+
if (!native) return;
|
|
2644
|
+
const threshold = optionsRef.current?.threshold ?? 5;
|
|
2645
|
+
const deltaX = Math.abs(native.clientX - mouseDown.x);
|
|
2646
|
+
const deltaY = Math.abs(native.clientY - mouseDown.y);
|
|
2647
|
+
if (deltaX > threshold || deltaY > threshold) {
|
|
2648
|
+
isPanning = true;
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
const handleMouseUp = (e) => {
|
|
2652
|
+
if (!mouseDown) return;
|
|
2653
|
+
const maxDuration = optionsRef.current?.maxDuration ?? 300;
|
|
2654
|
+
const elapsed = Date.now() - mouseDown.time;
|
|
2655
|
+
if (!isPanning && elapsed < maxDuration) {
|
|
2656
|
+
onClickRef.current(e.target);
|
|
2657
|
+
}
|
|
2658
|
+
mouseDown = null;
|
|
2659
|
+
isPanning = false;
|
|
2660
|
+
};
|
|
2661
|
+
canvas.on("mouse:down", handleMouseDown);
|
|
2662
|
+
canvas.on("mouse:move", handleMouseMove);
|
|
2663
|
+
canvas.on("mouse:up", handleMouseUp);
|
|
2664
|
+
return () => {
|
|
2665
|
+
canvas.off("mouse:down", handleMouseDown);
|
|
2666
|
+
canvas.off("mouse:move", handleMouseMove);
|
|
2667
|
+
canvas.off("mouse:up", handleMouseUp);
|
|
2668
|
+
};
|
|
2669
|
+
}, [canvasRef]);
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/hooks/useObjectOverlay.ts
|
|
2673
|
+
var import_react7 = require("react");
|
|
2674
|
+
var import_fabric19 = require("fabric");
|
|
2675
|
+
function useObjectOverlay(canvasRef, object, options) {
|
|
2676
|
+
const containerRef = (0, import_react7.useRef)(null);
|
|
2677
|
+
const optionsRef = (0, import_react7.useRef)(options);
|
|
2678
|
+
optionsRef.current = options;
|
|
2679
|
+
(0, import_react7.useEffect)(() => {
|
|
2680
|
+
const canvas = canvasRef.current;
|
|
2681
|
+
if (!canvas || !object) return;
|
|
2682
|
+
function update() {
|
|
2683
|
+
const el = containerRef.current;
|
|
2684
|
+
if (!el || !canvas || !object) return;
|
|
2685
|
+
const zoom = canvas.getZoom();
|
|
2686
|
+
const vt = canvas.viewportTransform;
|
|
2687
|
+
if (!vt) return;
|
|
2688
|
+
const center = object.getCenterPoint();
|
|
2689
|
+
const actualWidth = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
2690
|
+
const actualHeight = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
2691
|
+
const screenCoords = import_fabric19.util.transformPoint(center, vt);
|
|
2692
|
+
const screenWidth = actualWidth * zoom;
|
|
2693
|
+
const screenHeight = actualHeight * zoom;
|
|
2694
|
+
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2695
|
+
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
2696
|
+
el.style.width = `${screenWidth}px`;
|
|
2697
|
+
el.style.height = `${screenHeight}px`;
|
|
2698
|
+
const angle = object.angle ?? 0;
|
|
2699
|
+
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2700
|
+
const opts = optionsRef.current;
|
|
2701
|
+
if (opts?.autoScaleContent) {
|
|
2702
|
+
const contentScale = Math.min(screenWidth, screenHeight) / 100;
|
|
2703
|
+
const clampedScale = Math.max(0.1, Math.min(contentScale, 2));
|
|
2704
|
+
el.style.setProperty("--overlay-scale", String(clampedScale));
|
|
2705
|
+
if (opts.textSelector) {
|
|
2706
|
+
const textMinScale = opts.textMinScale ?? 0.5;
|
|
2707
|
+
const textEls = el.querySelectorAll(opts.textSelector);
|
|
2708
|
+
const display = clampedScale < textMinScale ? "none" : "";
|
|
2709
|
+
textEls.forEach((t) => {
|
|
2710
|
+
t.style.display = display;
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
update();
|
|
2716
|
+
canvas.on("after:render", update);
|
|
2717
|
+
canvas.on("mouse:wheel", update);
|
|
2718
|
+
object.on("moving", update);
|
|
2719
|
+
object.on("scaling", update);
|
|
2720
|
+
object.on("rotating", update);
|
|
2721
|
+
return () => {
|
|
2722
|
+
canvas.off("after:render", update);
|
|
2723
|
+
canvas.off("mouse:wheel", update);
|
|
2724
|
+
object.off("moving", update);
|
|
2725
|
+
object.off("scaling", update);
|
|
2726
|
+
object.off("rotating", update);
|
|
2727
|
+
};
|
|
2728
|
+
}, [canvasRef, object]);
|
|
2729
|
+
return containerRef;
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
// src/index.ts
|
|
2733
|
+
var import_fabric20 = require("fabric");
|
|
2377
2734
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2378
2735
|
0 && (module.exports = {
|
|
2379
2736
|
Canvas,
|
|
@@ -2383,6 +2740,12 @@ function useViewCanvas(options) {
|
|
|
2383
2740
|
DEFAULT_DRAG_SHAPE_STYLE,
|
|
2384
2741
|
DEFAULT_GUIDELINE_SHAPE_STYLE,
|
|
2385
2742
|
DEFAULT_SHAPE_STYLE,
|
|
2743
|
+
FabricCanvas,
|
|
2744
|
+
FabricImage,
|
|
2745
|
+
FabricObject,
|
|
2746
|
+
Point,
|
|
2747
|
+
Polygon,
|
|
2748
|
+
Rect,
|
|
2386
2749
|
createCircle,
|
|
2387
2750
|
createCircleAtPoint,
|
|
2388
2751
|
createPolygon,
|
|
@@ -2407,6 +2770,7 @@ function useViewCanvas(options) {
|
|
|
2407
2770
|
fitViewportToBackground,
|
|
2408
2771
|
getBackgroundInverted,
|
|
2409
2772
|
getBackgroundOpacity,
|
|
2773
|
+
getBackgroundSrc,
|
|
2410
2774
|
getBaseStrokeWidth,
|
|
2411
2775
|
getSnapPoints,
|
|
2412
2776
|
loadCanvas,
|
|
@@ -2418,7 +2782,12 @@ function useViewCanvas(options) {
|
|
|
2418
2782
|
setBackgroundInverted,
|
|
2419
2783
|
setBackgroundOpacity,
|
|
2420
2784
|
snapCursorPoint,
|
|
2785
|
+
useCanvasClick,
|
|
2786
|
+
useCanvasEvents,
|
|
2787
|
+
useCanvasTooltip,
|
|
2421
2788
|
useEditCanvas,
|
|
2422
|
-
|
|
2789
|
+
useObjectOverlay,
|
|
2790
|
+
useViewCanvas,
|
|
2791
|
+
util
|
|
2423
2792
|
});
|
|
2424
2793
|
//# sourceMappingURL=index.cjs.map
|