@bwp-web/canvas 0.6.0 → 0.6.2
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/alignment/cursorSnapping.d.ts.map +1 -1
- package/dist/alignment/objectAlignment.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/index.cjs +278 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +278 -188
- package/dist/index.js.map +1 -1
- package/dist/serialization.d.ts +7 -5
- package/dist/serialization.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/alignment/objectAlignmentRendering.d.ts +0 -21
- package/dist/alignment/objectAlignmentRendering.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -102,6 +102,10 @@ var DEFAULT_VIEWPORT_PADDING = 0.05;
|
|
|
102
102
|
var BASE_CANVAS_SIZE = 1e3;
|
|
103
103
|
var DEFAULT_SNAP_MARGIN = 6;
|
|
104
104
|
var DEFAULT_ANGLE_SNAP_INTERVAL = 15;
|
|
105
|
+
function computeSnapMargin(canvasWidth, canvasHeight, zoom, baseMargin = DEFAULT_SNAP_MARGIN, scaleWithCanvasSize = true) {
|
|
106
|
+
const sizeScale = scaleWithCanvasSize ? Math.max(canvasWidth || 800, canvasHeight || 600) / BASE_CANVAS_SIZE : 1;
|
|
107
|
+
return baseMargin * sizeScale / zoom;
|
|
108
|
+
}
|
|
105
109
|
var MIN_DRAG_SIZE = 3;
|
|
106
110
|
var POLYGON_CLOSE_THRESHOLD = 10;
|
|
107
111
|
var DEFAULT_IMAGE_MAX_SIZE = 4096;
|
|
@@ -309,23 +313,23 @@ function enablePanAndZoom(canvas, options) {
|
|
|
309
313
|
const targetX = canvasCenterX - objectCenter.x * zoom;
|
|
310
314
|
const targetY = canvasCenterY - objectCenter.y * zoom;
|
|
311
315
|
if (!panOpts?.animate) {
|
|
312
|
-
const
|
|
313
|
-
if (!
|
|
316
|
+
const viewportTransform2 = canvas.viewportTransform;
|
|
317
|
+
if (!viewportTransform2) return;
|
|
314
318
|
canvas.setViewportTransform([
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
+
viewportTransform2[0],
|
|
320
|
+
viewportTransform2[1],
|
|
321
|
+
viewportTransform2[2],
|
|
322
|
+
viewportTransform2[3],
|
|
319
323
|
targetX,
|
|
320
324
|
targetY
|
|
321
325
|
]);
|
|
322
326
|
return;
|
|
323
327
|
}
|
|
324
328
|
const duration = panOpts.duration ?? 300;
|
|
325
|
-
const
|
|
326
|
-
if (!
|
|
327
|
-
const startX =
|
|
328
|
-
const startY =
|
|
329
|
+
const viewportTransform = canvas.viewportTransform;
|
|
330
|
+
if (!viewportTransform) return;
|
|
331
|
+
const startX = viewportTransform[4];
|
|
332
|
+
const startY = viewportTransform[5];
|
|
329
333
|
const startTime = performance.now();
|
|
330
334
|
function step(now) {
|
|
331
335
|
const elapsed = now - startTime;
|
|
@@ -333,13 +337,13 @@ function enablePanAndZoom(canvas, options) {
|
|
|
333
337
|
const eased = 1 - Math.pow(1 - t, 3);
|
|
334
338
|
const currentX = startX + (targetX - startX) * eased;
|
|
335
339
|
const currentY = startY + (targetY - startY) * eased;
|
|
336
|
-
const
|
|
337
|
-
if (!
|
|
340
|
+
const currentTransform = canvas.viewportTransform;
|
|
341
|
+
if (!currentTransform) return;
|
|
338
342
|
canvas.setViewportTransform([
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
+
currentTransform[0],
|
|
344
|
+
currentTransform[1],
|
|
345
|
+
currentTransform[2],
|
|
346
|
+
currentTransform[3],
|
|
343
347
|
currentX,
|
|
344
348
|
currentY
|
|
345
349
|
]);
|
|
@@ -526,6 +530,7 @@ async function setBackgroundImage(canvas, url, options) {
|
|
|
526
530
|
imageUrl = result.url;
|
|
527
531
|
}
|
|
528
532
|
const img = await FabricImage.fromURL(imageUrl, { crossOrigin: "anonymous" });
|
|
533
|
+
img.set({ left: img.width / 2, top: img.height / 2 });
|
|
529
534
|
canvas.backgroundImage = img;
|
|
530
535
|
if (prevContrast !== void 0 && prevContrast !== 1) {
|
|
531
536
|
setBackgroundContrast(canvas, prevContrast);
|
|
@@ -723,63 +728,6 @@ registerSnapPointExtractor(
|
|
|
723
728
|
// src/alignment/objectAlignment.ts
|
|
724
729
|
import { util as util2 } from "fabric";
|
|
725
730
|
|
|
726
|
-
// src/alignment/objectAlignmentRendering.ts
|
|
727
|
-
function drawAlignmentLine(config, origin, target) {
|
|
728
|
-
const ctx = config.canvas.getTopContext();
|
|
729
|
-
const vt = config.canvas.viewportTransform;
|
|
730
|
-
const zoom = config.canvas.getZoom();
|
|
731
|
-
ctx.save();
|
|
732
|
-
ctx.transform(...vt);
|
|
733
|
-
ctx.lineWidth = config.width / zoom;
|
|
734
|
-
if (config.lineDash) ctx.setLineDash(config.lineDash);
|
|
735
|
-
ctx.strokeStyle = config.color;
|
|
736
|
-
ctx.beginPath();
|
|
737
|
-
ctx.moveTo(origin.x, origin.y);
|
|
738
|
-
ctx.lineTo(target.x, target.y);
|
|
739
|
-
ctx.stroke();
|
|
740
|
-
if (config.lineDash) ctx.setLineDash([]);
|
|
741
|
-
drawXMarker(ctx, origin, config.xSize / zoom);
|
|
742
|
-
drawXMarker(ctx, target, config.xSize / zoom);
|
|
743
|
-
ctx.restore();
|
|
744
|
-
}
|
|
745
|
-
function drawXMarker(ctx, point, size) {
|
|
746
|
-
ctx.save();
|
|
747
|
-
ctx.translate(point.x, point.y);
|
|
748
|
-
ctx.beginPath();
|
|
749
|
-
ctx.moveTo(-size, -size);
|
|
750
|
-
ctx.lineTo(size, size);
|
|
751
|
-
ctx.moveTo(size, -size);
|
|
752
|
-
ctx.lineTo(-size, size);
|
|
753
|
-
ctx.stroke();
|
|
754
|
-
ctx.restore();
|
|
755
|
-
}
|
|
756
|
-
function drawMarkerList(config, lines) {
|
|
757
|
-
const ctx = config.canvas.getTopContext();
|
|
758
|
-
const vt = config.canvas.viewportTransform;
|
|
759
|
-
const zoom = config.canvas.getZoom();
|
|
760
|
-
const markerSize = config.xSize / zoom;
|
|
761
|
-
ctx.save();
|
|
762
|
-
ctx.transform(...vt);
|
|
763
|
-
ctx.lineWidth = config.width / zoom;
|
|
764
|
-
ctx.strokeStyle = config.color;
|
|
765
|
-
for (const item of lines) drawXMarker(ctx, item.target, markerSize);
|
|
766
|
-
ctx.restore();
|
|
767
|
-
}
|
|
768
|
-
function drawVerticalAlignmentLines(config, lines) {
|
|
769
|
-
for (const v of lines) {
|
|
770
|
-
const { origin, target } = JSON.parse(v);
|
|
771
|
-
const from = { x: target.x, y: origin.y };
|
|
772
|
-
drawAlignmentLine(config, from, target);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
function drawHorizontalAlignmentLines(config, lines) {
|
|
776
|
-
for (const h of lines) {
|
|
777
|
-
const { origin, target } = JSON.parse(h);
|
|
778
|
-
const from = { x: origin.x, y: target.y };
|
|
779
|
-
drawAlignmentLine(config, from, target);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
|
|
783
731
|
// src/alignment/objectAlignmentMath.ts
|
|
784
732
|
var OPPOSITE_ORIGIN_MAP = {
|
|
785
733
|
tl: ["right", "bottom"],
|
|
@@ -903,6 +851,61 @@ function collectHorizontalSnapOffset(props) {
|
|
|
903
851
|
}
|
|
904
852
|
|
|
905
853
|
// src/alignment/objectAlignment.ts
|
|
854
|
+
function drawXMarker(ctx, point, size) {
|
|
855
|
+
ctx.save();
|
|
856
|
+
ctx.translate(point.x, point.y);
|
|
857
|
+
ctx.beginPath();
|
|
858
|
+
ctx.moveTo(-size, -size);
|
|
859
|
+
ctx.lineTo(size, size);
|
|
860
|
+
ctx.moveTo(size, -size);
|
|
861
|
+
ctx.lineTo(-size, size);
|
|
862
|
+
ctx.stroke();
|
|
863
|
+
ctx.restore();
|
|
864
|
+
}
|
|
865
|
+
function drawAlignmentLine(config, origin, target) {
|
|
866
|
+
const ctx = config.canvas.getTopContext();
|
|
867
|
+
const vt = config.canvas.viewportTransform;
|
|
868
|
+
const zoom = config.canvas.getZoom();
|
|
869
|
+
ctx.save();
|
|
870
|
+
ctx.transform(...vt);
|
|
871
|
+
ctx.lineWidth = config.width / zoom;
|
|
872
|
+
if (config.lineDash) ctx.setLineDash(config.lineDash);
|
|
873
|
+
ctx.strokeStyle = config.color;
|
|
874
|
+
ctx.beginPath();
|
|
875
|
+
ctx.moveTo(origin.x, origin.y);
|
|
876
|
+
ctx.lineTo(target.x, target.y);
|
|
877
|
+
ctx.stroke();
|
|
878
|
+
if (config.lineDash) ctx.setLineDash([]);
|
|
879
|
+
drawXMarker(ctx, origin, config.xSize / zoom);
|
|
880
|
+
drawXMarker(ctx, target, config.xSize / zoom);
|
|
881
|
+
ctx.restore();
|
|
882
|
+
}
|
|
883
|
+
function drawMarkerList(config, lines) {
|
|
884
|
+
const ctx = config.canvas.getTopContext();
|
|
885
|
+
const vt = config.canvas.viewportTransform;
|
|
886
|
+
const zoom = config.canvas.getZoom();
|
|
887
|
+
const markerSize = config.xSize / zoom;
|
|
888
|
+
ctx.save();
|
|
889
|
+
ctx.transform(...vt);
|
|
890
|
+
ctx.lineWidth = config.width / zoom;
|
|
891
|
+
ctx.strokeStyle = config.color;
|
|
892
|
+
for (const item of lines) drawXMarker(ctx, item.target, markerSize);
|
|
893
|
+
ctx.restore();
|
|
894
|
+
}
|
|
895
|
+
function drawVerticalAlignmentLines(config, lines) {
|
|
896
|
+
for (const v of lines) {
|
|
897
|
+
const { origin, target } = JSON.parse(v);
|
|
898
|
+
const from = { x: target.x, y: origin.y };
|
|
899
|
+
drawAlignmentLine(config, from, target);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function drawHorizontalAlignmentLines(config, lines) {
|
|
903
|
+
for (const h of lines) {
|
|
904
|
+
const { origin, target } = JSON.parse(h);
|
|
905
|
+
const from = { x: origin.x, y: target.y };
|
|
906
|
+
drawAlignmentLine(config, from, target);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
906
909
|
function adjustCornerForFlip(corner, target) {
|
|
907
910
|
let adjusted = corner;
|
|
908
911
|
if (target.flipX) {
|
|
@@ -957,9 +960,13 @@ var ObjectAlignmentGuides = class {
|
|
|
957
960
|
}
|
|
958
961
|
// --- Margin calculation ---
|
|
959
962
|
computeMargin() {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
+
return computeSnapMargin(
|
|
964
|
+
this.canvas.width ?? 800,
|
|
965
|
+
this.canvas.height ?? 600,
|
|
966
|
+
this.canvas.getZoom(),
|
|
967
|
+
this.margin,
|
|
968
|
+
this.scaleWithCanvasSize
|
|
969
|
+
);
|
|
963
970
|
}
|
|
964
971
|
// --- Snap point caching ---
|
|
965
972
|
getCachedSnapPoints(object) {
|
|
@@ -1095,10 +1102,13 @@ function enableRotationSnap(canvas, options) {
|
|
|
1095
1102
|
// src/alignment/cursorSnapping.ts
|
|
1096
1103
|
import { Point as Point5 } from "fabric";
|
|
1097
1104
|
function snapCursorPoint(canvas, rawPoint, options) {
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1105
|
+
const margin = computeSnapMargin(
|
|
1106
|
+
canvas.width ?? 800,
|
|
1107
|
+
canvas.height ?? 600,
|
|
1108
|
+
canvas.getZoom(),
|
|
1109
|
+
options?.margin ?? DEFAULT_SNAP_MARGIN,
|
|
1110
|
+
options?.scaleWithCanvasSize !== false
|
|
1111
|
+
);
|
|
1102
1112
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
1103
1113
|
let targetPoints;
|
|
1104
1114
|
if (options?.targetPoints) {
|
|
@@ -2161,20 +2171,7 @@ function enableScaledBorderRadius(canvas, options) {
|
|
|
2161
2171
|
function getBaseStrokeWidth(obj) {
|
|
2162
2172
|
return strokeBaseMap.get(obj) ?? obj.strokeWidth ?? 0;
|
|
2163
2173
|
}
|
|
2164
|
-
function
|
|
2165
|
-
const properties = [
|
|
2166
|
-
"data",
|
|
2167
|
-
"shapeType",
|
|
2168
|
-
// Control styling — absent from Fabric's default toObject output
|
|
2169
|
-
"borderColor",
|
|
2170
|
-
"cornerColor",
|
|
2171
|
-
"cornerStrokeColor",
|
|
2172
|
-
"transparentCorners",
|
|
2173
|
-
// Interaction locks — absent from Fabric's default toObject output
|
|
2174
|
-
"lockRotation",
|
|
2175
|
-
"lockUniScaling",
|
|
2176
|
-
...options?.properties ?? []
|
|
2177
|
-
];
|
|
2174
|
+
function prepareStrokeWidths(canvas) {
|
|
2178
2175
|
const scaledWidths = /* @__PURE__ */ new Map();
|
|
2179
2176
|
canvas.forEachObject((obj) => {
|
|
2180
2177
|
const base = strokeBaseMap.get(obj);
|
|
@@ -2183,6 +2180,11 @@ function serializeCanvas(canvas, options) {
|
|
|
2183
2180
|
obj.strokeWidth = base;
|
|
2184
2181
|
}
|
|
2185
2182
|
});
|
|
2183
|
+
return () => scaledWidths.forEach((scaled, obj) => {
|
|
2184
|
+
obj.strokeWidth = scaled;
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
function prepareBorderRadii(canvas) {
|
|
2186
2188
|
const appliedRadii = /* @__PURE__ */ new Map();
|
|
2187
2189
|
canvas.forEachObject((obj) => {
|
|
2188
2190
|
if (!(obj instanceof Rect5)) return;
|
|
@@ -2192,20 +2194,109 @@ function serializeCanvas(canvas, options) {
|
|
|
2192
2194
|
obj.set({ rx: base.rx, ry: base.ry });
|
|
2193
2195
|
}
|
|
2194
2196
|
});
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
scaledWidths.forEach((scaled, obj) => {
|
|
2198
|
-
obj.strokeWidth = scaled;
|
|
2197
|
+
return () => appliedRadii.forEach((radii, obj) => {
|
|
2198
|
+
obj.set(radii);
|
|
2199
2199
|
});
|
|
2200
|
-
|
|
2201
|
-
|
|
2200
|
+
}
|
|
2201
|
+
function prepareObjectOrigins(canvas) {
|
|
2202
|
+
const savedOrigins = /* @__PURE__ */ new Map();
|
|
2203
|
+
canvas.forEachObject((obj) => {
|
|
2204
|
+
if (obj.originX === "left" && obj.originY === "top") return;
|
|
2205
|
+
savedOrigins.set(obj, {
|
|
2206
|
+
originX: obj.originX,
|
|
2207
|
+
originY: obj.originY,
|
|
2208
|
+
left: obj.left ?? 0,
|
|
2209
|
+
top: obj.top ?? 0
|
|
2210
|
+
});
|
|
2211
|
+
const leftTop = obj.getPositionByOrigin("left", "top");
|
|
2212
|
+
obj.set({
|
|
2213
|
+
originX: "left",
|
|
2214
|
+
originY: "top",
|
|
2215
|
+
left: leftTop.x,
|
|
2216
|
+
top: leftTop.y
|
|
2217
|
+
});
|
|
2218
|
+
});
|
|
2219
|
+
return () => savedOrigins.forEach((saved, obj) => {
|
|
2220
|
+
obj.set(saved);
|
|
2202
2221
|
});
|
|
2222
|
+
}
|
|
2223
|
+
function prepareBackgroundOrigin(canvas) {
|
|
2224
|
+
const bg = canvas.backgroundImage;
|
|
2225
|
+
if (!(bg instanceof FabricImage2) || bg.originX === "left" && bg.originY === "top") {
|
|
2226
|
+
return () => {
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
const saved = {
|
|
2230
|
+
originX: bg.originX,
|
|
2231
|
+
originY: bg.originY,
|
|
2232
|
+
left: bg.left ?? 0,
|
|
2233
|
+
top: bg.top ?? 0
|
|
2234
|
+
};
|
|
2235
|
+
const leftTop = bg.getPositionByOrigin("left", "top");
|
|
2236
|
+
bg.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
|
|
2237
|
+
return () => {
|
|
2238
|
+
bg.set(saved);
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
function prepareStrokeWidthBaseData(canvas) {
|
|
2242
|
+
const savedData = /* @__PURE__ */ new Map();
|
|
2243
|
+
canvas.forEachObject((obj) => {
|
|
2244
|
+
const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;
|
|
2245
|
+
if (base !== void 0 && base !== 0 && obj.data) {
|
|
2246
|
+
savedData.set(obj, obj.data);
|
|
2247
|
+
obj.data = {
|
|
2248
|
+
...obj.data,
|
|
2249
|
+
strokeWidthBase: base
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
return () => savedData.forEach((originalData, obj) => {
|
|
2254
|
+
obj.data = originalData;
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
function serializeCanvas(canvas, options) {
|
|
2258
|
+
const properties = [
|
|
2259
|
+
"data",
|
|
2260
|
+
"shapeType",
|
|
2261
|
+
// Control styling — absent from Fabric's default toObject output
|
|
2262
|
+
"borderColor",
|
|
2263
|
+
"cornerColor",
|
|
2264
|
+
"cornerStrokeColor",
|
|
2265
|
+
"transparentCorners",
|
|
2266
|
+
// Interaction locks — absent from Fabric's default toObject output
|
|
2267
|
+
"lockRotation",
|
|
2268
|
+
"lockUniScaling",
|
|
2269
|
+
...options?.properties ?? []
|
|
2270
|
+
];
|
|
2271
|
+
const restoreStrokeWidths = prepareStrokeWidths(canvas);
|
|
2272
|
+
const restoreBorderRadii = prepareBorderRadii(canvas);
|
|
2273
|
+
const restoreOrigins = prepareObjectOrigins(canvas);
|
|
2274
|
+
const restoreBgOrigin = prepareBackgroundOrigin(canvas);
|
|
2275
|
+
const restoreData = prepareStrokeWidthBaseData(canvas);
|
|
2276
|
+
const json = canvas.toObject(properties);
|
|
2277
|
+
delete json.backgroundColor;
|
|
2278
|
+
json.backgroundFilters = {
|
|
2279
|
+
opacity: getBackgroundContrast(canvas),
|
|
2280
|
+
inverted: getBackgroundInverted(canvas)
|
|
2281
|
+
};
|
|
2282
|
+
if (canvas.lockLightMode !== void 0) {
|
|
2283
|
+
json.lockLightMode = canvas.lockLightMode;
|
|
2284
|
+
}
|
|
2285
|
+
restoreStrokeWidths();
|
|
2286
|
+
restoreBorderRadii();
|
|
2287
|
+
restoreOrigins();
|
|
2288
|
+
restoreBgOrigin();
|
|
2289
|
+
restoreData();
|
|
2203
2290
|
return json;
|
|
2204
2291
|
}
|
|
2205
2292
|
async function loadCanvas(canvas, json, options) {
|
|
2206
2293
|
await canvas.loadFromJSON(json);
|
|
2207
2294
|
canvas.backgroundColor = "";
|
|
2208
2295
|
delete canvas.backgroundFilters;
|
|
2296
|
+
const rawCanvas = canvas;
|
|
2297
|
+
if (rawCanvas.lockLightMode !== void 0) {
|
|
2298
|
+
canvas.lockLightMode = rawCanvas.lockLightMode;
|
|
2299
|
+
}
|
|
2209
2300
|
const bg = canvas.backgroundImage;
|
|
2210
2301
|
if (bg instanceof FabricImage2) {
|
|
2211
2302
|
if (bg.originX !== "center" || bg.originY !== "center") {
|
|
@@ -2398,105 +2489,104 @@ function useEditCanvas(options) {
|
|
|
2398
2489
|
(canvas) => {
|
|
2399
2490
|
canvasRef.current = canvas;
|
|
2400
2491
|
const opts = optionsRef.current;
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
)
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2426
|
-
);
|
|
2427
|
-
}
|
|
2428
|
-
if (opts?.rotationSnap !== false) {
|
|
2429
|
-
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2430
|
-
canvas,
|
|
2431
|
-
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2492
|
+
setupFeatures();
|
|
2493
|
+
setupEventListeners();
|
|
2494
|
+
invokeOnReady();
|
|
2495
|
+
function setupFeatures() {
|
|
2496
|
+
if (opts?.scaledStrokes !== false) {
|
|
2497
|
+
enableScaledStrokes(canvas);
|
|
2498
|
+
}
|
|
2499
|
+
if (opts?.borderRadius !== false) {
|
|
2500
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2501
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2502
|
+
}
|
|
2503
|
+
if (opts?.keyboardShortcuts !== false) {
|
|
2504
|
+
keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
|
|
2505
|
+
}
|
|
2506
|
+
setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
|
|
2507
|
+
if (opts?.panAndZoom !== false) {
|
|
2508
|
+
viewportRef.current = enablePanAndZoom(
|
|
2509
|
+
canvas,
|
|
2510
|
+
typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
const alignmentEnabled = resolveAlignmentEnabled(
|
|
2514
|
+
opts?.enableAlignment,
|
|
2515
|
+
opts?.alignment
|
|
2432
2516
|
);
|
|
2517
|
+
if (alignmentEnabled) {
|
|
2518
|
+
alignmentCleanupRef.current = enableObjectAlignment(
|
|
2519
|
+
canvas,
|
|
2520
|
+
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2521
|
+
);
|
|
2522
|
+
}
|
|
2523
|
+
if (opts?.rotationSnap !== false) {
|
|
2524
|
+
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2525
|
+
canvas,
|
|
2526
|
+
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2527
|
+
);
|
|
2528
|
+
}
|
|
2529
|
+
if (opts?.history) {
|
|
2530
|
+
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2531
|
+
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2532
|
+
}
|
|
2433
2533
|
}
|
|
2434
|
-
|
|
2435
|
-
setZoom(canvas.getZoom());
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
}
|
|
2478
|
-
});
|
|
2479
|
-
}
|
|
2480
|
-
if (opts?.history) {
|
|
2481
|
-
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2482
|
-
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2534
|
+
function setupEventListeners() {
|
|
2535
|
+
canvas.on("mouse:wheel", () => setZoom(canvas.getZoom()));
|
|
2536
|
+
canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
|
|
2537
|
+
canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
|
|
2538
|
+
canvas.on("selection:cleared", () => setSelected([]));
|
|
2539
|
+
if (opts?.trackChanges) {
|
|
2540
|
+
canvas.on("object:added", () => setIsDirty(true));
|
|
2541
|
+
canvas.on("object:removed", () => setIsDirty(true));
|
|
2542
|
+
canvas.on("object:modified", () => setIsDirty(true));
|
|
2543
|
+
}
|
|
2544
|
+
if (opts?.history) {
|
|
2545
|
+
const syncHistoryState = () => {
|
|
2546
|
+
const h = historyRef.current;
|
|
2547
|
+
if (!h) return;
|
|
2548
|
+
setTimeout(() => {
|
|
2549
|
+
setCanUndo(h.canUndo());
|
|
2550
|
+
setCanRedo(h.canRedo());
|
|
2551
|
+
}, 350);
|
|
2552
|
+
};
|
|
2553
|
+
canvas.on("object:added", syncHistoryState);
|
|
2554
|
+
canvas.on("object:removed", syncHistoryState);
|
|
2555
|
+
canvas.on("object:modified", syncHistoryState);
|
|
2556
|
+
}
|
|
2557
|
+
if (opts?.vertexEdit !== false) {
|
|
2558
|
+
const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
|
|
2559
|
+
canvas.on("mouse:dblclick", (e) => {
|
|
2560
|
+
if (e.target && e.target instanceof Polygon4) {
|
|
2561
|
+
vertexEditCleanupRef.current?.();
|
|
2562
|
+
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2563
|
+
canvas,
|
|
2564
|
+
e.target,
|
|
2565
|
+
{
|
|
2566
|
+
...vertexOpts,
|
|
2567
|
+
onExit: () => {
|
|
2568
|
+
vertexEditCleanupRef.current = null;
|
|
2569
|
+
setIsEditingVertices(false);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
);
|
|
2573
|
+
setIsEditingVertices(true);
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2483
2577
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2578
|
+
function invokeOnReady() {
|
|
2579
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2486
2580
|
Promise.resolve(onReadyResult).then(() => {
|
|
2487
|
-
if (canvas.backgroundImage) {
|
|
2581
|
+
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2488
2582
|
fitViewportToBackground(canvas);
|
|
2489
2583
|
syncZoom(canvasRef, setZoom);
|
|
2490
2584
|
}
|
|
2491
2585
|
historyRef.current?.pushSnapshot();
|
|
2492
2586
|
});
|
|
2493
|
-
} else {
|
|
2494
|
-
Promise.resolve(onReadyResult).then(() => {
|
|
2495
|
-
historyRef.current?.pushSnapshot();
|
|
2496
|
-
});
|
|
2497
2587
|
}
|
|
2498
2588
|
},
|
|
2499
|
-
//
|
|
2589
|
+
// Dependency array intentionally empty — we only initialize once on mount
|
|
2500
2590
|
[]
|
|
2501
2591
|
);
|
|
2502
2592
|
useEffect2(() => {
|