@deepnoid/canvas 0.1.36 → 0.1.37

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/README.md CHANGED
@@ -1,3 +1,6 @@
1
1
  # deepnoid-canvas
2
2
 
3
- 이 프로젝트는 본부 내 프로젝트에서 사용되는 공통 CANVAS 라이브러리입니다!!!
3
+ 이 프로젝트는 본부 내 프로젝트에서 사용되는 공통 CANVAS 라이브러리입니다!!!
4
+
5
+ AnnotationViewer → 전체 이미지 + UI 컨트롤 포함
6
+ AnnotationLayer → 실제 좌표/도형을 그리는 캔버스
@@ -0,0 +1,16 @@
1
+ import { Coordinate } from '../../types/coordinate';
2
+ type Props = {
3
+ moveX: number;
4
+ moveY: number;
5
+ zoomX: number;
6
+ zoomY: number;
7
+ zoom: number;
8
+ dx: number;
9
+ dy: number;
10
+ dw: number;
11
+ dh: number;
12
+ coordinates?: Coordinate[];
13
+ editable?: boolean;
14
+ };
15
+ declare const AnnotationLayer: ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }: Props) => import("react/jsx-runtime").JSX.Element;
16
+ export default AnnotationLayer;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useRef } from 'react';
3
- import { drawCanvas, drawRect, resolutionCanvas } from '../utils/graphic';
4
- const Canvas = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }) => {
3
+ import { drawCanvas, drawRect, resolutionCanvas } from '../../utils/graphic';
4
+ const AnnotationLayer = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }) => {
5
5
  const canvas = useRef(null);
6
6
  useEffect(() => {
7
7
  const redraw = () => {
@@ -32,4 +32,4 @@ const Canvas = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates
32
32
  willChange: 'transform',
33
33
  } }));
34
34
  };
35
- export default Canvas;
35
+ export default AnnotationLayer;
@@ -11,17 +11,16 @@ type CanvasState = {
11
11
  zoom: number;
12
12
  initZoom: number;
13
13
  };
14
- type UsePanZoomProps = {
14
+ type Props = {
15
15
  canvasRef: RefObject<HTMLCanvasElement | null>;
16
16
  imageRef: RefObject<HTMLImageElement>;
17
17
  panZoomable?: boolean;
18
18
  };
19
- export declare function usePanZoom({ canvasRef, imageRef, panZoomable }: UsePanZoomProps): {
19
+ export declare function useImagePanZoom({ canvasRef, imageRef, panZoomable }: Props): {
20
20
  canvasState: CanvasState;
21
21
  initZoomAndPosition: (canvas: HTMLCanvasElement, image: HTMLImageElement) => void;
22
22
  preserveZoomAndPosition: (canvas: HTMLCanvasElement, image: HTMLImageElement, zoomRatio: number) => void;
23
23
  initCanvas: () => void;
24
- clearCanvas: () => void;
25
24
  handleWheel: (event: WheelEvent) => void;
26
25
  handleMouseDown: (event: MouseEvent) => void;
27
26
  handleMouseMove: (event: MouseEvent) => void;
@@ -1,8 +1,7 @@
1
1
  'use client';
2
- import { useState } from 'react';
3
- import { getMousePointTransform, canvasCenterPoint, calculatorZoomPoint, calculateInitZoom, resolutionCanvas, } from '../utils/graphic';
4
- import { MouseStatus } from '../enum/common';
5
- export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
2
+ import { useState, useRef } from 'react';
3
+ import { getMousePointTransform, canvasCenterPoint, calculatorZoomPoint, calculateInitZoom, calculateZoom, } from '../../../utils/graphic';
4
+ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
6
5
  const ZOOM_UNIT = 0.9;
7
6
  const MAX_ZOOM = 4;
8
7
  const MIN_ZOOM = 0.5;
@@ -18,15 +17,15 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
18
17
  zoom: 0,
19
18
  initZoom: 0,
20
19
  });
21
- const [status, setStatus] = useState('');
22
20
  const [startMousePoint, setStartMousePoint] = useState();
21
+ const isDraggingRef = useRef(false);
23
22
  const initZoomAndPosition = (canvas, image) => {
24
- const point = canvasCenterPoint(canvas, image);
25
- const zoomPoint = calculatorZoomPoint(canvas, 0, 0, point.x, point.y);
23
+ const center = canvasCenterPoint(canvas, image);
24
+ const zoomPoint = calculatorZoomPoint(canvas, 0, 0, center.x, center.y);
26
25
  const initZoomValue = calculateInitZoom(canvas, image);
27
26
  setCanvasState({
28
- dx: point.x,
29
- dy: point.y,
27
+ dx: center.x,
28
+ dy: center.y,
30
29
  dw: image.width,
31
30
  dh: image.height,
32
31
  moveX: 0,
@@ -39,12 +38,12 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
39
38
  };
40
39
  const preserveZoomAndPosition = (canvas, image, zoomRatio) => {
41
40
  const prevZoomRatio = zoomRatio || 1;
42
- const point = canvasCenterPoint(canvas, image);
41
+ const center = canvasCenterPoint(canvas, image);
43
42
  const newInitZoom = calculateInitZoom(canvas, image);
44
- const zoomPoint = calculatorZoomPoint(canvas, canvasState.moveX, canvasState.moveY, point.x, point.y);
43
+ const zoomPoint = calculatorZoomPoint(canvas, canvasState.moveX, canvasState.moveY, center.x, center.y);
45
44
  setCanvasState({
46
- dx: point.x,
47
- dy: point.y,
45
+ dx: center.x,
46
+ dy: center.y,
48
47
  dw: image.width,
49
48
  dh: image.height,
50
49
  moveX: canvasState.moveX,
@@ -60,32 +59,20 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
60
59
  return;
61
60
  initZoomAndPosition(canvasRef.current, imageRef.current);
62
61
  };
63
- const clearCanvas = () => {
64
- if (!canvasRef.current)
65
- return;
66
- const ctx = canvasRef.current.getContext('2d');
67
- const canvas = resolutionCanvas(canvasRef.current);
68
- if (canvas)
69
- ctx?.clearRect(0, 0, canvas.width, canvas.height);
70
- };
71
62
  const handleWheel = (event) => {
72
63
  if (!panZoomable || !canvasRef.current || canvasState.initZoom <= 0)
73
64
  return;
74
- let calcZoom = event.deltaY < 0 ? canvasState.zoom * (1 / ZOOM_UNIT) : canvasState.zoom * ZOOM_UNIT;
75
- if (canvasState.initZoom * MAX_ZOOM < canvasState.zoom * ZOOM_UNIT)
76
- calcZoom = calcZoom * ZOOM_UNIT;
77
- if (canvasState.initZoom * MIN_ZOOM > canvasState.zoom * ZOOM_UNIT)
78
- calcZoom = calcZoom * (1 / ZOOM_UNIT);
65
+ const newZoom = calculateZoom(canvasState.zoom, event.deltaY, canvasState.initZoom * MIN_ZOOM, canvasState.initZoom * MAX_ZOOM, ZOOM_UNIT);
79
66
  const zoomPoint = calculatorZoomPoint(canvasRef.current, canvasState.moveX, canvasState.moveY, canvasState.dx, canvasState.dy);
80
67
  setCanvasState({
81
68
  ...canvasState,
82
69
  zoomX: zoomPoint.x,
83
70
  zoomY: zoomPoint.y,
84
- zoom: calcZoom,
71
+ zoom: newZoom,
85
72
  });
86
73
  };
87
74
  const handleMouseMove = (event) => {
88
- if (!panZoomable || !canvasRef.current || status !== MouseStatus.MOVE)
75
+ if (!panZoomable || !canvasRef.current || !isDraggingRef.current)
89
76
  return;
90
77
  const canvas = canvasRef.current;
91
78
  const rect = canvas.getBoundingClientRect();
@@ -111,7 +98,7 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
111
98
  const handleMouseDown = (event) => {
112
99
  if (!panZoomable || !canvasRef.current)
113
100
  return;
114
- setStatus(MouseStatus.MOVE);
101
+ isDraggingRef.current = true;
115
102
  const canvas = canvasRef.current;
116
103
  const rect = canvas.getBoundingClientRect();
117
104
  const mouseX = event.clientX - rect.left;
@@ -123,13 +110,13 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
123
110
  const handleMouseUp = (event) => {
124
111
  if (!panZoomable || !canvasRef.current)
125
112
  return;
126
- setStatus(MouseStatus.STOP);
113
+ isDraggingRef.current = false;
127
114
  event.preventDefault();
128
115
  };
129
116
  const handleMouseLeave = (event) => {
130
117
  if (!panZoomable || !canvasRef.current)
131
118
  return;
132
- setStatus(MouseStatus.STOP);
119
+ isDraggingRef.current = false;
133
120
  event.preventDefault();
134
121
  };
135
122
  return {
@@ -137,7 +124,6 @@ export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
137
124
  initZoomAndPosition,
138
125
  preserveZoomAndPosition,
139
126
  initCanvas,
140
- clearCanvas,
141
127
  handleWheel,
142
128
  handleMouseDown,
143
129
  handleMouseMove,
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode } from 'react';
2
- import { Coordinate } from '../types/coordinate';
2
+ import { Coordinate } from '../../types/coordinate';
3
3
  export type ZoomButtonType = {
4
4
  onClick: () => void;
5
5
  children: ReactNode;
@@ -18,5 +18,5 @@ type Props = {
18
18
  onImageLoadSuccess?: () => void;
19
19
  onImageLoadError?: (error: Error) => void;
20
20
  };
21
- declare const AnnotatedCanvas: ({ image, coordinates, panZoomable, ZoomButton, resetOnImageChange, editable, timeout, onImageLoadSuccess, onImageLoadError, }: Props) => import("react/jsx-runtime").JSX.Element;
22
- export default AnnotatedCanvas;
21
+ declare const AnnotationViewer: ({ image, coordinates, panZoomable, ZoomButton, resetOnImageChange, editable, timeout, onImageLoadSuccess, onImageLoadError, }: Props) => import("react/jsx-runtime").JSX.Element;
22
+ export default AnnotationViewer;
@@ -1,25 +1,24 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
- import Canvas from './Canvas';
5
- import { usePanZoom } from '../hooks/usePanZoom';
6
- import useResizeObserver from '../hooks/useResizeObserver';
7
- import { drawCanvas, resolutionCanvas } from '../utils/graphic';
8
- import { useDebounce } from '../hooks/useDebounce';
9
- const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton, resetOnImageChange = true, editable = false, timeout = 10000, onImageLoadSuccess, onImageLoadError, }) => {
4
+ import AnnotationLayer from '../AnnotationLayer';
5
+ import { useImagePanZoom } from './_hooks/useImagePanZoom';
6
+ import useResizeObserver from '../../hooks/useResizeObserver';
7
+ import { clearCanvasRect, drawCanvas, resolutionCanvas } from '../../utils/graphic';
8
+ import { useDebounce } from '../../hooks/useDebounce';
9
+ const AnnotationViewer = ({ image, coordinates, panZoomable = false, ZoomButton, resetOnImageChange = true, editable = false, timeout = 10000, onImageLoadSuccess, onImageLoadError, }) => {
10
10
  const canvasRef = useRef(null);
11
11
  const imageRef = useRef(new Image());
12
+ const [displayCoordinates, setDisplayCoordinates] = useState();
12
13
  const debouncedHandleResize = useDebounce((size) => {
13
14
  if (image && size.width && size.height && canvasRef.current && imageRef.current.src) {
14
- console.log('> Debounced resize - canvasRef.current:', canvasRef.current);
15
15
  const canvas = resolutionCanvas(canvasRef.current);
16
16
  if (canvas)
17
17
  initZoomAndPosition(canvas, imageRef.current);
18
18
  }
19
19
  }, 150);
20
20
  useResizeObserver({ ref: canvasRef, onResize: debouncedHandleResize });
21
- const [displayCoordinates, setDisplayCoordinates] = useState();
22
- const { canvasState, initZoomAndPosition, initCanvas, clearCanvas, preserveZoomAndPosition, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, } = usePanZoom({ canvasRef, imageRef, panZoomable });
21
+ const { canvasState, initZoomAndPosition, initCanvas, preserveZoomAndPosition, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, } = useImagePanZoom({ canvasRef, imageRef, panZoomable });
23
22
  useEffect(() => {
24
23
  const createEmptyImage = () => {
25
24
  const img = new Image();
@@ -27,7 +26,7 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
27
26
  return img;
28
27
  };
29
28
  const resetCanvas = (errorMsg) => {
30
- clearCanvas();
29
+ clearCanvasRect(canvasRef.current);
31
30
  setDisplayCoordinates([]);
32
31
  imageRef.current = createEmptyImage();
33
32
  if (errorMsg)
@@ -43,8 +42,6 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
43
42
  onImageLoadSuccess?.();
44
43
  if (resetOnImageChange) {
45
44
  setTimeout(() => initCanvas(), 0);
46
- // const canvas = resolutionCanvas(canvasRef.current);
47
- // if (canvas) initZoomAndPosition(canvas, tempImage);
48
45
  }
49
46
  else {
50
47
  preserveZoomAndPosition(canvasRef.current, tempImage, canvasState.zoom / (canvasState.initZoom || 1));
@@ -78,6 +75,6 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
78
75
  height: '100%',
79
76
  left: 0,
80
77
  top: 0,
81
- } }), _jsx(Canvas, { ...canvasState, coordinates: displayCoordinates, editable: editable }), ZoomButton && canvasState.initZoom > 0 && (_jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((canvasState.zoom / canvasState.initZoom) * 100)}%` }))] }) }));
78
+ } }), _jsx(AnnotationLayer, { ...canvasState, coordinates: displayCoordinates, editable: editable }), ZoomButton && canvasState.initZoom > 0 && (_jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((canvasState.zoom / canvasState.initZoom) * 100)}%` }))] }) }));
82
79
  };
83
- export default AnnotatedCanvas;
80
+ export default AnnotationViewer;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import AnnotatedCanvas from './components/AnnotatedCanvas';
2
- export { AnnotatedCanvas };
3
- export type { ZoomButtonType } from './components/AnnotatedCanvas';
1
+ import AnnotationViewer from './components/AnnotationViewer';
2
+ export { AnnotationViewer };
3
+ export type { ZoomButtonType } from './components/AnnotationViewer';
4
4
  export type { Rectangle, Coordinate } from './types/coordinate';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import AnnotatedCanvas from './components/AnnotatedCanvas';
2
- export { AnnotatedCanvas };
1
+ import AnnotationViewer from './components/AnnotationViewer';
2
+ export { AnnotationViewer };
@@ -34,4 +34,15 @@ export declare const __rotate: (mouse: Point, rotate: number, center: Point) =>
34
34
  * @returns 캔버스 크기 해상도에 맞춰 변형된 마우스 좌표
35
35
  */
36
36
  export declare const getMousePointTransform: (mousePoint: Point, movePoint: Point, zoomPoint: Point, originPoint: Point, zoom: number, canvas_el: HTMLCanvasElement, rotate?: number) => Point;
37
+ export declare const clearCanvasRect: (canvas: HTMLCanvasElement | null | undefined) => void;
38
+ /**
39
+ * 마우스 휠 기반 확대/축소 계산
40
+ * @param currentZoom 현재 zoom 값
41
+ * @param deltaY wheelEvent.deltaY
42
+ * @param minZoom 최소 zoom
43
+ * @param maxZoom 최대 zoom
44
+ * @param zoomUnit 확대/축소 단위
45
+ * @returns 계산된 zoom 값
46
+ */
47
+ export declare const calculateZoom: (currentZoom: number, deltaY: number, minZoom: number, maxZoom: number, zoomUnit: number) => number;
37
48
  export {};
@@ -131,3 +131,28 @@ export const getMousePointTransform = (mousePoint, movePoint, zoomPoint, originP
131
131
  mouse = __zoom(mouse, zoomPoint, zoom, canvas_el);
132
132
  return mouse;
133
133
  };
134
+ export const clearCanvasRect = (canvas) => {
135
+ if (!canvas)
136
+ return;
137
+ const ctx = canvas.getContext('2d');
138
+ const resolvedCanvas = resolutionCanvas(canvas);
139
+ if (resolvedCanvas)
140
+ ctx?.clearRect(0, 0, resolvedCanvas.width, resolvedCanvas.height);
141
+ };
142
+ /**
143
+ * 마우스 휠 기반 확대/축소 계산
144
+ * @param currentZoom 현재 zoom 값
145
+ * @param deltaY wheelEvent.deltaY
146
+ * @param minZoom 최소 zoom
147
+ * @param maxZoom 최대 zoom
148
+ * @param zoomUnit 확대/축소 단위
149
+ * @returns 계산된 zoom 값
150
+ */
151
+ export const calculateZoom = (currentZoom, deltaY, minZoom, maxZoom, zoomUnit) => {
152
+ let newZoom = deltaY < 0 ? currentZoom * (1 / zoomUnit) : currentZoom * zoomUnit;
153
+ if (newZoom > maxZoom)
154
+ newZoom = maxZoom;
155
+ if (newZoom < minZoom)
156
+ newZoom = minZoom;
157
+ return newZoom;
158
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnoid/canvas",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",
@@ -1,16 +0,0 @@
1
- import { Coordinate } from '../types/coordinate';
2
- type Props = {
3
- moveX: number;
4
- moveY: number;
5
- zoomX: number;
6
- zoomY: number;
7
- zoom: number;
8
- dx: number;
9
- dy: number;
10
- dw: number;
11
- dh: number;
12
- coordinates?: Coordinate[];
13
- editable?: boolean;
14
- };
15
- declare const Canvas: ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }: Props) => import("react/jsx-runtime").JSX.Element;
16
- export default Canvas;
@@ -1,4 +0,0 @@
1
- export declare enum MouseStatus {
2
- MOVE = "MOVE",
3
- STOP = "STOP"
4
- }
@@ -1,5 +0,0 @@
1
- export var MouseStatus;
2
- (function (MouseStatus) {
3
- MouseStatus["MOVE"] = "MOVE";
4
- MouseStatus["STOP"] = "STOP";
5
- })(MouseStatus || (MouseStatus = {}));