@bwp-web/canvas 0.5.0 → 0.6.0
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 +12 -1
- package/dist/Canvas/Canvas.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/fabricAugmentation.d.ts +3 -1
- package/dist/fabricAugmentation.d.ts.map +1 -1
- package/dist/history.d.ts +32 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/hooks/shared.d.ts +6 -4
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts +22 -0
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useObjectOverlay.d.ts +4 -3
- package/dist/hooks/useObjectOverlay.d.ts.map +1 -1
- package/dist/hooks/useViewCanvas.d.ts +7 -0
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +416 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +370 -112
- package/dist/index.js.map +1 -1
- package/dist/interactions/dragToCreate.d.ts +6 -1
- package/dist/interactions/dragToCreate.d.ts.map +1 -1
- package/dist/interactions/drawToCreate.d.ts +8 -3
- package/dist/interactions/drawToCreate.d.ts.map +1 -1
- package/dist/interactions/vertexEdit.d.ts +5 -1
- package/dist/interactions/vertexEdit.d.ts.map +1 -1
- package/dist/serialization.d.ts +25 -2
- package/dist/serialization.d.ts.map +1 -1
- package/dist/shapes/polygon.d.ts +2 -2
- package/dist/shapes/polygon.d.ts.map +1 -1
- package/dist/styles.d.ts +6 -0
- package/dist/styles.d.ts.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/viewport.d.ts +12 -2
- package/dist/viewport.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,7 +33,9 @@ function Canvas({
|
|
|
33
33
|
height,
|
|
34
34
|
className,
|
|
35
35
|
style,
|
|
36
|
-
onReady
|
|
36
|
+
onReady,
|
|
37
|
+
keyboardShortcuts,
|
|
38
|
+
fabricOptions
|
|
37
39
|
}) {
|
|
38
40
|
const canvasRef = useRef(null);
|
|
39
41
|
const wrapperRef = useRef(null);
|
|
@@ -45,11 +47,12 @@ function Canvas({
|
|
|
45
47
|
const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;
|
|
46
48
|
const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;
|
|
47
49
|
const fabricCanvas = new FabricCanvas(el, {
|
|
50
|
+
...fabricOptions,
|
|
48
51
|
width: initialWidth,
|
|
49
52
|
height: initialHeight
|
|
50
53
|
});
|
|
51
54
|
onReady?.(fabricCanvas);
|
|
52
|
-
const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);
|
|
55
|
+
const cleanupShortcuts = keyboardShortcuts ? enableKeyboardShortcuts(fabricCanvas) : void 0;
|
|
53
56
|
let observer;
|
|
54
57
|
let rafId = 0;
|
|
55
58
|
if (!isFixedSize) {
|
|
@@ -73,7 +76,7 @@ function Canvas({
|
|
|
73
76
|
return () => {
|
|
74
77
|
cancelAnimationFrame(rafId);
|
|
75
78
|
observer?.disconnect();
|
|
76
|
-
cleanupShortcuts();
|
|
79
|
+
cleanupShortcuts?.();
|
|
77
80
|
fabricCanvas.dispose();
|
|
78
81
|
};
|
|
79
82
|
}, []);
|
|
@@ -93,7 +96,7 @@ import {
|
|
|
93
96
|
// src/constants.ts
|
|
94
97
|
var DEFAULT_MIN_ZOOM = 0.2;
|
|
95
98
|
var DEFAULT_MAX_ZOOM = 10;
|
|
96
|
-
var DEFAULT_ZOOM_FACTOR =
|
|
99
|
+
var DEFAULT_ZOOM_FACTOR = 0.999;
|
|
97
100
|
var DEFAULT_ZOOM_STEP = 1.2;
|
|
98
101
|
var DEFAULT_VIEWPORT_PADDING = 0.05;
|
|
99
102
|
var BASE_CANVAS_SIZE = 1e3;
|
|
@@ -136,7 +139,7 @@ function setupWheelZoom(canvas, bounds, zoomFactor, isEnabled) {
|
|
|
136
139
|
e.stopPropagation();
|
|
137
140
|
const delta = e.deltaY;
|
|
138
141
|
let zoom = canvas.getZoom();
|
|
139
|
-
zoom
|
|
142
|
+
zoom *= zoomFactor ** delta;
|
|
140
143
|
zoom = Math.min(Math.max(zoom, bounds.minZoom), bounds.maxZoom);
|
|
141
144
|
canvas.zoomToPoint(new Point(e.offsetX, e.offsetY), zoom);
|
|
142
145
|
};
|
|
@@ -244,8 +247,15 @@ function enablePanAndZoom(canvas, options) {
|
|
|
244
247
|
const zoomFactor = options?.zoomFactor ?? DEFAULT_ZOOM_FACTOR;
|
|
245
248
|
let mode = options?.initialMode ?? "select";
|
|
246
249
|
let enabled = true;
|
|
250
|
+
let currentAnimRafId = null;
|
|
247
251
|
const isEnabled = () => enabled;
|
|
248
252
|
const getMode = () => mode;
|
|
253
|
+
function cancelAnimation() {
|
|
254
|
+
if (currentAnimRafId !== null) {
|
|
255
|
+
cancelAnimationFrame(currentAnimRafId);
|
|
256
|
+
currentAnimRafId = null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
249
259
|
const handleWheel = setupWheelZoom(canvas, bounds, zoomFactor, isEnabled);
|
|
250
260
|
const panHandlers = setupMousePan(canvas, getMode, isEnabled);
|
|
251
261
|
const cleanupPinch = setupPinchZoom(canvas, bounds, isEnabled);
|
|
@@ -291,6 +301,7 @@ function enablePanAndZoom(canvas, options) {
|
|
|
291
301
|
);
|
|
292
302
|
},
|
|
293
303
|
panToObject(object, panOpts) {
|
|
304
|
+
cancelAnimation();
|
|
294
305
|
const zoom = canvas.getZoom();
|
|
295
306
|
const objectCenter = object.getCenterPoint();
|
|
296
307
|
const canvasCenterX = canvas.getWidth() / 2;
|
|
@@ -333,12 +344,37 @@ function enablePanAndZoom(canvas, options) {
|
|
|
333
344
|
currentY
|
|
334
345
|
]);
|
|
335
346
|
if (t < 1) {
|
|
336
|
-
requestAnimationFrame(step);
|
|
347
|
+
currentAnimRafId = requestAnimationFrame(step);
|
|
348
|
+
} else {
|
|
349
|
+
currentAnimRafId = null;
|
|
337
350
|
}
|
|
338
351
|
}
|
|
339
|
-
requestAnimationFrame(step);
|
|
352
|
+
currentAnimRafId = requestAnimationFrame(step);
|
|
353
|
+
},
|
|
354
|
+
zoomToFit(object, fitOpts) {
|
|
355
|
+
cancelAnimation();
|
|
356
|
+
const padding = fitOpts?.padding ?? 0.1;
|
|
357
|
+
const objWidth = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
358
|
+
const objHeight = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
359
|
+
if (!objWidth || !objHeight) return;
|
|
360
|
+
const canvasWidth = canvas.getWidth();
|
|
361
|
+
const canvasHeight = canvas.getHeight();
|
|
362
|
+
const availableWidth = canvasWidth * (1 - padding * 2);
|
|
363
|
+
const availableHeight = canvasHeight * (1 - padding * 2);
|
|
364
|
+
const zoom = Math.min(
|
|
365
|
+
Math.max(
|
|
366
|
+
Math.min(availableWidth / objWidth, availableHeight / objHeight),
|
|
367
|
+
bounds.minZoom
|
|
368
|
+
),
|
|
369
|
+
bounds.maxZoom
|
|
370
|
+
);
|
|
371
|
+
const objectCenter = object.getCenterPoint();
|
|
372
|
+
const offsetX = canvasWidth / 2 - objectCenter.x * zoom;
|
|
373
|
+
const offsetY = canvasHeight / 2 - objectCenter.y * zoom;
|
|
374
|
+
canvas.setViewportTransform([zoom, 0, 0, zoom, offsetX, offsetY]);
|
|
340
375
|
},
|
|
341
376
|
cleanup() {
|
|
377
|
+
cancelAnimation();
|
|
342
378
|
canvas.off("mouse:wheel", handleWheel);
|
|
343
379
|
canvas.off("mouse:down", panHandlers.handleMouseDown);
|
|
344
380
|
canvas.off("mouse:move", panHandlers.handleMouseMove);
|
|
@@ -351,6 +387,9 @@ function resetViewport(canvas) {
|
|
|
351
387
|
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
|
|
352
388
|
}
|
|
353
389
|
|
|
390
|
+
// src/hooks/shared.ts
|
|
391
|
+
import { useMemo } from "react";
|
|
392
|
+
|
|
354
393
|
// src/background.ts
|
|
355
394
|
import { FabricImage, filters } from "fabric";
|
|
356
395
|
function getBackgroundImage(canvas) {
|
|
@@ -481,7 +520,8 @@ function resizeImageUrl(url, options) {
|
|
|
481
520
|
async function setBackgroundImage(canvas, url, options) {
|
|
482
521
|
const prevContrast = options?.preserveContrast ? getBackgroundContrast(canvas) : void 0;
|
|
483
522
|
let imageUrl = url;
|
|
484
|
-
|
|
523
|
+
const hasResizeOptions = options?.maxSize !== void 0 || options?.minSize !== void 0;
|
|
524
|
+
if (hasResizeOptions) {
|
|
485
525
|
const result = await resizeImageUrl(url, options);
|
|
486
526
|
imageUrl = result.url;
|
|
487
527
|
}
|
|
@@ -499,29 +539,35 @@ function syncZoom(canvasRef, setZoom) {
|
|
|
499
539
|
const canvas = canvasRef.current;
|
|
500
540
|
if (canvas) setZoom(canvas.getZoom());
|
|
501
541
|
}
|
|
502
|
-
function
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
542
|
+
function useViewportActions(canvasRef, viewportRef, setZoom) {
|
|
543
|
+
return useMemo(() => {
|
|
544
|
+
const resetViewport2 = () => {
|
|
545
|
+
const canvas = canvasRef.current;
|
|
546
|
+
if (!canvas) return;
|
|
547
|
+
if (canvas.backgroundImage) {
|
|
548
|
+
fitViewportToBackground(canvas);
|
|
549
|
+
} else {
|
|
550
|
+
resetViewport(canvas);
|
|
551
|
+
}
|
|
552
|
+
setZoom(canvas.getZoom());
|
|
553
|
+
};
|
|
554
|
+
const zoomIn = (step) => {
|
|
555
|
+
viewportRef.current?.zoomIn(step);
|
|
556
|
+
syncZoom(canvasRef, setZoom);
|
|
557
|
+
};
|
|
558
|
+
const zoomOut = (step) => {
|
|
559
|
+
viewportRef.current?.zoomOut(step);
|
|
560
|
+
syncZoom(canvasRef, setZoom);
|
|
561
|
+
};
|
|
562
|
+
const panToObject = (object, panOpts) => {
|
|
563
|
+
viewportRef.current?.panToObject(object, panOpts);
|
|
564
|
+
};
|
|
565
|
+
const zoomToFit = (object, fitOpts) => {
|
|
566
|
+
viewportRef.current?.zoomToFit(object, fitOpts);
|
|
567
|
+
syncZoom(canvasRef, setZoom);
|
|
568
|
+
};
|
|
569
|
+
return { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit };
|
|
570
|
+
}, []);
|
|
525
571
|
}
|
|
526
572
|
function resolveAlignmentEnabled(enableAlignment, alignmentProp) {
|
|
527
573
|
if (enableAlignment !== void 0) return enableAlignment;
|
|
@@ -1410,6 +1456,11 @@ function enableDragToCreate(canvas, factory, options) {
|
|
|
1410
1456
|
if (width < MIN_DRAG_SIZE && height < MIN_DRAG_SIZE) {
|
|
1411
1457
|
canvas.requestRenderAll();
|
|
1412
1458
|
previewRect = null;
|
|
1459
|
+
if (options?.clickFactory) {
|
|
1460
|
+
const obj2 = options.clickFactory(canvas, { x: startX, y: startY });
|
|
1461
|
+
restoreViewport(options?.viewport);
|
|
1462
|
+
options?.onCreated?.(obj2);
|
|
1463
|
+
}
|
|
1413
1464
|
return;
|
|
1414
1465
|
}
|
|
1415
1466
|
const obj = factory(canvas, { startX, startY, width, height });
|
|
@@ -1583,7 +1634,7 @@ function createPolygonAtPoint(canvas, point, options) {
|
|
|
1583
1634
|
canvas.requestRenderAll();
|
|
1584
1635
|
return polygon;
|
|
1585
1636
|
}
|
|
1586
|
-
function createPolygonFromDrag(canvas, start, end,
|
|
1637
|
+
function createPolygonFromDrag(canvas, start, end, options) {
|
|
1587
1638
|
const width = Math.abs(end.x - start.x);
|
|
1588
1639
|
const height = Math.abs(end.y - start.y);
|
|
1589
1640
|
const left = Math.min(start.x, end.x) + width / 2;
|
|
@@ -1595,16 +1646,16 @@ function createPolygonFromDrag(canvas, start, end, style) {
|
|
|
1595
1646
|
{ x: width, y: height },
|
|
1596
1647
|
{ x: 0, y: height }
|
|
1597
1648
|
],
|
|
1598
|
-
{ ...DEFAULT_SHAPE_STYLE, left, top, ...
|
|
1649
|
+
{ ...DEFAULT_SHAPE_STYLE, left, top, ...options }
|
|
1599
1650
|
);
|
|
1600
1651
|
canvas.add(polygon);
|
|
1601
1652
|
canvas.requestRenderAll();
|
|
1602
1653
|
return polygon;
|
|
1603
1654
|
}
|
|
1604
|
-
function createPolygonFromVertices(canvas, points,
|
|
1655
|
+
function createPolygonFromVertices(canvas, points, options) {
|
|
1605
1656
|
const polygon = new Polygon2(
|
|
1606
1657
|
points.map((p) => ({ x: p.x, y: p.y })),
|
|
1607
|
-
{ ...DEFAULT_SHAPE_STYLE, ...
|
|
1658
|
+
{ ...DEFAULT_SHAPE_STYLE, ...options }
|
|
1608
1659
|
);
|
|
1609
1660
|
canvas.add(polygon);
|
|
1610
1661
|
canvas.requestRenderAll();
|
|
@@ -1728,14 +1779,14 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1728
1779
|
const finalize = () => {
|
|
1729
1780
|
removePreviewElements();
|
|
1730
1781
|
snapping.clearSnapResult();
|
|
1731
|
-
const
|
|
1782
|
+
const obj = options?.factory ? options.factory(canvas, [...points]) : createPolygonFromVertices(canvas, points, options?.style);
|
|
1732
1783
|
if (options?.data) {
|
|
1733
|
-
|
|
1784
|
+
obj.data = options.data;
|
|
1734
1785
|
}
|
|
1735
1786
|
canvas.selection = previousSelection;
|
|
1736
1787
|
canvas.requestRenderAll();
|
|
1737
1788
|
restoreViewport(options?.viewport);
|
|
1738
|
-
options?.onCreated?.(
|
|
1789
|
+
options?.onCreated?.(obj);
|
|
1739
1790
|
points.length = 0;
|
|
1740
1791
|
};
|
|
1741
1792
|
const handleMouseDown = (event) => {
|
|
@@ -2047,14 +2098,19 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
|
|
|
2047
2098
|
});
|
|
2048
2099
|
canvas.discardActiveObject();
|
|
2049
2100
|
canvas.requestRenderAll();
|
|
2050
|
-
onExit?.();
|
|
2101
|
+
(options?.onExit ?? onExit)?.();
|
|
2051
2102
|
}
|
|
2052
2103
|
return cleanup;
|
|
2053
2104
|
}
|
|
2054
2105
|
|
|
2055
2106
|
// src/serialization.ts
|
|
2056
|
-
import {
|
|
2107
|
+
import {
|
|
2108
|
+
FabricImage as FabricImage2,
|
|
2109
|
+
Rect as Rect5
|
|
2110
|
+
} from "fabric";
|
|
2057
2111
|
var strokeBaseMap = /* @__PURE__ */ new WeakMap();
|
|
2112
|
+
var borderRadiusBaseMap = /* @__PURE__ */ new WeakMap();
|
|
2113
|
+
var DEFAULT_VIEW_BORDER_RADIUS = 4;
|
|
2058
2114
|
function enableScaledStrokes(canvas) {
|
|
2059
2115
|
function applyScaledStrokes() {
|
|
2060
2116
|
const zoom = canvas.getZoom();
|
|
@@ -2079,6 +2135,29 @@ function enableScaledStrokes(canvas) {
|
|
|
2079
2135
|
});
|
|
2080
2136
|
};
|
|
2081
2137
|
}
|
|
2138
|
+
function enableScaledBorderRadius(canvas, options) {
|
|
2139
|
+
const radius = options?.radius ?? DEFAULT_VIEW_BORDER_RADIUS;
|
|
2140
|
+
function applyScaledBorderRadius() {
|
|
2141
|
+
canvas.forEachObject((obj) => {
|
|
2142
|
+
if (!(obj instanceof Rect5)) return;
|
|
2143
|
+
if (!borderRadiusBaseMap.has(obj)) return;
|
|
2144
|
+
const rx = radius / (obj.scaleX ?? 1);
|
|
2145
|
+
const ry = radius / (obj.scaleY ?? 1);
|
|
2146
|
+
obj.set({ rx, ry });
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
canvas.on("before:render", applyScaledBorderRadius);
|
|
2150
|
+
return () => {
|
|
2151
|
+
canvas.off("before:render", applyScaledBorderRadius);
|
|
2152
|
+
canvas.forEachObject((obj) => {
|
|
2153
|
+
if (!(obj instanceof Rect5)) return;
|
|
2154
|
+
const base = borderRadiusBaseMap.get(obj);
|
|
2155
|
+
if (base !== void 0) {
|
|
2156
|
+
obj.set({ rx: base.rx, ry: base.ry });
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2082
2161
|
function getBaseStrokeWidth(obj) {
|
|
2083
2162
|
return strokeBaseMap.get(obj) ?? obj.strokeWidth ?? 0;
|
|
2084
2163
|
}
|
|
@@ -2104,15 +2183,42 @@ function serializeCanvas(canvas, options) {
|
|
|
2104
2183
|
obj.strokeWidth = base;
|
|
2105
2184
|
}
|
|
2106
2185
|
});
|
|
2186
|
+
const appliedRadii = /* @__PURE__ */ new Map();
|
|
2187
|
+
canvas.forEachObject((obj) => {
|
|
2188
|
+
if (!(obj instanceof Rect5)) return;
|
|
2189
|
+
const base = borderRadiusBaseMap.get(obj);
|
|
2190
|
+
if (base !== void 0) {
|
|
2191
|
+
appliedRadii.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });
|
|
2192
|
+
obj.set({ rx: base.rx, ry: base.ry });
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2107
2195
|
const json = canvas.toObject(properties);
|
|
2108
2196
|
delete json.backgroundColor;
|
|
2109
2197
|
scaledWidths.forEach((scaled, obj) => {
|
|
2110
2198
|
obj.strokeWidth = scaled;
|
|
2111
2199
|
});
|
|
2200
|
+
appliedRadii.forEach((radii, obj) => {
|
|
2201
|
+
obj.set({ rx: radii.rx, ry: radii.ry });
|
|
2202
|
+
});
|
|
2112
2203
|
return json;
|
|
2113
2204
|
}
|
|
2114
2205
|
async function loadCanvas(canvas, json, options) {
|
|
2115
2206
|
await canvas.loadFromJSON(json);
|
|
2207
|
+
canvas.backgroundColor = "";
|
|
2208
|
+
delete canvas.backgroundFilters;
|
|
2209
|
+
const bg = canvas.backgroundImage;
|
|
2210
|
+
if (bg instanceof FabricImage2) {
|
|
2211
|
+
if (bg.originX !== "center" || bg.originY !== "center") {
|
|
2212
|
+
const center = bg.getCenterPoint();
|
|
2213
|
+
bg.set({
|
|
2214
|
+
originX: "center",
|
|
2215
|
+
originY: "center",
|
|
2216
|
+
left: center.x,
|
|
2217
|
+
top: center.y
|
|
2218
|
+
});
|
|
2219
|
+
bg.setCoords();
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2116
2222
|
if (options?.filter) {
|
|
2117
2223
|
const toRemove = [];
|
|
2118
2224
|
canvas.forEachObject((obj) => {
|
|
@@ -2132,15 +2238,107 @@ async function loadCanvas(canvas, json, options) {
|
|
|
2132
2238
|
obj.setCoords();
|
|
2133
2239
|
});
|
|
2134
2240
|
canvas.forEachObject((obj) => {
|
|
2241
|
+
const data = obj.data;
|
|
2242
|
+
if (data?.strokeWidthBase !== void 0) {
|
|
2243
|
+
delete data.strokeWidthBase;
|
|
2244
|
+
}
|
|
2135
2245
|
obj.set(DEFAULT_CONTROL_STYLE);
|
|
2136
2246
|
if (obj.shapeType === "circle" && obj instanceof Rect5) {
|
|
2137
2247
|
restoreCircleConstraints(obj);
|
|
2138
2248
|
}
|
|
2249
|
+
const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;
|
|
2250
|
+
if (borderRadius !== false && obj instanceof Rect5 && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
|
|
2251
|
+
borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });
|
|
2252
|
+
const rx = borderRadius / (obj.scaleX ?? 1);
|
|
2253
|
+
const ry = borderRadius / (obj.scaleY ?? 1);
|
|
2254
|
+
obj.set({ rx, ry });
|
|
2255
|
+
}
|
|
2139
2256
|
});
|
|
2140
2257
|
canvas.requestRenderAll();
|
|
2141
2258
|
return canvas.getObjects();
|
|
2142
2259
|
}
|
|
2143
2260
|
|
|
2261
|
+
// src/history.ts
|
|
2262
|
+
function createHistoryTracker(canvas, options) {
|
|
2263
|
+
const maxSize = options?.maxSize ?? 50;
|
|
2264
|
+
const debounceMs = options?.debounce ?? 300;
|
|
2265
|
+
const snapshots = [];
|
|
2266
|
+
let currentIndex = -1;
|
|
2267
|
+
let isUndoRedo = false;
|
|
2268
|
+
let debounceTimer = null;
|
|
2269
|
+
function captureSnapshot() {
|
|
2270
|
+
if (isUndoRedo) return;
|
|
2271
|
+
const snapshot = serializeCanvas(canvas);
|
|
2272
|
+
if (currentIndex < snapshots.length - 1) {
|
|
2273
|
+
snapshots.length = currentIndex + 1;
|
|
2274
|
+
}
|
|
2275
|
+
snapshots.push(snapshot);
|
|
2276
|
+
if (snapshots.length > maxSize) {
|
|
2277
|
+
snapshots.shift();
|
|
2278
|
+
}
|
|
2279
|
+
currentIndex = snapshots.length - 1;
|
|
2280
|
+
}
|
|
2281
|
+
function debouncedCapture() {
|
|
2282
|
+
if (debounceTimer !== null) {
|
|
2283
|
+
clearTimeout(debounceTimer);
|
|
2284
|
+
}
|
|
2285
|
+
debounceTimer = setTimeout(() => {
|
|
2286
|
+
debounceTimer = null;
|
|
2287
|
+
captureSnapshot();
|
|
2288
|
+
}, debounceMs);
|
|
2289
|
+
}
|
|
2290
|
+
const onChange = () => {
|
|
2291
|
+
if (!isUndoRedo) debouncedCapture();
|
|
2292
|
+
};
|
|
2293
|
+
canvas.on("object:added", onChange);
|
|
2294
|
+
canvas.on("object:modified", onChange);
|
|
2295
|
+
canvas.on("object:removed", onChange);
|
|
2296
|
+
async function loadSnapshot(index) {
|
|
2297
|
+
if (index < 0 || index >= snapshots.length) return;
|
|
2298
|
+
isUndoRedo = true;
|
|
2299
|
+
currentIndex = index;
|
|
2300
|
+
try {
|
|
2301
|
+
await loadCanvas(canvas, snapshots[index]);
|
|
2302
|
+
} finally {
|
|
2303
|
+
isUndoRedo = false;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return {
|
|
2307
|
+
async undo() {
|
|
2308
|
+
if (currentIndex <= 0) return;
|
|
2309
|
+
await loadSnapshot(currentIndex - 1);
|
|
2310
|
+
},
|
|
2311
|
+
async redo() {
|
|
2312
|
+
if (currentIndex >= snapshots.length - 1) return;
|
|
2313
|
+
await loadSnapshot(currentIndex + 1);
|
|
2314
|
+
},
|
|
2315
|
+
canUndo() {
|
|
2316
|
+
return currentIndex > 0;
|
|
2317
|
+
},
|
|
2318
|
+
canRedo() {
|
|
2319
|
+
return currentIndex < snapshots.length - 1;
|
|
2320
|
+
},
|
|
2321
|
+
pushSnapshot() {
|
|
2322
|
+
if (debounceTimer !== null) {
|
|
2323
|
+
clearTimeout(debounceTimer);
|
|
2324
|
+
debounceTimer = null;
|
|
2325
|
+
}
|
|
2326
|
+
captureSnapshot();
|
|
2327
|
+
},
|
|
2328
|
+
cleanup() {
|
|
2329
|
+
if (debounceTimer !== null) {
|
|
2330
|
+
clearTimeout(debounceTimer);
|
|
2331
|
+
debounceTimer = null;
|
|
2332
|
+
}
|
|
2333
|
+
canvas.off("object:added", onChange);
|
|
2334
|
+
canvas.off("object:modified", onChange);
|
|
2335
|
+
canvas.off("object:removed", onChange);
|
|
2336
|
+
snapshots.length = 0;
|
|
2337
|
+
currentIndex = -1;
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2144
2342
|
// src/hooks/useEditCanvas.ts
|
|
2145
2343
|
function useEditCanvas(options) {
|
|
2146
2344
|
const canvasRef = useRef2(null);
|
|
@@ -2150,11 +2348,19 @@ function useEditCanvas(options) {
|
|
|
2150
2348
|
const modeCleanupRef = useRef2(null);
|
|
2151
2349
|
const vertexEditCleanupRef = useRef2(null);
|
|
2152
2350
|
const keyboardCleanupRef = useRef2(null);
|
|
2351
|
+
const historyRef = useRef2(null);
|
|
2352
|
+
const optionsRef = useRef2(options);
|
|
2353
|
+
optionsRef.current = options;
|
|
2354
|
+
const savedSelectabilityRef = useRef2(
|
|
2355
|
+
/* @__PURE__ */ new WeakMap()
|
|
2356
|
+
);
|
|
2153
2357
|
const [zoom, setZoom] = useState(1);
|
|
2154
2358
|
const [selected, setSelected] = useState([]);
|
|
2155
2359
|
const [viewportMode, setViewportModeState] = useState("select");
|
|
2156
2360
|
const [isEditingVertices, setIsEditingVertices] = useState(false);
|
|
2157
2361
|
const [isDirty, setIsDirty] = useState(false);
|
|
2362
|
+
const [canUndo, setCanUndo] = useState(false);
|
|
2363
|
+
const [canRedo, setCanRedo] = useState(false);
|
|
2158
2364
|
const setMode = useCallback((setup) => {
|
|
2159
2365
|
vertexEditCleanupRef.current?.();
|
|
2160
2366
|
vertexEditCleanupRef.current = null;
|
|
@@ -2166,10 +2372,17 @@ function useEditCanvas(options) {
|
|
|
2166
2372
|
if (setup === null) {
|
|
2167
2373
|
canvas.selection = true;
|
|
2168
2374
|
canvas.forEachObject((obj) => {
|
|
2169
|
-
|
|
2170
|
-
obj.
|
|
2375
|
+
const saved = savedSelectabilityRef.current.get(obj);
|
|
2376
|
+
obj.selectable = saved?.selectable ?? true;
|
|
2377
|
+
obj.evented = saved?.evented ?? true;
|
|
2171
2378
|
});
|
|
2172
2379
|
} else {
|
|
2380
|
+
canvas.forEachObject((obj) => {
|
|
2381
|
+
savedSelectabilityRef.current.set(obj, {
|
|
2382
|
+
selectable: obj.selectable,
|
|
2383
|
+
evented: obj.evented
|
|
2384
|
+
});
|
|
2385
|
+
});
|
|
2173
2386
|
canvas.selection = false;
|
|
2174
2387
|
canvas.forEachObject((obj) => {
|
|
2175
2388
|
obj.selectable = false;
|
|
@@ -2184,33 +2397,38 @@ function useEditCanvas(options) {
|
|
|
2184
2397
|
const onReady = useCallback(
|
|
2185
2398
|
(canvas) => {
|
|
2186
2399
|
canvasRef.current = canvas;
|
|
2187
|
-
|
|
2400
|
+
const opts = optionsRef.current;
|
|
2401
|
+
if (opts?.scaledStrokes !== false) {
|
|
2188
2402
|
enableScaledStrokes(canvas);
|
|
2189
2403
|
}
|
|
2190
|
-
if (
|
|
2404
|
+
if (opts?.borderRadius !== false) {
|
|
2405
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2406
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2407
|
+
}
|
|
2408
|
+
if (opts?.keyboardShortcuts !== false) {
|
|
2191
2409
|
keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
|
|
2192
2410
|
}
|
|
2193
|
-
setCanvasAlignmentEnabled(canvas,
|
|
2194
|
-
if (
|
|
2411
|
+
setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
|
|
2412
|
+
if (opts?.panAndZoom !== false) {
|
|
2195
2413
|
viewportRef.current = enablePanAndZoom(
|
|
2196
2414
|
canvas,
|
|
2197
|
-
typeof
|
|
2415
|
+
typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
|
|
2198
2416
|
);
|
|
2199
2417
|
}
|
|
2200
2418
|
const alignmentEnabled = resolveAlignmentEnabled(
|
|
2201
|
-
|
|
2202
|
-
|
|
2419
|
+
opts?.enableAlignment,
|
|
2420
|
+
opts?.alignment
|
|
2203
2421
|
);
|
|
2204
2422
|
if (alignmentEnabled) {
|
|
2205
2423
|
alignmentCleanupRef.current = enableObjectAlignment(
|
|
2206
2424
|
canvas,
|
|
2207
|
-
typeof
|
|
2425
|
+
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2208
2426
|
);
|
|
2209
2427
|
}
|
|
2210
|
-
if (
|
|
2428
|
+
if (opts?.rotationSnap !== false) {
|
|
2211
2429
|
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2212
2430
|
canvas,
|
|
2213
|
-
typeof
|
|
2431
|
+
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2214
2432
|
);
|
|
2215
2433
|
}
|
|
2216
2434
|
canvas.on("mouse:wheel", () => {
|
|
@@ -2225,41 +2443,60 @@ function useEditCanvas(options) {
|
|
|
2225
2443
|
canvas.on("selection:cleared", () => {
|
|
2226
2444
|
setSelected([]);
|
|
2227
2445
|
});
|
|
2228
|
-
if (
|
|
2446
|
+
if (opts?.trackChanges) {
|
|
2229
2447
|
canvas.on("object:added", () => setIsDirty(true));
|
|
2230
2448
|
canvas.on("object:removed", () => setIsDirty(true));
|
|
2231
2449
|
canvas.on("object:modified", () => setIsDirty(true));
|
|
2232
2450
|
}
|
|
2233
|
-
if (
|
|
2234
|
-
const
|
|
2451
|
+
if (opts?.history) {
|
|
2452
|
+
const syncHistoryState = () => {
|
|
2453
|
+
const h = historyRef.current;
|
|
2454
|
+
if (!h) return;
|
|
2455
|
+
setTimeout(() => {
|
|
2456
|
+
setCanUndo(h.canUndo());
|
|
2457
|
+
setCanRedo(h.canRedo());
|
|
2458
|
+
}, 350);
|
|
2459
|
+
};
|
|
2460
|
+
canvas.on("object:added", syncHistoryState);
|
|
2461
|
+
canvas.on("object:removed", syncHistoryState);
|
|
2462
|
+
canvas.on("object:modified", syncHistoryState);
|
|
2463
|
+
}
|
|
2464
|
+
if (opts?.vertexEdit !== false) {
|
|
2465
|
+
const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
|
|
2235
2466
|
canvas.on("mouse:dblclick", (e) => {
|
|
2236
2467
|
if (e.target && e.target instanceof Polygon4) {
|
|
2237
2468
|
vertexEditCleanupRef.current?.();
|
|
2238
|
-
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
vertexOpts,
|
|
2242
|
-
() => {
|
|
2469
|
+
vertexEditCleanupRef.current = enableVertexEdit(canvas, e.target, {
|
|
2470
|
+
...vertexOpts,
|
|
2471
|
+
onExit: () => {
|
|
2243
2472
|
vertexEditCleanupRef.current = null;
|
|
2244
2473
|
setIsEditingVertices(false);
|
|
2245
2474
|
}
|
|
2246
|
-
);
|
|
2475
|
+
});
|
|
2247
2476
|
setIsEditingVertices(true);
|
|
2248
2477
|
}
|
|
2249
2478
|
});
|
|
2250
2479
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2480
|
+
if (opts?.history) {
|
|
2481
|
+
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2482
|
+
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2483
|
+
}
|
|
2484
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2485
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2253
2486
|
Promise.resolve(onReadyResult).then(() => {
|
|
2254
2487
|
if (canvas.backgroundImage) {
|
|
2255
2488
|
fitViewportToBackground(canvas);
|
|
2256
2489
|
syncZoom(canvasRef, setZoom);
|
|
2257
2490
|
}
|
|
2491
|
+
historyRef.current?.pushSnapshot();
|
|
2492
|
+
});
|
|
2493
|
+
} else {
|
|
2494
|
+
Promise.resolve(onReadyResult).then(() => {
|
|
2495
|
+
historyRef.current?.pushSnapshot();
|
|
2258
2496
|
});
|
|
2259
2497
|
}
|
|
2260
2498
|
},
|
|
2261
2499
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2262
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2263
2500
|
[]
|
|
2264
2501
|
);
|
|
2265
2502
|
useEffect2(() => {
|
|
@@ -2284,24 +2521,20 @@ function useEditCanvas(options) {
|
|
|
2284
2521
|
viewportRef.current?.setMode(mode);
|
|
2285
2522
|
setViewportModeState(mode);
|
|
2286
2523
|
}, []);
|
|
2287
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2288
|
-
canvasRef,
|
|
2289
|
-
viewportRef,
|
|
2290
|
-
setZoom
|
|
2291
|
-
);
|
|
2524
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2292
2525
|
const setBackground = useCallback(
|
|
2293
2526
|
async (url, bgOpts) => {
|
|
2294
2527
|
const canvas = canvasRef.current;
|
|
2295
2528
|
if (!canvas) throw new Error("Canvas not ready");
|
|
2296
|
-
const
|
|
2529
|
+
const opts = optionsRef.current;
|
|
2530
|
+
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2297
2531
|
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2298
|
-
if (
|
|
2532
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2299
2533
|
fitViewportToBackground(canvas);
|
|
2300
2534
|
syncZoom(canvasRef, setZoom);
|
|
2301
2535
|
}
|
|
2302
2536
|
return img;
|
|
2303
2537
|
},
|
|
2304
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2305
2538
|
[]
|
|
2306
2539
|
);
|
|
2307
2540
|
return {
|
|
@@ -2326,7 +2559,9 @@ function useEditCanvas(options) {
|
|
|
2326
2559
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2327
2560
|
zoomOut,
|
|
2328
2561
|
/** Pan the viewport to center on a specific object. */
|
|
2329
|
-
panToObject
|
|
2562
|
+
panToObject,
|
|
2563
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2564
|
+
zoomToFit
|
|
2330
2565
|
},
|
|
2331
2566
|
/** Whether vertex edit mode is currently active (reactive). */
|
|
2332
2567
|
isEditingVertices,
|
|
@@ -2358,38 +2593,57 @@ function useEditCanvas(options) {
|
|
|
2358
2593
|
/** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
|
|
2359
2594
|
isDirty,
|
|
2360
2595
|
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2361
|
-
resetDirty: useCallback(() => setIsDirty(false), [])
|
|
2596
|
+
resetDirty: useCallback(() => setIsDirty(false), []),
|
|
2597
|
+
/** Undo the last change. Requires `history: true`. */
|
|
2598
|
+
undo: useCallback(async () => {
|
|
2599
|
+
const h = historyRef.current;
|
|
2600
|
+
if (!h) return;
|
|
2601
|
+
await h.undo();
|
|
2602
|
+
setCanUndo(h.canUndo());
|
|
2603
|
+
setCanRedo(h.canRedo());
|
|
2604
|
+
}, []),
|
|
2605
|
+
/** Redo a previously undone change. Requires `history: true`. */
|
|
2606
|
+
redo: useCallback(async () => {
|
|
2607
|
+
const h = historyRef.current;
|
|
2608
|
+
if (!h) return;
|
|
2609
|
+
await h.redo();
|
|
2610
|
+
setCanUndo(h.canUndo());
|
|
2611
|
+
setCanRedo(h.canRedo());
|
|
2612
|
+
}, []),
|
|
2613
|
+
/** Whether an undo operation is available (reactive). Requires `history: true`. */
|
|
2614
|
+
canUndo,
|
|
2615
|
+
/** Whether a redo operation is available (reactive). Requires `history: true`. */
|
|
2616
|
+
canRedo
|
|
2362
2617
|
};
|
|
2363
2618
|
}
|
|
2364
2619
|
|
|
2365
2620
|
// src/hooks/useViewCanvas.ts
|
|
2366
2621
|
import { useCallback as useCallback2, useRef as useRef3, useState as useState2 } from "react";
|
|
2367
|
-
import { Rect as Rect6 } from "fabric";
|
|
2368
|
-
var VIEW_BORDER_RADIUS = 4;
|
|
2369
2622
|
function lockCanvas(canvas) {
|
|
2370
2623
|
canvas.selection = false;
|
|
2371
2624
|
canvas.forEachObject((obj) => {
|
|
2372
2625
|
obj.selectable = false;
|
|
2373
|
-
obj.evented = false;
|
|
2374
|
-
if (obj instanceof Rect6 && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
|
|
2375
|
-
const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);
|
|
2376
|
-
const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);
|
|
2377
|
-
obj.set({ rx, ry });
|
|
2378
|
-
}
|
|
2379
2626
|
});
|
|
2380
2627
|
}
|
|
2381
2628
|
function useViewCanvas(options) {
|
|
2382
2629
|
const canvasRef = useRef3(null);
|
|
2383
2630
|
const viewportRef = useRef3(null);
|
|
2631
|
+
const optionsRef = useRef3(options);
|
|
2632
|
+
optionsRef.current = options;
|
|
2384
2633
|
const [zoom, setZoom] = useState2(1);
|
|
2385
2634
|
const onReady = useCallback2(
|
|
2386
2635
|
(canvas) => {
|
|
2387
2636
|
canvasRef.current = canvas;
|
|
2388
|
-
|
|
2637
|
+
const opts = optionsRef.current;
|
|
2638
|
+
if (opts?.scaledStrokes !== false) {
|
|
2389
2639
|
enableScaledStrokes(canvas);
|
|
2390
2640
|
}
|
|
2391
|
-
if (
|
|
2392
|
-
const
|
|
2641
|
+
if (opts?.borderRadius !== false) {
|
|
2642
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2643
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2644
|
+
}
|
|
2645
|
+
if (opts?.panAndZoom !== false) {
|
|
2646
|
+
const panAndZoomOpts = typeof opts?.panAndZoom === "object" ? opts.panAndZoom : {};
|
|
2393
2647
|
viewportRef.current = enablePanAndZoom(canvas, {
|
|
2394
2648
|
...panAndZoomOpts,
|
|
2395
2649
|
initialMode: "pan"
|
|
@@ -2402,8 +2656,8 @@ function useViewCanvas(options) {
|
|
|
2402
2656
|
canvas.on("mouse:wheel", () => {
|
|
2403
2657
|
setZoom(canvas.getZoom());
|
|
2404
2658
|
});
|
|
2405
|
-
const onReadyResult =
|
|
2406
|
-
if (
|
|
2659
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2660
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2407
2661
|
Promise.resolve(onReadyResult).then(() => {
|
|
2408
2662
|
if (canvas.backgroundImage) {
|
|
2409
2663
|
fitViewportToBackground(canvas);
|
|
@@ -2413,14 +2667,9 @@ function useViewCanvas(options) {
|
|
|
2413
2667
|
}
|
|
2414
2668
|
},
|
|
2415
2669
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2416
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2417
2670
|
[]
|
|
2418
2671
|
);
|
|
2419
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2420
|
-
canvasRef,
|
|
2421
|
-
viewportRef,
|
|
2422
|
-
setZoom
|
|
2423
|
-
);
|
|
2672
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2424
2673
|
const findObject = (id) => {
|
|
2425
2674
|
const c = canvasRef.current;
|
|
2426
2675
|
if (!c) return void 0;
|
|
@@ -2484,7 +2733,9 @@ function useViewCanvas(options) {
|
|
|
2484
2733
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2485
2734
|
zoomOut,
|
|
2486
2735
|
/** Pan the viewport to center on a specific object. */
|
|
2487
|
-
panToObject
|
|
2736
|
+
panToObject,
|
|
2737
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2738
|
+
zoomToFit
|
|
2488
2739
|
},
|
|
2489
2740
|
/** Update a single object's visual style by its `data.id`. */
|
|
2490
2741
|
setObjectStyle,
|
|
@@ -2657,36 +2908,41 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
2657
2908
|
const screenCoords = util4.transformPoint(center, vt);
|
|
2658
2909
|
const screenWidth = actualWidth * zoom;
|
|
2659
2910
|
const screenHeight = actualHeight * zoom;
|
|
2660
|
-
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2661
|
-
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
2662
|
-
el.style.width = `${screenWidth}px`;
|
|
2663
|
-
el.style.height = `${screenHeight}px`;
|
|
2664
2911
|
const angle = object.angle ?? 0;
|
|
2665
|
-
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2666
2912
|
const opts = optionsRef.current;
|
|
2667
|
-
if (opts?.autoScaleContent) {
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
el.style.
|
|
2671
|
-
|
|
2672
|
-
|
|
2913
|
+
if (opts?.autoScaleContent !== false) {
|
|
2914
|
+
el.style.left = `${screenCoords.x - actualWidth / 2}px`;
|
|
2915
|
+
el.style.top = `${screenCoords.y - actualHeight / 2}px`;
|
|
2916
|
+
el.style.width = `${actualWidth}px`;
|
|
2917
|
+
el.style.height = `${actualHeight}px`;
|
|
2918
|
+
el.style.transformOrigin = "center center";
|
|
2919
|
+
el.style.transform = angle !== 0 ? `scale(${zoom}) rotate(${angle}deg)` : `scale(${zoom})`;
|
|
2920
|
+
el.style.rotate = "";
|
|
2921
|
+
el.style.setProperty("--overlay-scale", String(zoom));
|
|
2922
|
+
if (opts?.textSelector) {
|
|
2923
|
+
const textMinScale = opts?.textMinScale ?? 0.5;
|
|
2673
2924
|
const textEls = el.querySelectorAll(opts.textSelector);
|
|
2674
|
-
const display =
|
|
2925
|
+
const display = zoom < textMinScale ? "none" : "";
|
|
2675
2926
|
textEls.forEach((t) => {
|
|
2676
2927
|
t.style.display = display;
|
|
2677
2928
|
});
|
|
2678
2929
|
}
|
|
2930
|
+
} else {
|
|
2931
|
+
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2932
|
+
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
2933
|
+
el.style.width = `${screenWidth}px`;
|
|
2934
|
+
el.style.height = `${screenHeight}px`;
|
|
2935
|
+
el.style.transform = "";
|
|
2936
|
+
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2679
2937
|
}
|
|
2680
2938
|
}
|
|
2681
2939
|
update();
|
|
2682
2940
|
canvas.on("after:render", update);
|
|
2683
|
-
canvas.on("mouse:wheel", update);
|
|
2684
2941
|
object.on("moving", update);
|
|
2685
2942
|
object.on("scaling", update);
|
|
2686
2943
|
object.on("rotating", update);
|
|
2687
2944
|
return () => {
|
|
2688
2945
|
canvas.off("after:render", update);
|
|
2689
|
-
canvas.off("mouse:wheel", update);
|
|
2690
2946
|
object.off("moving", update);
|
|
2691
2947
|
object.off("scaling", update);
|
|
2692
2948
|
object.off("rotating", update);
|
|
@@ -2699,8 +2955,8 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
2699
2955
|
import {
|
|
2700
2956
|
Canvas as Canvas2,
|
|
2701
2957
|
FabricObject as FabricObject5,
|
|
2702
|
-
FabricImage as
|
|
2703
|
-
Rect as
|
|
2958
|
+
FabricImage as FabricImage3,
|
|
2959
|
+
Rect as Rect6,
|
|
2704
2960
|
Polygon as Polygon5,
|
|
2705
2961
|
Point as Point9,
|
|
2706
2962
|
util as util5
|
|
@@ -2714,13 +2970,14 @@ export {
|
|
|
2714
2970
|
DEFAULT_GUIDELINE_SHAPE_STYLE,
|
|
2715
2971
|
DEFAULT_SHAPE_STYLE,
|
|
2716
2972
|
Canvas2 as FabricCanvas,
|
|
2717
|
-
|
|
2973
|
+
FabricImage3 as FabricImage,
|
|
2718
2974
|
FabricObject5 as FabricObject,
|
|
2719
2975
|
Point9 as Point,
|
|
2720
2976
|
Polygon5 as Polygon,
|
|
2721
|
-
|
|
2977
|
+
Rect6 as Rect,
|
|
2722
2978
|
createCircle,
|
|
2723
2979
|
createCircleAtPoint,
|
|
2980
|
+
createHistoryTracker,
|
|
2724
2981
|
createPolygon,
|
|
2725
2982
|
createPolygonAtPoint,
|
|
2726
2983
|
createPolygonFromDrag,
|
|
@@ -2738,6 +2995,7 @@ export {
|
|
|
2738
2995
|
enableObjectAlignment,
|
|
2739
2996
|
enablePanAndZoom,
|
|
2740
2997
|
enableRotationSnap,
|
|
2998
|
+
enableScaledBorderRadius,
|
|
2741
2999
|
enableScaledStrokes,
|
|
2742
3000
|
enableVertexEdit,
|
|
2743
3001
|
fitViewportToBackground,
|