@bwp-web/canvas 0.4.2 → 0.5.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 +11 -1
- package/dist/Canvas/Canvas.d.ts.map +1 -1
- package/dist/background.d.ts +21 -8
- 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/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/shared.d.ts +3 -2
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts +4 -2
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useObjectOverlay.d.ts +49 -0
- package/dist/hooks/useObjectOverlay.d.ts.map +1 -0
- package/dist/hooks/useViewCanvas.d.ts +3 -1
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +253 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +246 -33
- package/dist/index.js.map +1 -1
- package/dist/interactions/dragToCreate.d.ts +2 -0
- package/dist/interactions/dragToCreate.d.ts.map +1 -1
- package/dist/interactions/drawToCreate.d.ts +7 -1
- package/dist/interactions/drawToCreate.d.ts.map +1 -1
- package/dist/serialization.d.ts +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/viewport.d.ts +18 -7
- package/dist/viewport.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export { useCanvasTooltip } from './hooks';
|
|
|
11
11
|
export type { UseCanvasTooltipOptions, CanvasTooltipState } from './hooks';
|
|
12
12
|
export { useCanvasClick } from './hooks';
|
|
13
13
|
export type { UseCanvasClickOptions } from './hooks';
|
|
14
|
+
export { useObjectOverlay } from './hooks';
|
|
15
|
+
export type { UseObjectOverlayOptions } from './hooks';
|
|
14
16
|
export type { Point2D, ShapeStyleOptions, SnappingOptions, InteractionModeOptions, SnappableInteractionOptions, DragBounds, ModeSetup, } from './types';
|
|
15
17
|
export { createRectangle, createRectangleAtPoint, editRectangle, } from './shapes';
|
|
16
18
|
export type { RectangleOptions, RectangleAtPointOptions } from './shapes';
|
|
@@ -26,7 +28,7 @@ export type { DrawToCreateOptions } from './interactions';
|
|
|
26
28
|
export { enableVertexEdit } from './interactions';
|
|
27
29
|
export type { VertexEditOptions } from './interactions';
|
|
28
30
|
export { enablePanAndZoom, resetViewport } from './viewport';
|
|
29
|
-
export type { ViewportController, ViewportMode, PanAndZoomOptions, } from './viewport';
|
|
31
|
+
export type { ViewportController, ViewportMode, PanAndZoomOptions, PanToObjectOptions, } from './viewport';
|
|
30
32
|
export { getSnapPoints, registerSnapPointExtractor } from './alignment';
|
|
31
33
|
export type { SnapPointExtractor } from './alignment';
|
|
32
34
|
export { enableObjectAlignment } from './alignment';
|
|
@@ -38,8 +40,8 @@ export type { CursorSnapResult, CursorSnapOptions, GuidelineStyle, } from './ali
|
|
|
38
40
|
export { deleteObjects, enableKeyboardShortcuts } from './keyboard';
|
|
39
41
|
export { enableScaledStrokes, serializeCanvas, loadCanvas, getBaseStrokeWidth, } from './serialization';
|
|
40
42
|
export type { SerializeOptions, LoadCanvasOptions } from './serialization';
|
|
41
|
-
export { fitViewportToBackground,
|
|
43
|
+
export { fitViewportToBackground, getBackgroundSrc, setBackgroundContrast, getBackgroundContrast, setBackgroundInverted, getBackgroundInverted, resizeImageUrl, setBackgroundImage, } from './background';
|
|
42
44
|
export type { FitViewportOptions, ResizeResult, ResizeImageOptions, SetBackgroundImageOptions, } from './background';
|
|
43
45
|
export { DEFAULT_CONTROL_STYLE, DEFAULT_SHAPE_STYLE, DEFAULT_CIRCLE_STYLE, DEFAULT_DRAG_SHAPE_STYLE, DEFAULT_GUIDELINE_SHAPE_STYLE, DEFAULT_ALIGNMENT_STYLE, } from './styles';
|
|
44
|
-
export { Canvas as FabricCanvas, FabricObject, FabricImage, Rect, Polygon, } from 'fabric';
|
|
46
|
+
export { Canvas as FabricCanvas, FabricObject, FabricImage, Rect, Polygon, Point, util, } from 'fabric';
|
|
45
47
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,YAAY,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -29,27 +29,56 @@ function enableKeyboardShortcuts(canvas) {
|
|
|
29
29
|
// src/Canvas/Canvas.tsx
|
|
30
30
|
import { jsx } from "react/jsx-runtime";
|
|
31
31
|
function Canvas({
|
|
32
|
-
width
|
|
33
|
-
height
|
|
32
|
+
width,
|
|
33
|
+
height,
|
|
34
34
|
className,
|
|
35
35
|
style,
|
|
36
36
|
onReady
|
|
37
37
|
}) {
|
|
38
38
|
const canvasRef = useRef(null);
|
|
39
|
+
const wrapperRef = useRef(null);
|
|
40
|
+
const isFixedSize = width !== void 0 && height !== void 0;
|
|
39
41
|
useEffect(() => {
|
|
40
42
|
const el = canvasRef.current;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
43
|
+
const wrapper = wrapperRef.current;
|
|
44
|
+
if (!el || !wrapper) return;
|
|
45
|
+
const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;
|
|
46
|
+
const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;
|
|
47
|
+
const fabricCanvas = new FabricCanvas(el, {
|
|
48
|
+
width: initialWidth,
|
|
49
|
+
height: initialHeight
|
|
50
|
+
});
|
|
45
51
|
onReady?.(fabricCanvas);
|
|
46
52
|
const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);
|
|
53
|
+
let observer;
|
|
54
|
+
let rafId = 0;
|
|
55
|
+
if (!isFixedSize) {
|
|
56
|
+
let currentWidth = initialWidth;
|
|
57
|
+
let currentHeight = initialHeight;
|
|
58
|
+
observer = new ResizeObserver((entries) => {
|
|
59
|
+
cancelAnimationFrame(rafId);
|
|
60
|
+
rafId = requestAnimationFrame(() => {
|
|
61
|
+
const entry = entries[0];
|
|
62
|
+
if (!entry) return;
|
|
63
|
+
const { width: newWidth, height: newHeight } = entry.contentRect;
|
|
64
|
+
if (newWidth > 0 && newHeight > 0 && (newWidth !== currentWidth || newHeight !== currentHeight)) {
|
|
65
|
+
currentWidth = newWidth;
|
|
66
|
+
currentHeight = newHeight;
|
|
67
|
+
fabricCanvas.setDimensions({ width: newWidth, height: newHeight });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
observer.observe(wrapper);
|
|
72
|
+
}
|
|
47
73
|
return () => {
|
|
74
|
+
cancelAnimationFrame(rafId);
|
|
75
|
+
observer?.disconnect();
|
|
48
76
|
cleanupShortcuts();
|
|
49
77
|
fabricCanvas.dispose();
|
|
50
78
|
};
|
|
51
79
|
}, []);
|
|
52
|
-
|
|
80
|
+
const wrapperStyle = isFixedSize ? { ...style } : { width: "100%", height: "100%", ...style };
|
|
81
|
+
return /* @__PURE__ */ jsx("div", { ref: wrapperRef, className, style: wrapperStyle, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef }) });
|
|
53
82
|
}
|
|
54
83
|
|
|
55
84
|
// src/hooks/useEditCanvas.ts
|
|
@@ -57,13 +86,15 @@ import { useCallback, useEffect as useEffect2, useRef as useRef2, useState } fro
|
|
|
57
86
|
import { Polygon as Polygon4 } from "fabric";
|
|
58
87
|
|
|
59
88
|
// src/viewport.ts
|
|
60
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
Point
|
|
91
|
+
} from "fabric";
|
|
61
92
|
|
|
62
93
|
// src/constants.ts
|
|
63
94
|
var DEFAULT_MIN_ZOOM = 0.2;
|
|
64
95
|
var DEFAULT_MAX_ZOOM = 10;
|
|
65
96
|
var DEFAULT_ZOOM_FACTOR = 1.03;
|
|
66
|
-
var DEFAULT_ZOOM_STEP =
|
|
97
|
+
var DEFAULT_ZOOM_STEP = 1.2;
|
|
67
98
|
var DEFAULT_VIEWPORT_PADDING = 0.05;
|
|
68
99
|
var BASE_CANVAS_SIZE = 1e3;
|
|
69
100
|
var DEFAULT_SNAP_MARGIN = 6;
|
|
@@ -245,20 +276,68 @@ function enablePanAndZoom(canvas, options) {
|
|
|
245
276
|
setEnabled(value) {
|
|
246
277
|
enabled = value;
|
|
247
278
|
},
|
|
248
|
-
zoomIn(
|
|
249
|
-
const z = Math.min(canvas.getZoom()
|
|
279
|
+
zoomIn(factor = DEFAULT_ZOOM_STEP) {
|
|
280
|
+
const z = Math.min(canvas.getZoom() * factor, bounds.maxZoom);
|
|
250
281
|
canvas.zoomToPoint(
|
|
251
282
|
new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),
|
|
252
283
|
z
|
|
253
284
|
);
|
|
254
285
|
},
|
|
255
|
-
zoomOut(
|
|
256
|
-
const z = Math.max(canvas.getZoom()
|
|
286
|
+
zoomOut(factor = DEFAULT_ZOOM_STEP) {
|
|
287
|
+
const z = Math.max(canvas.getZoom() / factor, bounds.minZoom);
|
|
257
288
|
canvas.zoomToPoint(
|
|
258
289
|
new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),
|
|
259
290
|
z
|
|
260
291
|
);
|
|
261
292
|
},
|
|
293
|
+
panToObject(object, panOpts) {
|
|
294
|
+
const zoom = canvas.getZoom();
|
|
295
|
+
const objectCenter = object.getCenterPoint();
|
|
296
|
+
const canvasCenterX = canvas.getWidth() / 2;
|
|
297
|
+
const canvasCenterY = canvas.getHeight() / 2;
|
|
298
|
+
const targetX = canvasCenterX - objectCenter.x * zoom;
|
|
299
|
+
const targetY = canvasCenterY - objectCenter.y * zoom;
|
|
300
|
+
if (!panOpts?.animate) {
|
|
301
|
+
const vt2 = canvas.viewportTransform;
|
|
302
|
+
if (!vt2) return;
|
|
303
|
+
canvas.setViewportTransform([
|
|
304
|
+
vt2[0],
|
|
305
|
+
vt2[1],
|
|
306
|
+
vt2[2],
|
|
307
|
+
vt2[3],
|
|
308
|
+
targetX,
|
|
309
|
+
targetY
|
|
310
|
+
]);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const duration = panOpts.duration ?? 300;
|
|
314
|
+
const vt = canvas.viewportTransform;
|
|
315
|
+
if (!vt) return;
|
|
316
|
+
const startX = vt[4];
|
|
317
|
+
const startY = vt[5];
|
|
318
|
+
const startTime = performance.now();
|
|
319
|
+
function step(now) {
|
|
320
|
+
const elapsed = now - startTime;
|
|
321
|
+
const t = Math.min(elapsed / duration, 1);
|
|
322
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
323
|
+
const currentX = startX + (targetX - startX) * eased;
|
|
324
|
+
const currentY = startY + (targetY - startY) * eased;
|
|
325
|
+
const currentVt = canvas.viewportTransform;
|
|
326
|
+
if (!currentVt) return;
|
|
327
|
+
canvas.setViewportTransform([
|
|
328
|
+
currentVt[0],
|
|
329
|
+
currentVt[1],
|
|
330
|
+
currentVt[2],
|
|
331
|
+
currentVt[3],
|
|
332
|
+
currentX,
|
|
333
|
+
currentY
|
|
334
|
+
]);
|
|
335
|
+
if (t < 1) {
|
|
336
|
+
requestAnimationFrame(step);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
requestAnimationFrame(step);
|
|
340
|
+
},
|
|
262
341
|
cleanup() {
|
|
263
342
|
canvas.off("mouse:wheel", handleWheel);
|
|
264
343
|
canvas.off("mouse:down", panHandlers.handleMouseDown);
|
|
@@ -277,6 +356,11 @@ import { FabricImage, filters } from "fabric";
|
|
|
277
356
|
function getBackgroundImage(canvas) {
|
|
278
357
|
return canvas.backgroundImage;
|
|
279
358
|
}
|
|
359
|
+
function getBackgroundSrc(canvas) {
|
|
360
|
+
const bg = getBackgroundImage(canvas);
|
|
361
|
+
if (!bg) return null;
|
|
362
|
+
return bg.getSrc() || null;
|
|
363
|
+
}
|
|
280
364
|
function fitViewportToBackground(canvas, options) {
|
|
281
365
|
const bg = getBackgroundImage(canvas);
|
|
282
366
|
if (!bg) return;
|
|
@@ -299,15 +383,37 @@ function fitViewportToBackground(canvas, options) {
|
|
|
299
383
|
canvas.setViewportTransform([scale, 0, 0, scale, offsetX, offsetY]);
|
|
300
384
|
canvas.requestRenderAll();
|
|
301
385
|
}
|
|
302
|
-
function
|
|
386
|
+
function setBackgroundContrast(canvas, value) {
|
|
303
387
|
const bg = getBackgroundImage(canvas);
|
|
304
388
|
if (!bg) return;
|
|
305
|
-
|
|
389
|
+
const contrast = Math.min(Math.max(value, 0), 2) - 1;
|
|
390
|
+
const currentFilters = bg.filters ?? [];
|
|
391
|
+
const contrastIdx = currentFilters.findIndex(
|
|
392
|
+
(f) => f instanceof filters.Contrast
|
|
393
|
+
);
|
|
394
|
+
if (contrast === 0) {
|
|
395
|
+
if (contrastIdx >= 0) {
|
|
396
|
+
bg.filters = currentFilters.filter(
|
|
397
|
+
(f) => !(f instanceof filters.Contrast)
|
|
398
|
+
);
|
|
399
|
+
bg.applyFilters();
|
|
400
|
+
}
|
|
401
|
+
} else if (contrastIdx >= 0) {
|
|
402
|
+
currentFilters[contrastIdx].contrast = contrast;
|
|
403
|
+
bg.applyFilters();
|
|
404
|
+
} else {
|
|
405
|
+
bg.filters = [...currentFilters, new filters.Contrast({ contrast })];
|
|
406
|
+
bg.applyFilters();
|
|
407
|
+
}
|
|
306
408
|
canvas.requestRenderAll();
|
|
307
409
|
}
|
|
308
|
-
function
|
|
410
|
+
function getBackgroundContrast(canvas) {
|
|
309
411
|
const bg = getBackgroundImage(canvas);
|
|
310
|
-
|
|
412
|
+
if (!bg) return 1;
|
|
413
|
+
const contrastFilter = bg.filters?.find(
|
|
414
|
+
(f) => f instanceof filters.Contrast
|
|
415
|
+
);
|
|
416
|
+
return 1 + (contrastFilter?.contrast ?? 0);
|
|
311
417
|
}
|
|
312
418
|
function setBackgroundInverted(canvas, inverted) {
|
|
313
419
|
const bg = getBackgroundImage(canvas);
|
|
@@ -373,7 +479,7 @@ function resizeImageUrl(url, options) {
|
|
|
373
479
|
});
|
|
374
480
|
}
|
|
375
481
|
async function setBackgroundImage(canvas, url, options) {
|
|
376
|
-
const
|
|
482
|
+
const prevContrast = options?.preserveContrast ? getBackgroundContrast(canvas) : void 0;
|
|
377
483
|
let imageUrl = url;
|
|
378
484
|
if (options !== void 0) {
|
|
379
485
|
const result = await resizeImageUrl(url, options);
|
|
@@ -381,8 +487,8 @@ async function setBackgroundImage(canvas, url, options) {
|
|
|
381
487
|
}
|
|
382
488
|
const img = await FabricImage.fromURL(imageUrl, { crossOrigin: "anonymous" });
|
|
383
489
|
canvas.backgroundImage = img;
|
|
384
|
-
if (
|
|
385
|
-
|
|
490
|
+
if (prevContrast !== void 0 && prevContrast !== 1) {
|
|
491
|
+
setBackgroundContrast(canvas, prevContrast);
|
|
386
492
|
}
|
|
387
493
|
canvas.requestRenderAll();
|
|
388
494
|
return img;
|
|
@@ -412,7 +518,10 @@ function createViewportActions(canvasRef, viewportRef, setZoom) {
|
|
|
412
518
|
viewportRef.current?.zoomOut(step);
|
|
413
519
|
syncZoom(canvasRef, setZoom);
|
|
414
520
|
};
|
|
415
|
-
|
|
521
|
+
const panToObject = (object, panOpts) => {
|
|
522
|
+
viewportRef.current?.panToObject(object, panOpts);
|
|
523
|
+
};
|
|
524
|
+
return { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject };
|
|
416
525
|
}
|
|
417
526
|
function resolveAlignmentEnabled(enableAlignment, alignmentProp) {
|
|
418
527
|
if (enableAlignment !== void 0) return enableAlignment;
|
|
@@ -1308,21 +1417,39 @@ function enableDragToCreate(canvas, factory, options) {
|
|
|
1308
1417
|
options?.onCreated?.(obj);
|
|
1309
1418
|
previewRect = null;
|
|
1310
1419
|
};
|
|
1420
|
+
const handleKeyDown = (e) => {
|
|
1421
|
+
if (e.key === "Escape") {
|
|
1422
|
+
e.stopImmediatePropagation();
|
|
1423
|
+
e.preventDefault();
|
|
1424
|
+
cleanup("cancel");
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1311
1427
|
canvas.on("mouse:down", handleMouseDown);
|
|
1312
1428
|
canvas.on("mouse:move", handleMouseMove);
|
|
1313
1429
|
canvas.on("mouse:up", handleMouseUp);
|
|
1314
|
-
|
|
1430
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
1431
|
+
let exited = false;
|
|
1432
|
+
function cleanup(reason) {
|
|
1433
|
+
if (exited) return;
|
|
1434
|
+
exited = true;
|
|
1315
1435
|
canvas.off("mouse:down", handleMouseDown);
|
|
1316
1436
|
canvas.off("mouse:move", handleMouseMove);
|
|
1317
1437
|
canvas.off("mouse:up", handleMouseUp);
|
|
1438
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
1318
1439
|
shiftTracker.cleanup();
|
|
1319
1440
|
snapping.cleanup();
|
|
1320
1441
|
if (isDrawing && previewRect) {
|
|
1442
|
+
snapping.excludeSet.delete(previewRect);
|
|
1321
1443
|
canvas.remove(previewRect);
|
|
1444
|
+
canvas.selection = previousSelection;
|
|
1322
1445
|
canvas.requestRenderAll();
|
|
1323
1446
|
}
|
|
1324
1447
|
restoreViewport(options?.viewport);
|
|
1325
|
-
|
|
1448
|
+
if (reason === "cancel") {
|
|
1449
|
+
options?.onCancel?.();
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return () => cleanup();
|
|
1326
1453
|
}
|
|
1327
1454
|
|
|
1328
1455
|
// src/interactions/drawToCreate.ts
|
|
@@ -1602,6 +1729,9 @@ function enableDrawToCreate(canvas, options) {
|
|
|
1602
1729
|
removePreviewElements();
|
|
1603
1730
|
snapping.clearSnapResult();
|
|
1604
1731
|
const polygon = createPolygonFromVertices(canvas, points, options?.style);
|
|
1732
|
+
if (options?.data) {
|
|
1733
|
+
polygon.data = options.data;
|
|
1734
|
+
}
|
|
1605
1735
|
canvas.selection = previousSelection;
|
|
1606
1736
|
canvas.requestRenderAll();
|
|
1607
1737
|
restoreViewport(options?.viewport);
|
|
@@ -1975,6 +2105,7 @@ function serializeCanvas(canvas, options) {
|
|
|
1975
2105
|
}
|
|
1976
2106
|
});
|
|
1977
2107
|
const json = canvas.toObject(properties);
|
|
2108
|
+
delete json.backgroundColor;
|
|
1978
2109
|
scaledWidths.forEach((scaled, obj) => {
|
|
1979
2110
|
obj.strokeWidth = scaled;
|
|
1980
2111
|
});
|
|
@@ -1989,6 +2120,17 @@ async function loadCanvas(canvas, json, options) {
|
|
|
1989
2120
|
});
|
|
1990
2121
|
for (const obj of toRemove) canvas.remove(obj);
|
|
1991
2122
|
}
|
|
2123
|
+
canvas.forEachObject((obj) => {
|
|
2124
|
+
if (obj.originX === "center" && obj.originY === "center") return;
|
|
2125
|
+
const center = obj.getCenterPoint();
|
|
2126
|
+
obj.set({
|
|
2127
|
+
originX: "center",
|
|
2128
|
+
originY: "center",
|
|
2129
|
+
left: center.x,
|
|
2130
|
+
top: center.y
|
|
2131
|
+
});
|
|
2132
|
+
obj.setCoords();
|
|
2133
|
+
});
|
|
1992
2134
|
canvas.forEachObject((obj) => {
|
|
1993
2135
|
obj.set(DEFAULT_CONTROL_STYLE);
|
|
1994
2136
|
if (obj.shapeType === "circle" && obj instanceof Rect5) {
|
|
@@ -1996,6 +2138,7 @@ async function loadCanvas(canvas, json, options) {
|
|
|
1996
2138
|
}
|
|
1997
2139
|
});
|
|
1998
2140
|
canvas.requestRenderAll();
|
|
2141
|
+
return canvas.getObjects();
|
|
1999
2142
|
}
|
|
2000
2143
|
|
|
2001
2144
|
// src/hooks/useEditCanvas.ts
|
|
@@ -2141,7 +2284,7 @@ function useEditCanvas(options) {
|
|
|
2141
2284
|
viewportRef.current?.setMode(mode);
|
|
2142
2285
|
setViewportModeState(mode);
|
|
2143
2286
|
}, []);
|
|
2144
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut } = createViewportActions(
|
|
2287
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
|
|
2145
2288
|
canvasRef,
|
|
2146
2289
|
viewportRef,
|
|
2147
2290
|
setZoom
|
|
@@ -2150,7 +2293,7 @@ function useEditCanvas(options) {
|
|
|
2150
2293
|
async (url, bgOpts) => {
|
|
2151
2294
|
const canvas = canvasRef.current;
|
|
2152
2295
|
if (!canvas) throw new Error("Canvas not ready");
|
|
2153
|
-
const resizeOpts = options?.backgroundResize !== false ? typeof options?.backgroundResize === "object" ? { ...options.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.
|
|
2296
|
+
const resizeOpts = options?.backgroundResize !== false ? typeof options?.backgroundResize === "object" ? { ...options.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2154
2297
|
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2155
2298
|
if (options?.autoFitToBackground !== false) {
|
|
2156
2299
|
fitViewportToBackground(canvas);
|
|
@@ -2181,7 +2324,9 @@ function useEditCanvas(options) {
|
|
|
2181
2324
|
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2182
2325
|
zoomIn,
|
|
2183
2326
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2184
|
-
zoomOut
|
|
2327
|
+
zoomOut,
|
|
2328
|
+
/** Pan the viewport to center on a specific object. */
|
|
2329
|
+
panToObject
|
|
2185
2330
|
},
|
|
2186
2331
|
/** Whether vertex edit mode is currently active (reactive). */
|
|
2187
2332
|
isEditingVertices,
|
|
@@ -2206,7 +2351,7 @@ function useEditCanvas(options) {
|
|
|
2206
2351
|
* exceeds the configured limits (opt out via `backgroundResize: false`),
|
|
2207
2352
|
* and fits the viewport after setting if `autoFitToBackground` is enabled.
|
|
2208
2353
|
*
|
|
2209
|
-
* Pass `{
|
|
2354
|
+
* Pass `{ preserveContrast: true }` to keep the current background contrast
|
|
2210
2355
|
* when replacing the image.
|
|
2211
2356
|
*/
|
|
2212
2357
|
setBackground,
|
|
@@ -2271,7 +2416,7 @@ function useViewCanvas(options) {
|
|
|
2271
2416
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2272
2417
|
[]
|
|
2273
2418
|
);
|
|
2274
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut } = createViewportActions(
|
|
2419
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
|
|
2275
2420
|
canvasRef,
|
|
2276
2421
|
viewportRef,
|
|
2277
2422
|
setZoom
|
|
@@ -2337,7 +2482,9 @@ function useViewCanvas(options) {
|
|
|
2337
2482
|
/** Zoom in toward the canvas center. Default step: 0.2. */
|
|
2338
2483
|
zoomIn,
|
|
2339
2484
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2340
|
-
zoomOut
|
|
2485
|
+
zoomOut,
|
|
2486
|
+
/** Pan the viewport to center on a specific object. */
|
|
2487
|
+
panToObject
|
|
2341
2488
|
},
|
|
2342
2489
|
/** Update a single object's visual style by its `data.id`. */
|
|
2343
2490
|
setObjectStyle,
|
|
@@ -2488,13 +2635,75 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
2488
2635
|
}, [canvasRef]);
|
|
2489
2636
|
}
|
|
2490
2637
|
|
|
2638
|
+
// src/hooks/useObjectOverlay.ts
|
|
2639
|
+
import { useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
2640
|
+
import { util as util4 } from "fabric";
|
|
2641
|
+
function useObjectOverlay(canvasRef, object, options) {
|
|
2642
|
+
const containerRef = useRef7(null);
|
|
2643
|
+
const optionsRef = useRef7(options);
|
|
2644
|
+
optionsRef.current = options;
|
|
2645
|
+
useEffect6(() => {
|
|
2646
|
+
const canvas = canvasRef.current;
|
|
2647
|
+
if (!canvas || !object) return;
|
|
2648
|
+
function update() {
|
|
2649
|
+
const el = containerRef.current;
|
|
2650
|
+
if (!el || !canvas || !object) return;
|
|
2651
|
+
const zoom = canvas.getZoom();
|
|
2652
|
+
const vt = canvas.viewportTransform;
|
|
2653
|
+
if (!vt) return;
|
|
2654
|
+
const center = object.getCenterPoint();
|
|
2655
|
+
const actualWidth = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
2656
|
+
const actualHeight = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
2657
|
+
const screenCoords = util4.transformPoint(center, vt);
|
|
2658
|
+
const screenWidth = actualWidth * zoom;
|
|
2659
|
+
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
|
+
const angle = object.angle ?? 0;
|
|
2665
|
+
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2666
|
+
const opts = optionsRef.current;
|
|
2667
|
+
if (opts?.autoScaleContent) {
|
|
2668
|
+
const contentScale = Math.min(screenWidth, screenHeight) / 100;
|
|
2669
|
+
const clampedScale = Math.max(0.1, Math.min(contentScale, 2));
|
|
2670
|
+
el.style.setProperty("--overlay-scale", String(clampedScale));
|
|
2671
|
+
if (opts.textSelector) {
|
|
2672
|
+
const textMinScale = opts.textMinScale ?? 0.5;
|
|
2673
|
+
const textEls = el.querySelectorAll(opts.textSelector);
|
|
2674
|
+
const display = clampedScale < textMinScale ? "none" : "";
|
|
2675
|
+
textEls.forEach((t) => {
|
|
2676
|
+
t.style.display = display;
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
update();
|
|
2682
|
+
canvas.on("after:render", update);
|
|
2683
|
+
canvas.on("mouse:wheel", update);
|
|
2684
|
+
object.on("moving", update);
|
|
2685
|
+
object.on("scaling", update);
|
|
2686
|
+
object.on("rotating", update);
|
|
2687
|
+
return () => {
|
|
2688
|
+
canvas.off("after:render", update);
|
|
2689
|
+
canvas.off("mouse:wheel", update);
|
|
2690
|
+
object.off("moving", update);
|
|
2691
|
+
object.off("scaling", update);
|
|
2692
|
+
object.off("rotating", update);
|
|
2693
|
+
};
|
|
2694
|
+
}, [canvasRef, object]);
|
|
2695
|
+
return containerRef;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2491
2698
|
// src/index.ts
|
|
2492
2699
|
import {
|
|
2493
2700
|
Canvas as Canvas2,
|
|
2494
2701
|
FabricObject as FabricObject5,
|
|
2495
2702
|
FabricImage as FabricImage2,
|
|
2496
2703
|
Rect as Rect7,
|
|
2497
|
-
Polygon as Polygon5
|
|
2704
|
+
Polygon as Polygon5,
|
|
2705
|
+
Point as Point9,
|
|
2706
|
+
util as util5
|
|
2498
2707
|
} from "fabric";
|
|
2499
2708
|
export {
|
|
2500
2709
|
Canvas,
|
|
@@ -2507,6 +2716,7 @@ export {
|
|
|
2507
2716
|
Canvas2 as FabricCanvas,
|
|
2508
2717
|
FabricImage2 as FabricImage,
|
|
2509
2718
|
FabricObject5 as FabricObject,
|
|
2719
|
+
Point9 as Point,
|
|
2510
2720
|
Polygon5 as Polygon,
|
|
2511
2721
|
Rect7 as Rect,
|
|
2512
2722
|
createCircle,
|
|
@@ -2531,8 +2741,9 @@ export {
|
|
|
2531
2741
|
enableScaledStrokes,
|
|
2532
2742
|
enableVertexEdit,
|
|
2533
2743
|
fitViewportToBackground,
|
|
2744
|
+
getBackgroundContrast,
|
|
2534
2745
|
getBackgroundInverted,
|
|
2535
|
-
|
|
2746
|
+
getBackgroundSrc,
|
|
2536
2747
|
getBaseStrokeWidth,
|
|
2537
2748
|
getSnapPoints,
|
|
2538
2749
|
loadCanvas,
|
|
@@ -2540,14 +2751,16 @@ export {
|
|
|
2540
2751
|
resetViewport,
|
|
2541
2752
|
resizeImageUrl,
|
|
2542
2753
|
serializeCanvas,
|
|
2754
|
+
setBackgroundContrast,
|
|
2543
2755
|
setBackgroundImage,
|
|
2544
2756
|
setBackgroundInverted,
|
|
2545
|
-
setBackgroundOpacity,
|
|
2546
2757
|
snapCursorPoint,
|
|
2547
2758
|
useCanvasClick,
|
|
2548
2759
|
useCanvasEvents,
|
|
2549
2760
|
useCanvasTooltip,
|
|
2550
2761
|
useEditCanvas,
|
|
2551
|
-
|
|
2762
|
+
useObjectOverlay,
|
|
2763
|
+
useViewCanvas,
|
|
2764
|
+
util5 as util
|
|
2552
2765
|
};
|
|
2553
2766
|
//# sourceMappingURL=index.js.map
|