@bwp-web/canvas 0.5.1 → 0.6.1
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 +411 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +374 -111
- package/dist/index.js.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 +20 -8
- 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/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;
|
|
@@ -1588,7 +1634,7 @@ function createPolygonAtPoint(canvas, point, options) {
|
|
|
1588
1634
|
canvas.requestRenderAll();
|
|
1589
1635
|
return polygon;
|
|
1590
1636
|
}
|
|
1591
|
-
function createPolygonFromDrag(canvas, start, end,
|
|
1637
|
+
function createPolygonFromDrag(canvas, start, end, options) {
|
|
1592
1638
|
const width = Math.abs(end.x - start.x);
|
|
1593
1639
|
const height = Math.abs(end.y - start.y);
|
|
1594
1640
|
const left = Math.min(start.x, end.x) + width / 2;
|
|
@@ -1600,16 +1646,16 @@ function createPolygonFromDrag(canvas, start, end, style) {
|
|
|
1600
1646
|
{ x: width, y: height },
|
|
1601
1647
|
{ x: 0, y: height }
|
|
1602
1648
|
],
|
|
1603
|
-
{ ...DEFAULT_SHAPE_STYLE, left, top, ...
|
|
1649
|
+
{ ...DEFAULT_SHAPE_STYLE, left, top, ...options }
|
|
1604
1650
|
);
|
|
1605
1651
|
canvas.add(polygon);
|
|
1606
1652
|
canvas.requestRenderAll();
|
|
1607
1653
|
return polygon;
|
|
1608
1654
|
}
|
|
1609
|
-
function createPolygonFromVertices(canvas, points,
|
|
1655
|
+
function createPolygonFromVertices(canvas, points, options) {
|
|
1610
1656
|
const polygon = new Polygon2(
|
|
1611
1657
|
points.map((p) => ({ x: p.x, y: p.y })),
|
|
1612
|
-
{ ...DEFAULT_SHAPE_STYLE, ...
|
|
1658
|
+
{ ...DEFAULT_SHAPE_STYLE, ...options }
|
|
1613
1659
|
);
|
|
1614
1660
|
canvas.add(polygon);
|
|
1615
1661
|
canvas.requestRenderAll();
|
|
@@ -1733,14 +1779,14 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1733
1779
|
const finalize = () => {
|
|
1734
1780
|
removePreviewElements();
|
|
1735
1781
|
snapping.clearSnapResult();
|
|
1736
|
-
const
|
|
1782
|
+
const obj = options?.factory ? options.factory(canvas, [...points]) : createPolygonFromVertices(canvas, points, options?.style);
|
|
1737
1783
|
if (options?.data) {
|
|
1738
|
-
|
|
1784
|
+
obj.data = options.data;
|
|
1739
1785
|
}
|
|
1740
1786
|
canvas.selection = previousSelection;
|
|
1741
1787
|
canvas.requestRenderAll();
|
|
1742
1788
|
restoreViewport(options?.viewport);
|
|
1743
|
-
options?.onCreated?.(
|
|
1789
|
+
options?.onCreated?.(obj);
|
|
1744
1790
|
points.length = 0;
|
|
1745
1791
|
};
|
|
1746
1792
|
const handleMouseDown = (event) => {
|
|
@@ -2052,16 +2098,19 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
|
|
|
2052
2098
|
});
|
|
2053
2099
|
canvas.discardActiveObject();
|
|
2054
2100
|
canvas.requestRenderAll();
|
|
2055
|
-
onExit?.();
|
|
2101
|
+
(options?.onExit ?? onExit)?.();
|
|
2056
2102
|
}
|
|
2057
2103
|
return cleanup;
|
|
2058
2104
|
}
|
|
2059
2105
|
|
|
2060
2106
|
// src/serialization.ts
|
|
2061
|
-
import {
|
|
2107
|
+
import {
|
|
2108
|
+
FabricImage as FabricImage2,
|
|
2109
|
+
Rect as Rect5
|
|
2110
|
+
} from "fabric";
|
|
2062
2111
|
var strokeBaseMap = /* @__PURE__ */ new WeakMap();
|
|
2063
2112
|
var borderRadiusBaseMap = /* @__PURE__ */ new WeakMap();
|
|
2064
|
-
var
|
|
2113
|
+
var DEFAULT_VIEW_BORDER_RADIUS = 4;
|
|
2065
2114
|
function enableScaledStrokes(canvas) {
|
|
2066
2115
|
function applyScaledStrokes() {
|
|
2067
2116
|
const zoom = canvas.getZoom();
|
|
@@ -2086,13 +2135,14 @@ function enableScaledStrokes(canvas) {
|
|
|
2086
2135
|
});
|
|
2087
2136
|
};
|
|
2088
2137
|
}
|
|
2089
|
-
function enableScaledBorderRadius(canvas) {
|
|
2138
|
+
function enableScaledBorderRadius(canvas, options) {
|
|
2139
|
+
const radius = options?.radius ?? DEFAULT_VIEW_BORDER_RADIUS;
|
|
2090
2140
|
function applyScaledBorderRadius() {
|
|
2091
2141
|
canvas.forEachObject((obj) => {
|
|
2092
2142
|
if (!(obj instanceof Rect5)) return;
|
|
2093
2143
|
if (!borderRadiusBaseMap.has(obj)) return;
|
|
2094
|
-
const rx =
|
|
2095
|
-
const ry =
|
|
2144
|
+
const rx = radius / (obj.scaleX ?? 1);
|
|
2145
|
+
const ry = radius / (obj.scaleY ?? 1);
|
|
2096
2146
|
obj.set({ rx, ry });
|
|
2097
2147
|
});
|
|
2098
2148
|
}
|
|
@@ -2142,19 +2192,81 @@ function serializeCanvas(canvas, options) {
|
|
|
2142
2192
|
obj.set({ rx: base.rx, ry: base.ry });
|
|
2143
2193
|
}
|
|
2144
2194
|
});
|
|
2195
|
+
const savedOrigins = /* @__PURE__ */ new Map();
|
|
2196
|
+
canvas.forEachObject((obj) => {
|
|
2197
|
+
if (obj.originX === "left" && obj.originY === "top") return;
|
|
2198
|
+
savedOrigins.set(obj, {
|
|
2199
|
+
originX: obj.originX,
|
|
2200
|
+
originY: obj.originY,
|
|
2201
|
+
left: obj.left ?? 0,
|
|
2202
|
+
top: obj.top ?? 0
|
|
2203
|
+
});
|
|
2204
|
+
const leftTop = obj.getPositionByOrigin("left", "top");
|
|
2205
|
+
obj.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
|
|
2206
|
+
});
|
|
2207
|
+
const bg = canvas.backgroundImage;
|
|
2208
|
+
let savedBgOrigin = null;
|
|
2209
|
+
if (bg instanceof FabricImage2 && (bg.originX !== "left" || bg.originY !== "top")) {
|
|
2210
|
+
savedBgOrigin = {
|
|
2211
|
+
originX: bg.originX,
|
|
2212
|
+
originY: bg.originY,
|
|
2213
|
+
left: bg.left ?? 0,
|
|
2214
|
+
top: bg.top ?? 0
|
|
2215
|
+
};
|
|
2216
|
+
const leftTop = bg.getPositionByOrigin("left", "top");
|
|
2217
|
+
bg.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
|
|
2218
|
+
}
|
|
2219
|
+
const savedData = /* @__PURE__ */ new Map();
|
|
2220
|
+
canvas.forEachObject((obj) => {
|
|
2221
|
+
const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;
|
|
2222
|
+
if (base !== void 0 && base !== 0 && obj.data) {
|
|
2223
|
+
savedData.set(obj, obj.data);
|
|
2224
|
+
obj.data = {
|
|
2225
|
+
...obj.data,
|
|
2226
|
+
strokeWidthBase: base
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
});
|
|
2145
2230
|
const json = canvas.toObject(properties);
|
|
2146
2231
|
delete json.backgroundColor;
|
|
2232
|
+
json.backgroundFilters = {
|
|
2233
|
+
opacity: getBackgroundContrast(canvas),
|
|
2234
|
+
inverted: getBackgroundInverted(canvas)
|
|
2235
|
+
};
|
|
2147
2236
|
scaledWidths.forEach((scaled, obj) => {
|
|
2148
2237
|
obj.strokeWidth = scaled;
|
|
2149
2238
|
});
|
|
2150
2239
|
appliedRadii.forEach((radii, obj) => {
|
|
2151
2240
|
obj.set({ rx: radii.rx, ry: radii.ry });
|
|
2152
2241
|
});
|
|
2242
|
+
savedOrigins.forEach((saved, obj) => {
|
|
2243
|
+
obj.set(saved);
|
|
2244
|
+
});
|
|
2245
|
+
if (savedBgOrigin && bg instanceof FabricImage2) {
|
|
2246
|
+
bg.set(savedBgOrigin);
|
|
2247
|
+
}
|
|
2248
|
+
savedData.forEach((originalData, obj) => {
|
|
2249
|
+
obj.data = originalData;
|
|
2250
|
+
});
|
|
2153
2251
|
return json;
|
|
2154
2252
|
}
|
|
2155
2253
|
async function loadCanvas(canvas, json, options) {
|
|
2156
2254
|
await canvas.loadFromJSON(json);
|
|
2157
2255
|
canvas.backgroundColor = "";
|
|
2256
|
+
delete canvas.backgroundFilters;
|
|
2257
|
+
const bg = canvas.backgroundImage;
|
|
2258
|
+
if (bg instanceof FabricImage2) {
|
|
2259
|
+
if (bg.originX !== "center" || bg.originY !== "center") {
|
|
2260
|
+
const center = bg.getCenterPoint();
|
|
2261
|
+
bg.set({
|
|
2262
|
+
originX: "center",
|
|
2263
|
+
originY: "center",
|
|
2264
|
+
left: center.x,
|
|
2265
|
+
top: center.y
|
|
2266
|
+
});
|
|
2267
|
+
bg.setCoords();
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2158
2270
|
if (options?.filter) {
|
|
2159
2271
|
const toRemove = [];
|
|
2160
2272
|
canvas.forEachObject((obj) => {
|
|
@@ -2174,14 +2286,19 @@ async function loadCanvas(canvas, json, options) {
|
|
|
2174
2286
|
obj.setCoords();
|
|
2175
2287
|
});
|
|
2176
2288
|
canvas.forEachObject((obj) => {
|
|
2289
|
+
const data = obj.data;
|
|
2290
|
+
if (data?.strokeWidthBase !== void 0) {
|
|
2291
|
+
delete data.strokeWidthBase;
|
|
2292
|
+
}
|
|
2177
2293
|
obj.set(DEFAULT_CONTROL_STYLE);
|
|
2178
2294
|
if (obj.shapeType === "circle" && obj instanceof Rect5) {
|
|
2179
2295
|
restoreCircleConstraints(obj);
|
|
2180
2296
|
}
|
|
2181
|
-
|
|
2297
|
+
const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;
|
|
2298
|
+
if (borderRadius !== false && obj instanceof Rect5 && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
|
|
2182
2299
|
borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });
|
|
2183
|
-
const rx =
|
|
2184
|
-
const ry =
|
|
2300
|
+
const rx = borderRadius / (obj.scaleX ?? 1);
|
|
2301
|
+
const ry = borderRadius / (obj.scaleY ?? 1);
|
|
2185
2302
|
obj.set({ rx, ry });
|
|
2186
2303
|
}
|
|
2187
2304
|
});
|
|
@@ -2189,6 +2306,87 @@ async function loadCanvas(canvas, json, options) {
|
|
|
2189
2306
|
return canvas.getObjects();
|
|
2190
2307
|
}
|
|
2191
2308
|
|
|
2309
|
+
// src/history.ts
|
|
2310
|
+
function createHistoryTracker(canvas, options) {
|
|
2311
|
+
const maxSize = options?.maxSize ?? 50;
|
|
2312
|
+
const debounceMs = options?.debounce ?? 300;
|
|
2313
|
+
const snapshots = [];
|
|
2314
|
+
let currentIndex = -1;
|
|
2315
|
+
let isUndoRedo = false;
|
|
2316
|
+
let debounceTimer = null;
|
|
2317
|
+
function captureSnapshot() {
|
|
2318
|
+
if (isUndoRedo) return;
|
|
2319
|
+
const snapshot = serializeCanvas(canvas);
|
|
2320
|
+
if (currentIndex < snapshots.length - 1) {
|
|
2321
|
+
snapshots.length = currentIndex + 1;
|
|
2322
|
+
}
|
|
2323
|
+
snapshots.push(snapshot);
|
|
2324
|
+
if (snapshots.length > maxSize) {
|
|
2325
|
+
snapshots.shift();
|
|
2326
|
+
}
|
|
2327
|
+
currentIndex = snapshots.length - 1;
|
|
2328
|
+
}
|
|
2329
|
+
function debouncedCapture() {
|
|
2330
|
+
if (debounceTimer !== null) {
|
|
2331
|
+
clearTimeout(debounceTimer);
|
|
2332
|
+
}
|
|
2333
|
+
debounceTimer = setTimeout(() => {
|
|
2334
|
+
debounceTimer = null;
|
|
2335
|
+
captureSnapshot();
|
|
2336
|
+
}, debounceMs);
|
|
2337
|
+
}
|
|
2338
|
+
const onChange = () => {
|
|
2339
|
+
if (!isUndoRedo) debouncedCapture();
|
|
2340
|
+
};
|
|
2341
|
+
canvas.on("object:added", onChange);
|
|
2342
|
+
canvas.on("object:modified", onChange);
|
|
2343
|
+
canvas.on("object:removed", onChange);
|
|
2344
|
+
async function loadSnapshot(index) {
|
|
2345
|
+
if (index < 0 || index >= snapshots.length) return;
|
|
2346
|
+
isUndoRedo = true;
|
|
2347
|
+
currentIndex = index;
|
|
2348
|
+
try {
|
|
2349
|
+
await loadCanvas(canvas, snapshots[index]);
|
|
2350
|
+
} finally {
|
|
2351
|
+
isUndoRedo = false;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return {
|
|
2355
|
+
async undo() {
|
|
2356
|
+
if (currentIndex <= 0) return;
|
|
2357
|
+
await loadSnapshot(currentIndex - 1);
|
|
2358
|
+
},
|
|
2359
|
+
async redo() {
|
|
2360
|
+
if (currentIndex >= snapshots.length - 1) return;
|
|
2361
|
+
await loadSnapshot(currentIndex + 1);
|
|
2362
|
+
},
|
|
2363
|
+
canUndo() {
|
|
2364
|
+
return currentIndex > 0;
|
|
2365
|
+
},
|
|
2366
|
+
canRedo() {
|
|
2367
|
+
return currentIndex < snapshots.length - 1;
|
|
2368
|
+
},
|
|
2369
|
+
pushSnapshot() {
|
|
2370
|
+
if (debounceTimer !== null) {
|
|
2371
|
+
clearTimeout(debounceTimer);
|
|
2372
|
+
debounceTimer = null;
|
|
2373
|
+
}
|
|
2374
|
+
captureSnapshot();
|
|
2375
|
+
},
|
|
2376
|
+
cleanup() {
|
|
2377
|
+
if (debounceTimer !== null) {
|
|
2378
|
+
clearTimeout(debounceTimer);
|
|
2379
|
+
debounceTimer = null;
|
|
2380
|
+
}
|
|
2381
|
+
canvas.off("object:added", onChange);
|
|
2382
|
+
canvas.off("object:modified", onChange);
|
|
2383
|
+
canvas.off("object:removed", onChange);
|
|
2384
|
+
snapshots.length = 0;
|
|
2385
|
+
currentIndex = -1;
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2192
2390
|
// src/hooks/useEditCanvas.ts
|
|
2193
2391
|
function useEditCanvas(options) {
|
|
2194
2392
|
const canvasRef = useRef2(null);
|
|
@@ -2198,11 +2396,19 @@ function useEditCanvas(options) {
|
|
|
2198
2396
|
const modeCleanupRef = useRef2(null);
|
|
2199
2397
|
const vertexEditCleanupRef = useRef2(null);
|
|
2200
2398
|
const keyboardCleanupRef = useRef2(null);
|
|
2399
|
+
const historyRef = useRef2(null);
|
|
2400
|
+
const optionsRef = useRef2(options);
|
|
2401
|
+
optionsRef.current = options;
|
|
2402
|
+
const savedSelectabilityRef = useRef2(
|
|
2403
|
+
/* @__PURE__ */ new WeakMap()
|
|
2404
|
+
);
|
|
2201
2405
|
const [zoom, setZoom] = useState(1);
|
|
2202
2406
|
const [selected, setSelected] = useState([]);
|
|
2203
2407
|
const [viewportMode, setViewportModeState] = useState("select");
|
|
2204
2408
|
const [isEditingVertices, setIsEditingVertices] = useState(false);
|
|
2205
2409
|
const [isDirty, setIsDirty] = useState(false);
|
|
2410
|
+
const [canUndo, setCanUndo] = useState(false);
|
|
2411
|
+
const [canRedo, setCanRedo] = useState(false);
|
|
2206
2412
|
const setMode = useCallback((setup) => {
|
|
2207
2413
|
vertexEditCleanupRef.current?.();
|
|
2208
2414
|
vertexEditCleanupRef.current = null;
|
|
@@ -2214,10 +2420,17 @@ function useEditCanvas(options) {
|
|
|
2214
2420
|
if (setup === null) {
|
|
2215
2421
|
canvas.selection = true;
|
|
2216
2422
|
canvas.forEachObject((obj) => {
|
|
2217
|
-
|
|
2218
|
-
obj.
|
|
2423
|
+
const saved = savedSelectabilityRef.current.get(obj);
|
|
2424
|
+
obj.selectable = saved?.selectable ?? true;
|
|
2425
|
+
obj.evented = saved?.evented ?? true;
|
|
2219
2426
|
});
|
|
2220
2427
|
} else {
|
|
2428
|
+
canvas.forEachObject((obj) => {
|
|
2429
|
+
savedSelectabilityRef.current.set(obj, {
|
|
2430
|
+
selectable: obj.selectable,
|
|
2431
|
+
evented: obj.evented
|
|
2432
|
+
});
|
|
2433
|
+
});
|
|
2221
2434
|
canvas.selection = false;
|
|
2222
2435
|
canvas.forEachObject((obj) => {
|
|
2223
2436
|
obj.selectable = false;
|
|
@@ -2232,34 +2445,38 @@ function useEditCanvas(options) {
|
|
|
2232
2445
|
const onReady = useCallback(
|
|
2233
2446
|
(canvas) => {
|
|
2234
2447
|
canvasRef.current = canvas;
|
|
2235
|
-
|
|
2448
|
+
const opts = optionsRef.current;
|
|
2449
|
+
if (opts?.scaledStrokes !== false) {
|
|
2236
2450
|
enableScaledStrokes(canvas);
|
|
2237
2451
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2452
|
+
if (opts?.borderRadius !== false) {
|
|
2453
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2454
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2455
|
+
}
|
|
2456
|
+
if (opts?.keyboardShortcuts !== false) {
|
|
2240
2457
|
keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
|
|
2241
2458
|
}
|
|
2242
|
-
setCanvasAlignmentEnabled(canvas,
|
|
2243
|
-
if (
|
|
2459
|
+
setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
|
|
2460
|
+
if (opts?.panAndZoom !== false) {
|
|
2244
2461
|
viewportRef.current = enablePanAndZoom(
|
|
2245
2462
|
canvas,
|
|
2246
|
-
typeof
|
|
2463
|
+
typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
|
|
2247
2464
|
);
|
|
2248
2465
|
}
|
|
2249
2466
|
const alignmentEnabled = resolveAlignmentEnabled(
|
|
2250
|
-
|
|
2251
|
-
|
|
2467
|
+
opts?.enableAlignment,
|
|
2468
|
+
opts?.alignment
|
|
2252
2469
|
);
|
|
2253
2470
|
if (alignmentEnabled) {
|
|
2254
2471
|
alignmentCleanupRef.current = enableObjectAlignment(
|
|
2255
2472
|
canvas,
|
|
2256
|
-
typeof
|
|
2473
|
+
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2257
2474
|
);
|
|
2258
2475
|
}
|
|
2259
|
-
if (
|
|
2476
|
+
if (opts?.rotationSnap !== false) {
|
|
2260
2477
|
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2261
2478
|
canvas,
|
|
2262
|
-
typeof
|
|
2479
|
+
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2263
2480
|
);
|
|
2264
2481
|
}
|
|
2265
2482
|
canvas.on("mouse:wheel", () => {
|
|
@@ -2274,41 +2491,60 @@ function useEditCanvas(options) {
|
|
|
2274
2491
|
canvas.on("selection:cleared", () => {
|
|
2275
2492
|
setSelected([]);
|
|
2276
2493
|
});
|
|
2277
|
-
if (
|
|
2494
|
+
if (opts?.trackChanges) {
|
|
2278
2495
|
canvas.on("object:added", () => setIsDirty(true));
|
|
2279
2496
|
canvas.on("object:removed", () => setIsDirty(true));
|
|
2280
2497
|
canvas.on("object:modified", () => setIsDirty(true));
|
|
2281
2498
|
}
|
|
2282
|
-
if (
|
|
2283
|
-
const
|
|
2499
|
+
if (opts?.history) {
|
|
2500
|
+
const syncHistoryState = () => {
|
|
2501
|
+
const h = historyRef.current;
|
|
2502
|
+
if (!h) return;
|
|
2503
|
+
setTimeout(() => {
|
|
2504
|
+
setCanUndo(h.canUndo());
|
|
2505
|
+
setCanRedo(h.canRedo());
|
|
2506
|
+
}, 350);
|
|
2507
|
+
};
|
|
2508
|
+
canvas.on("object:added", syncHistoryState);
|
|
2509
|
+
canvas.on("object:removed", syncHistoryState);
|
|
2510
|
+
canvas.on("object:modified", syncHistoryState);
|
|
2511
|
+
}
|
|
2512
|
+
if (opts?.vertexEdit !== false) {
|
|
2513
|
+
const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
|
|
2284
2514
|
canvas.on("mouse:dblclick", (e) => {
|
|
2285
2515
|
if (e.target && e.target instanceof Polygon4) {
|
|
2286
2516
|
vertexEditCleanupRef.current?.();
|
|
2287
|
-
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
vertexOpts,
|
|
2291
|
-
() => {
|
|
2517
|
+
vertexEditCleanupRef.current = enableVertexEdit(canvas, e.target, {
|
|
2518
|
+
...vertexOpts,
|
|
2519
|
+
onExit: () => {
|
|
2292
2520
|
vertexEditCleanupRef.current = null;
|
|
2293
2521
|
setIsEditingVertices(false);
|
|
2294
2522
|
}
|
|
2295
|
-
);
|
|
2523
|
+
});
|
|
2296
2524
|
setIsEditingVertices(true);
|
|
2297
2525
|
}
|
|
2298
2526
|
});
|
|
2299
2527
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2528
|
+
if (opts?.history) {
|
|
2529
|
+
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2530
|
+
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2531
|
+
}
|
|
2532
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2533
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2302
2534
|
Promise.resolve(onReadyResult).then(() => {
|
|
2303
2535
|
if (canvas.backgroundImage) {
|
|
2304
2536
|
fitViewportToBackground(canvas);
|
|
2305
2537
|
syncZoom(canvasRef, setZoom);
|
|
2306
2538
|
}
|
|
2539
|
+
historyRef.current?.pushSnapshot();
|
|
2540
|
+
});
|
|
2541
|
+
} else {
|
|
2542
|
+
Promise.resolve(onReadyResult).then(() => {
|
|
2543
|
+
historyRef.current?.pushSnapshot();
|
|
2307
2544
|
});
|
|
2308
2545
|
}
|
|
2309
2546
|
},
|
|
2310
2547
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2311
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2312
2548
|
[]
|
|
2313
2549
|
);
|
|
2314
2550
|
useEffect2(() => {
|
|
@@ -2333,24 +2569,20 @@ function useEditCanvas(options) {
|
|
|
2333
2569
|
viewportRef.current?.setMode(mode);
|
|
2334
2570
|
setViewportModeState(mode);
|
|
2335
2571
|
}, []);
|
|
2336
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2337
|
-
canvasRef,
|
|
2338
|
-
viewportRef,
|
|
2339
|
-
setZoom
|
|
2340
|
-
);
|
|
2572
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2341
2573
|
const setBackground = useCallback(
|
|
2342
2574
|
async (url, bgOpts) => {
|
|
2343
2575
|
const canvas = canvasRef.current;
|
|
2344
2576
|
if (!canvas) throw new Error("Canvas not ready");
|
|
2345
|
-
const
|
|
2577
|
+
const opts = optionsRef.current;
|
|
2578
|
+
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2346
2579
|
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2347
|
-
if (
|
|
2580
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2348
2581
|
fitViewportToBackground(canvas);
|
|
2349
2582
|
syncZoom(canvasRef, setZoom);
|
|
2350
2583
|
}
|
|
2351
2584
|
return img;
|
|
2352
2585
|
},
|
|
2353
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2354
2586
|
[]
|
|
2355
2587
|
);
|
|
2356
2588
|
return {
|
|
@@ -2375,7 +2607,9 @@ function useEditCanvas(options) {
|
|
|
2375
2607
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2376
2608
|
zoomOut,
|
|
2377
2609
|
/** Pan the viewport to center on a specific object. */
|
|
2378
|
-
panToObject
|
|
2610
|
+
panToObject,
|
|
2611
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2612
|
+
zoomToFit
|
|
2379
2613
|
},
|
|
2380
2614
|
/** Whether vertex edit mode is currently active (reactive). */
|
|
2381
2615
|
isEditingVertices,
|
|
@@ -2407,7 +2641,27 @@ function useEditCanvas(options) {
|
|
|
2407
2641
|
/** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
|
|
2408
2642
|
isDirty,
|
|
2409
2643
|
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2410
|
-
resetDirty: useCallback(() => setIsDirty(false), [])
|
|
2644
|
+
resetDirty: useCallback(() => setIsDirty(false), []),
|
|
2645
|
+
/** Undo the last change. Requires `history: true`. */
|
|
2646
|
+
undo: useCallback(async () => {
|
|
2647
|
+
const h = historyRef.current;
|
|
2648
|
+
if (!h) return;
|
|
2649
|
+
await h.undo();
|
|
2650
|
+
setCanUndo(h.canUndo());
|
|
2651
|
+
setCanRedo(h.canRedo());
|
|
2652
|
+
}, []),
|
|
2653
|
+
/** Redo a previously undone change. Requires `history: true`. */
|
|
2654
|
+
redo: useCallback(async () => {
|
|
2655
|
+
const h = historyRef.current;
|
|
2656
|
+
if (!h) return;
|
|
2657
|
+
await h.redo();
|
|
2658
|
+
setCanUndo(h.canUndo());
|
|
2659
|
+
setCanRedo(h.canRedo());
|
|
2660
|
+
}, []),
|
|
2661
|
+
/** Whether an undo operation is available (reactive). Requires `history: true`. */
|
|
2662
|
+
canUndo,
|
|
2663
|
+
/** Whether a redo operation is available (reactive). Requires `history: true`. */
|
|
2664
|
+
canRedo
|
|
2411
2665
|
};
|
|
2412
2666
|
}
|
|
2413
2667
|
|
|
@@ -2422,16 +2676,22 @@ function lockCanvas(canvas) {
|
|
|
2422
2676
|
function useViewCanvas(options) {
|
|
2423
2677
|
const canvasRef = useRef3(null);
|
|
2424
2678
|
const viewportRef = useRef3(null);
|
|
2679
|
+
const optionsRef = useRef3(options);
|
|
2680
|
+
optionsRef.current = options;
|
|
2425
2681
|
const [zoom, setZoom] = useState2(1);
|
|
2426
2682
|
const onReady = useCallback2(
|
|
2427
2683
|
(canvas) => {
|
|
2428
2684
|
canvasRef.current = canvas;
|
|
2429
|
-
|
|
2685
|
+
const opts = optionsRef.current;
|
|
2686
|
+
if (opts?.scaledStrokes !== false) {
|
|
2430
2687
|
enableScaledStrokes(canvas);
|
|
2431
2688
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2689
|
+
if (opts?.borderRadius !== false) {
|
|
2690
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2691
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2692
|
+
}
|
|
2693
|
+
if (opts?.panAndZoom !== false) {
|
|
2694
|
+
const panAndZoomOpts = typeof opts?.panAndZoom === "object" ? opts.panAndZoom : {};
|
|
2435
2695
|
viewportRef.current = enablePanAndZoom(canvas, {
|
|
2436
2696
|
...panAndZoomOpts,
|
|
2437
2697
|
initialMode: "pan"
|
|
@@ -2444,8 +2704,8 @@ function useViewCanvas(options) {
|
|
|
2444
2704
|
canvas.on("mouse:wheel", () => {
|
|
2445
2705
|
setZoom(canvas.getZoom());
|
|
2446
2706
|
});
|
|
2447
|
-
const onReadyResult =
|
|
2448
|
-
if (
|
|
2707
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2708
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2449
2709
|
Promise.resolve(onReadyResult).then(() => {
|
|
2450
2710
|
if (canvas.backgroundImage) {
|
|
2451
2711
|
fitViewportToBackground(canvas);
|
|
@@ -2455,14 +2715,9 @@ function useViewCanvas(options) {
|
|
|
2455
2715
|
}
|
|
2456
2716
|
},
|
|
2457
2717
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2458
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2459
2718
|
[]
|
|
2460
2719
|
);
|
|
2461
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2462
|
-
canvasRef,
|
|
2463
|
-
viewportRef,
|
|
2464
|
-
setZoom
|
|
2465
|
-
);
|
|
2720
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2466
2721
|
const findObject = (id) => {
|
|
2467
2722
|
const c = canvasRef.current;
|
|
2468
2723
|
if (!c) return void 0;
|
|
@@ -2526,7 +2781,9 @@ function useViewCanvas(options) {
|
|
|
2526
2781
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2527
2782
|
zoomOut,
|
|
2528
2783
|
/** Pan the viewport to center on a specific object. */
|
|
2529
|
-
panToObject
|
|
2784
|
+
panToObject,
|
|
2785
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2786
|
+
zoomToFit
|
|
2530
2787
|
},
|
|
2531
2788
|
/** Update a single object's visual style by its `data.id`. */
|
|
2532
2789
|
setObjectStyle,
|
|
@@ -2699,36 +2956,41 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
2699
2956
|
const screenCoords = util4.transformPoint(center, vt);
|
|
2700
2957
|
const screenWidth = actualWidth * zoom;
|
|
2701
2958
|
const screenHeight = actualHeight * zoom;
|
|
2702
|
-
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2703
|
-
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
2704
|
-
el.style.width = `${screenWidth}px`;
|
|
2705
|
-
el.style.height = `${screenHeight}px`;
|
|
2706
2959
|
const angle = object.angle ?? 0;
|
|
2707
|
-
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2708
2960
|
const opts = optionsRef.current;
|
|
2709
|
-
if (opts?.autoScaleContent) {
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
el.style.
|
|
2713
|
-
|
|
2714
|
-
|
|
2961
|
+
if (opts?.autoScaleContent !== false) {
|
|
2962
|
+
el.style.left = `${screenCoords.x - actualWidth / 2}px`;
|
|
2963
|
+
el.style.top = `${screenCoords.y - actualHeight / 2}px`;
|
|
2964
|
+
el.style.width = `${actualWidth}px`;
|
|
2965
|
+
el.style.height = `${actualHeight}px`;
|
|
2966
|
+
el.style.transformOrigin = "center center";
|
|
2967
|
+
el.style.transform = angle !== 0 ? `scale(${zoom}) rotate(${angle}deg)` : `scale(${zoom})`;
|
|
2968
|
+
el.style.rotate = "";
|
|
2969
|
+
el.style.setProperty("--overlay-scale", String(zoom));
|
|
2970
|
+
if (opts?.textSelector) {
|
|
2971
|
+
const textMinScale = opts?.textMinScale ?? 0.5;
|
|
2715
2972
|
const textEls = el.querySelectorAll(opts.textSelector);
|
|
2716
|
-
const display =
|
|
2973
|
+
const display = zoom < textMinScale ? "none" : "";
|
|
2717
2974
|
textEls.forEach((t) => {
|
|
2718
2975
|
t.style.display = display;
|
|
2719
2976
|
});
|
|
2720
2977
|
}
|
|
2978
|
+
} else {
|
|
2979
|
+
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2980
|
+
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
2981
|
+
el.style.width = `${screenWidth}px`;
|
|
2982
|
+
el.style.height = `${screenHeight}px`;
|
|
2983
|
+
el.style.transform = "";
|
|
2984
|
+
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2721
2985
|
}
|
|
2722
2986
|
}
|
|
2723
2987
|
update();
|
|
2724
2988
|
canvas.on("after:render", update);
|
|
2725
|
-
canvas.on("mouse:wheel", update);
|
|
2726
2989
|
object.on("moving", update);
|
|
2727
2990
|
object.on("scaling", update);
|
|
2728
2991
|
object.on("rotating", update);
|
|
2729
2992
|
return () => {
|
|
2730
2993
|
canvas.off("after:render", update);
|
|
2731
|
-
canvas.off("mouse:wheel", update);
|
|
2732
2994
|
object.off("moving", update);
|
|
2733
2995
|
object.off("scaling", update);
|
|
2734
2996
|
object.off("rotating", update);
|
|
@@ -2741,7 +3003,7 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
2741
3003
|
import {
|
|
2742
3004
|
Canvas as Canvas2,
|
|
2743
3005
|
FabricObject as FabricObject5,
|
|
2744
|
-
FabricImage as
|
|
3006
|
+
FabricImage as FabricImage3,
|
|
2745
3007
|
Rect as Rect6,
|
|
2746
3008
|
Polygon as Polygon5,
|
|
2747
3009
|
Point as Point9,
|
|
@@ -2756,13 +3018,14 @@ export {
|
|
|
2756
3018
|
DEFAULT_GUIDELINE_SHAPE_STYLE,
|
|
2757
3019
|
DEFAULT_SHAPE_STYLE,
|
|
2758
3020
|
Canvas2 as FabricCanvas,
|
|
2759
|
-
|
|
3021
|
+
FabricImage3 as FabricImage,
|
|
2760
3022
|
FabricObject5 as FabricObject,
|
|
2761
3023
|
Point9 as Point,
|
|
2762
3024
|
Polygon5 as Polygon,
|
|
2763
3025
|
Rect6 as Rect,
|
|
2764
3026
|
createCircle,
|
|
2765
3027
|
createCircleAtPoint,
|
|
3028
|
+
createHistoryTracker,
|
|
2766
3029
|
createPolygon,
|
|
2767
3030
|
createPolygonAtPoint,
|
|
2768
3031
|
createPolygonFromDrag,
|