@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 +4 -1
- package/dist/components/AnnotationLayer/index.d.ts +16 -0
- package/dist/components/{Canvas.js → AnnotationLayer/index.js} +3 -3
- package/dist/{hooks/usePanZoom.d.ts → components/AnnotationViewer/_hooks/useImagePanZoom.d.ts} +2 -3
- package/dist/{hooks/usePanZoom.js → components/AnnotationViewer/_hooks/useImagePanZoom.js} +18 -32
- package/dist/components/{AnnotatedCanvas.d.ts → AnnotationViewer/index.d.ts} +3 -3
- package/dist/components/{AnnotatedCanvas.js → AnnotationViewer/index.js} +11 -14
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/utils/graphic.d.ts +11 -0
- package/dist/utils/graphic.js +25 -0
- package/package.json +1 -1
- package/dist/components/Canvas.d.ts +0 -16
- package/dist/enum/common.d.ts +0 -4
- package/dist/enum/common.js +0 -5
package/README.md
CHANGED
|
@@ -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 '
|
|
4
|
-
const
|
|
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
|
|
35
|
+
export default AnnotationLayer;
|
package/dist/{hooks/usePanZoom.d.ts → components/AnnotationViewer/_hooks/useImagePanZoom.d.ts}
RENAMED
|
@@ -11,17 +11,16 @@ type CanvasState = {
|
|
|
11
11
|
zoom: number;
|
|
12
12
|
initZoom: number;
|
|
13
13
|
};
|
|
14
|
-
type
|
|
14
|
+
type Props = {
|
|
15
15
|
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
16
16
|
imageRef: RefObject<HTMLImageElement>;
|
|
17
17
|
panZoomable?: boolean;
|
|
18
18
|
};
|
|
19
|
-
export declare function
|
|
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,
|
|
4
|
-
|
|
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
|
|
25
|
-
const zoomPoint = calculatorZoomPoint(canvas, 0, 0,
|
|
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:
|
|
29
|
-
dy:
|
|
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
|
|
41
|
+
const center = canvasCenterPoint(canvas, image);
|
|
43
42
|
const newInitZoom = calculateInitZoom(canvas, image);
|
|
44
|
-
const zoomPoint = calculatorZoomPoint(canvas, canvasState.moveX, canvasState.moveY,
|
|
43
|
+
const zoomPoint = calculatorZoomPoint(canvas, canvasState.moveX, canvasState.moveY, center.x, center.y);
|
|
45
44
|
setCanvasState({
|
|
46
|
-
dx:
|
|
47
|
-
dy:
|
|
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
|
-
|
|
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:
|
|
71
|
+
zoom: newZoom,
|
|
85
72
|
});
|
|
86
73
|
};
|
|
87
74
|
const handleMouseMove = (event) => {
|
|
88
|
-
if (!panZoomable || !canvasRef.current ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
|
22
|
-
export default
|
|
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
|
|
5
|
-
import {
|
|
6
|
-
import useResizeObserver from '
|
|
7
|
-
import { drawCanvas, resolutionCanvas } from '
|
|
8
|
-
import { useDebounce } from '
|
|
9
|
-
const
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
80
|
+
export default AnnotationViewer;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
export {
|
|
3
|
-
export type { ZoomButtonType } from './components/
|
|
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
|
|
2
|
-
export {
|
|
1
|
+
import AnnotationViewer from './components/AnnotationViewer';
|
|
2
|
+
export { AnnotationViewer };
|
package/dist/utils/graphic.d.ts
CHANGED
|
@@ -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 {};
|
package/dist/utils/graphic.js
CHANGED
|
@@ -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,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;
|
package/dist/enum/common.d.ts
DELETED