@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/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, setBackgroundOpacity, getBackgroundOpacity, setBackgroundInverted, getBackgroundInverted, resizeImageUrl, setBackgroundImage, } from './background';
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAGvD,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,2BAA2B,EAC3B,UAAU,EACV,SAAS,GACV,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEpE,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EACV,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAG3E,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,UAAU,CAAC;AAKlB,OAAO,EACL,MAAM,IAAI,YAAY,EACtB,YAAY,EACZ,WAAW,EACX,IAAI,EACJ,OAAO,EACP,KAAK,EACL,IAAI,GACL,MAAM,QAAQ,CAAC"}
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 = 800,
33
- height = 600,
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
- if (!el) {
42
- return;
43
- }
44
- const fabricCanvas = new FabricCanvas(el, { width, height });
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
- return /* @__PURE__ */ jsx("div", { className, style, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef }) });
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 { Point } from "fabric";
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 = 0.2;
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(step = DEFAULT_ZOOM_STEP) {
249
- const z = Math.min(canvas.getZoom() + step, bounds.maxZoom);
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(step = DEFAULT_ZOOM_STEP) {
256
- const z = Math.max(canvas.getZoom() - step, bounds.minZoom);
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 setBackgroundOpacity(canvas, opacity) {
386
+ function setBackgroundContrast(canvas, value) {
303
387
  const bg = getBackgroundImage(canvas);
304
388
  if (!bg) return;
305
- bg.set("opacity", Math.min(1, Math.max(0, opacity)));
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 getBackgroundOpacity(canvas) {
410
+ function getBackgroundContrast(canvas) {
309
411
  const bg = getBackgroundImage(canvas);
310
- return bg?.opacity ?? 1;
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 prevOpacity = options?.preserveOpacity ? getBackgroundOpacity(canvas) : void 0;
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 (prevOpacity !== void 0 && prevOpacity !== 1) {
385
- img.set("opacity", prevOpacity);
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
- return { resetViewport: resetViewport2, zoomIn, zoomOut };
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
- return () => {
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?.preserveOpacity ? { preserveOpacity: true } : void 0;
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 `{ preserveOpacity: true }` to keep the current background opacity
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
- getBackgroundOpacity,
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
- useViewCanvas
2762
+ useObjectOverlay,
2763
+ useViewCanvas,
2764
+ util5 as util
2552
2765
  };
2553
2766
  //# sourceMappingURL=index.js.map