@deepnoid/canvas 0.1.38 → 0.1.40
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/components/{AnnotationViewer → AnnotationCanvas}/_hooks/useImagePanZoom.d.ts +7 -13
- package/dist/components/{AnnotationViewer → AnnotationCanvas}/_hooks/useImagePanZoom.js +33 -23
- package/dist/components/AnnotationCanvas/_utils/createHistory.d.ts +11 -0
- package/dist/components/AnnotationCanvas/_utils/createHistory.js +34 -0
- package/dist/components/AnnotationCanvas/_utils/panZoom.d.ts +10 -0
- package/dist/components/AnnotationCanvas/_utils/panZoom.js +29 -0
- package/dist/components/AnnotationCanvas/index.d.ts +26 -0
- package/dist/components/AnnotationCanvas/index.js +83 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangle.d.ts +5 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangle.js +75 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangleUtils.d.ts +30 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangleUtils.js +211 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/useDrawEvents.d.ts +25 -0
- package/dist/components/AnnotationLayer/_hooks/drawEvents/useDrawEvents.js +42 -0
- package/dist/components/AnnotationLayer/_hooks/useCanvasDraw.d.ts +13 -0
- package/dist/components/AnnotationLayer/_hooks/useCanvasDraw.js +115 -0
- package/dist/components/AnnotationLayer/_hooks/useHotkeys.d.ts +7 -0
- package/dist/components/AnnotationLayer/_hooks/useHotkeys.js +15 -0
- package/dist/components/AnnotationLayer/_hooks/useMouse.d.ts +21 -0
- package/dist/components/AnnotationLayer/_hooks/useMouse.js +34 -0
- package/dist/components/AnnotationLayer/index.d.ts +10 -13
- package/dist/components/AnnotationLayer/index.js +116 -22
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +8 -0
- package/dist/enum/common.d.ts +13 -0
- package/dist/enum/common.js +15 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -2
- package/dist/types/index.d.ts +56 -0
- package/dist/utils/canvas.d.ts +3 -0
- package/dist/utils/canvas.js +37 -0
- package/dist/utils/common/cloneDeep.d.ts +1 -0
- package/dist/utils/common/cloneDeep.js +18 -0
- package/dist/utils/common/cloneDeepWith.d.ts +1 -0
- package/dist/utils/common/cloneDeepWith.js +34 -0
- package/dist/utils/common/isEqualWith.d.ts +2 -0
- package/dist/utils/common/isEqualWith.js +70 -0
- package/dist/utils/mouseActions.d.ts +8 -0
- package/dist/utils/mouseActions.js +19 -0
- package/dist/utils/pointTransform.d.ts +2 -0
- package/dist/utils/pointTransform.js +46 -0
- package/package.json +2 -1
- package/dist/components/AnnotationViewer/index.d.ts +0 -22
- package/dist/components/AnnotationViewer/index.js +0 -80
- package/dist/constants/graphic.d.ts +0 -1
- package/dist/constants/graphic.js +0 -1
- package/dist/types/coordinate.d.ts +0 -10
- package/dist/utils/graphic.d.ts +0 -48
- package/dist/utils/graphic.js +0 -158
- /package/dist/types/{coordinate.js → index.js} +0 -0
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
import { RefObject, WheelEvent, MouseEvent } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
dy: number;
|
|
5
|
-
dw: number;
|
|
6
|
-
dh: number;
|
|
7
|
-
moveX: number;
|
|
8
|
-
moveY: number;
|
|
9
|
-
zoomX: number;
|
|
10
|
-
zoomY: number;
|
|
11
|
-
zoom: number;
|
|
2
|
+
import { AnnotationCanvasOptionsZoom, CanvasState } from '../../../types';
|
|
3
|
+
type ImageCanvasState = CanvasState & {
|
|
12
4
|
initZoom: number;
|
|
13
5
|
};
|
|
14
6
|
type Props = {
|
|
15
7
|
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
16
8
|
imageRef: RefObject<HTMLImageElement>;
|
|
17
|
-
|
|
9
|
+
panZoomEnabled?: boolean;
|
|
10
|
+
zoom?: AnnotationCanvasOptionsZoom;
|
|
11
|
+
editable?: boolean;
|
|
18
12
|
};
|
|
19
|
-
export declare function useImagePanZoom({ canvasRef, imageRef,
|
|
20
|
-
canvasState:
|
|
13
|
+
export declare function useImagePanZoom({ canvasRef, imageRef, panZoomEnabled, zoom, editable }: Props): {
|
|
14
|
+
canvasState: ImageCanvasState;
|
|
21
15
|
initZoomAndPosition: (canvas: HTMLCanvasElement, image: HTMLImageElement) => void;
|
|
22
16
|
preserveZoomAndPosition: (canvas: HTMLCanvasElement, image: HTMLImageElement, zoomRatio: number) => void;
|
|
23
17
|
initCanvas: () => void;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useState, useRef } from 'react';
|
|
3
|
-
import { getMousePointTransform
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const MIN_ZOOM = 0.5;
|
|
3
|
+
import { getMousePointTransform } from '../../../utils/pointTransform';
|
|
4
|
+
import { isMouseClickAction, isMouseDragAction, MouseAction } from '../../../utils/mouseActions';
|
|
5
|
+
import { calculateInitZoom, calculateZoom, calculatorZoomPoint, canvasCenterPoint } from '../_utils/panZoom';
|
|
6
|
+
export function useImagePanZoom({ canvasRef, imageRef, panZoomEnabled = false, zoom, editable = false }) {
|
|
8
7
|
const [canvasState, setCanvasState] = useState({
|
|
9
|
-
dx: 0,
|
|
10
|
-
dy: 0,
|
|
11
|
-
dw: 0,
|
|
12
|
-
dh: 0,
|
|
13
8
|
moveX: 0,
|
|
14
9
|
moveY: 0,
|
|
15
10
|
zoomX: 0,
|
|
16
11
|
zoomY: 0,
|
|
17
12
|
zoom: 0,
|
|
13
|
+
dx: 0,
|
|
14
|
+
dy: 0,
|
|
15
|
+
dw: 0,
|
|
16
|
+
dh: 0,
|
|
18
17
|
initZoom: 0,
|
|
19
18
|
});
|
|
20
19
|
const [startMousePoint, setStartMousePoint] = useState();
|
|
@@ -24,16 +23,16 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
24
23
|
const zoomPoint = calculatorZoomPoint(canvas, 0, 0, center.x, center.y);
|
|
25
24
|
const initZoomValue = calculateInitZoom(canvas, image);
|
|
26
25
|
setCanvasState({
|
|
27
|
-
dx: center.x,
|
|
28
|
-
dy: center.y,
|
|
29
|
-
dw: image.width,
|
|
30
|
-
dh: image.height,
|
|
31
26
|
moveX: 0,
|
|
32
27
|
moveY: 0,
|
|
33
28
|
zoomX: zoomPoint.x,
|
|
34
29
|
zoomY: zoomPoint.y,
|
|
35
30
|
zoom: initZoomValue,
|
|
36
31
|
initZoom: initZoomValue,
|
|
32
|
+
dx: center.x,
|
|
33
|
+
dy: center.y,
|
|
34
|
+
dw: image.width,
|
|
35
|
+
dh: image.height,
|
|
37
36
|
});
|
|
38
37
|
};
|
|
39
38
|
const preserveZoomAndPosition = (canvas, image, zoomRatio) => {
|
|
@@ -42,15 +41,15 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
42
41
|
const newInitZoom = calculateInitZoom(canvas, image);
|
|
43
42
|
const zoomPoint = calculatorZoomPoint(canvas, canvasState.moveX, canvasState.moveY, center.x, center.y);
|
|
44
43
|
setCanvasState({
|
|
45
|
-
dx: center.x,
|
|
46
|
-
dy: center.y,
|
|
47
|
-
dw: image.width,
|
|
48
|
-
dh: image.height,
|
|
49
44
|
moveX: canvasState.moveX,
|
|
50
45
|
moveY: canvasState.moveY,
|
|
51
46
|
zoomX: zoomPoint.x,
|
|
52
47
|
zoomY: zoomPoint.y,
|
|
53
48
|
zoom: newInitZoom * prevZoomRatio,
|
|
49
|
+
dx: center.x,
|
|
50
|
+
dy: center.y,
|
|
51
|
+
dw: image.width,
|
|
52
|
+
dh: image.height,
|
|
54
53
|
initZoom: newInitZoom,
|
|
55
54
|
});
|
|
56
55
|
};
|
|
@@ -60,9 +59,16 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
60
59
|
initZoomAndPosition(canvasRef.current, imageRef.current);
|
|
61
60
|
};
|
|
62
61
|
const handleWheel = (event) => {
|
|
63
|
-
if (!
|
|
62
|
+
if (!panZoomEnabled || !canvasRef.current || canvasState.initZoom <= 0)
|
|
64
63
|
return;
|
|
65
|
-
const
|
|
64
|
+
const zoomStep = zoom?.step || 0.9;
|
|
65
|
+
const maxZoom = zoom?.max;
|
|
66
|
+
const minZoom = zoom?.min;
|
|
67
|
+
const newZoom = maxZoom && minZoom
|
|
68
|
+
? calculateZoom(canvasState.zoom, event.deltaY, canvasState.initZoom * minZoom, canvasState.initZoom * maxZoom, zoomStep)
|
|
69
|
+
: event.deltaY < 0
|
|
70
|
+
? (canvasState.zoom || 1) * (1 / zoomStep)
|
|
71
|
+
: (canvasState.zoom || 1) * zoomStep;
|
|
66
72
|
const zoomPoint = calculatorZoomPoint(canvasRef.current, canvasState.moveX, canvasState.moveY, canvasState.dx, canvasState.dy);
|
|
67
73
|
setCanvasState({
|
|
68
74
|
...canvasState,
|
|
@@ -72,7 +78,9 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
72
78
|
});
|
|
73
79
|
};
|
|
74
80
|
const handleMouseMove = (event) => {
|
|
75
|
-
if (!
|
|
81
|
+
if (!panZoomEnabled || !canvasRef.current || !isDraggingRef.current)
|
|
82
|
+
return;
|
|
83
|
+
if (editable && isMouseDragAction(event.button, MouseAction.LEFT))
|
|
76
84
|
return;
|
|
77
85
|
const canvas = canvasRef.current;
|
|
78
86
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -96,7 +104,9 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
96
104
|
event.preventDefault();
|
|
97
105
|
};
|
|
98
106
|
const handleMouseDown = (event) => {
|
|
99
|
-
if (!
|
|
107
|
+
if (!panZoomEnabled || !canvasRef.current)
|
|
108
|
+
return;
|
|
109
|
+
if (editable && isMouseClickAction(event.button, MouseAction.LEFT))
|
|
100
110
|
return;
|
|
101
111
|
isDraggingRef.current = true;
|
|
102
112
|
const canvas = canvasRef.current;
|
|
@@ -108,13 +118,13 @@ export function useImagePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
|
108
118
|
event.preventDefault();
|
|
109
119
|
};
|
|
110
120
|
const handleMouseUp = (event) => {
|
|
111
|
-
if (!
|
|
121
|
+
if (!panZoomEnabled || !canvasRef.current)
|
|
112
122
|
return;
|
|
113
123
|
isDraggingRef.current = false;
|
|
114
124
|
event.preventDefault();
|
|
115
125
|
};
|
|
116
126
|
const handleMouseLeave = (event) => {
|
|
117
|
-
if (!
|
|
127
|
+
if (!panZoomEnabled || !canvasRef.current)
|
|
118
128
|
return;
|
|
119
129
|
isDraggingRef.current = false;
|
|
120
130
|
event.preventDefault();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Coordinate } from '../../../types';
|
|
2
|
+
export type CoordinateHistory = {
|
|
3
|
+
getHistory: () => Coordinate[][];
|
|
4
|
+
getHistoryIndex: () => number;
|
|
5
|
+
setHistoryIndex: (index: number) => void;
|
|
6
|
+
init: (coordinates?: Coordinate[]) => void;
|
|
7
|
+
add: (coordinates: Coordinate[]) => void;
|
|
8
|
+
undo: () => Coordinate[] | undefined;
|
|
9
|
+
redo: () => Coordinate[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare const createHistory: () => CoordinateHistory;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const createHistory = () => {
|
|
2
|
+
let history = [];
|
|
3
|
+
let historyIndex = -1;
|
|
4
|
+
return {
|
|
5
|
+
getHistory: () => history,
|
|
6
|
+
getHistoryIndex: () => historyIndex,
|
|
7
|
+
setHistoryIndex: (index) => {
|
|
8
|
+
historyIndex = index;
|
|
9
|
+
},
|
|
10
|
+
init: (coordinates = []) => {
|
|
11
|
+
history = [coordinates];
|
|
12
|
+
historyIndex = 0;
|
|
13
|
+
},
|
|
14
|
+
add: (coordinates) => {
|
|
15
|
+
history.splice(historyIndex + 1);
|
|
16
|
+
history.push(coordinates);
|
|
17
|
+
historyIndex++;
|
|
18
|
+
},
|
|
19
|
+
undo: () => {
|
|
20
|
+
if (historyIndex > 0) {
|
|
21
|
+
historyIndex--;
|
|
22
|
+
return history[historyIndex];
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
},
|
|
26
|
+
redo: () => {
|
|
27
|
+
if (historyIndex < history.length - 1) {
|
|
28
|
+
historyIndex++;
|
|
29
|
+
return history[historyIndex];
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const canvasCenterPoint: (canvas: HTMLCanvasElement, image: HTMLImageElement) => {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
export declare const calculatorZoomPoint: (canvas: HTMLCanvasElement, movementX: number, movementY: number, dx: number, dy: number) => {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const calculateInitZoom: (canvas: HTMLCanvasElement, image: HTMLImageElement) => number;
|
|
10
|
+
export declare const calculateZoom: (currentZoom: number, deltaY: number, minZoom: number, maxZoom: number, zoomUnit: number) => number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const canvasCenterPoint = (canvas, image) => {
|
|
2
|
+
const centerX = canvas.width / 2 - image.width / 2;
|
|
3
|
+
const centerY = canvas.height / 2 - image.height / 2;
|
|
4
|
+
return { x: centerX, y: centerY };
|
|
5
|
+
};
|
|
6
|
+
export const calculatorZoomPoint = (canvas, movementX, movementY, dx, dy) => {
|
|
7
|
+
let x = 0;
|
|
8
|
+
let y = 0;
|
|
9
|
+
if (canvas) {
|
|
10
|
+
x = -dx - movementX + canvas.width / 2;
|
|
11
|
+
y = -dy - movementY + canvas.height / 2;
|
|
12
|
+
}
|
|
13
|
+
return { x, y };
|
|
14
|
+
};
|
|
15
|
+
export const calculateInitZoom = (canvas, image) => {
|
|
16
|
+
if (!canvas || !image.naturalWidth)
|
|
17
|
+
return 1;
|
|
18
|
+
const canvasRatio = canvas.clientWidth / canvas.clientHeight;
|
|
19
|
+
const imageRatio = image.naturalWidth / image.naturalHeight;
|
|
20
|
+
return canvasRatio < imageRatio ? canvas.clientWidth / image.naturalWidth : canvas.clientHeight / image.naturalHeight;
|
|
21
|
+
};
|
|
22
|
+
export const calculateZoom = (currentZoom, deltaY, minZoom, maxZoom, zoomUnit) => {
|
|
23
|
+
let newZoom = deltaY < 0 ? currentZoom * (1 / zoomUnit) : currentZoom * zoomUnit;
|
|
24
|
+
if (newZoom > maxZoom)
|
|
25
|
+
newZoom = maxZoom;
|
|
26
|
+
if (newZoom < minZoom)
|
|
27
|
+
newZoom = minZoom;
|
|
28
|
+
return newZoom;
|
|
29
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ComponentType, Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
|
+
import { AnnotationCanvasDrawing, AnnotationCanvasOptionsZoom, Coordinate } from '../../types';
|
|
3
|
+
export type ZoomButtonType = {
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export type AnnotationCanvasProps = {
|
|
8
|
+
image: string;
|
|
9
|
+
coordinates?: Coordinate[];
|
|
10
|
+
setCoordinates?: Dispatch<SetStateAction<Coordinate[] | undefined>>;
|
|
11
|
+
options: {
|
|
12
|
+
panZoomEnabled?: boolean;
|
|
13
|
+
zoom?: AnnotationCanvasOptionsZoom;
|
|
14
|
+
ZoomButton?: ComponentType<ZoomButtonType>;
|
|
15
|
+
resetOnImageChange?: boolean;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
};
|
|
18
|
+
drawing: AnnotationCanvasDrawing;
|
|
19
|
+
events: {
|
|
20
|
+
onImageLoadSuccess?: () => void;
|
|
21
|
+
onImageLoadError?: (error: Error) => void;
|
|
22
|
+
};
|
|
23
|
+
editable?: boolean;
|
|
24
|
+
};
|
|
25
|
+
declare const AnnotationCanvas: ({ image, coordinates, setCoordinates, options, drawing, events, editable, }: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export default AnnotationCanvas;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import AnnotationLayer from '../AnnotationLayer';
|
|
5
|
+
import { useImagePanZoom } from './_hooks/useImagePanZoom';
|
|
6
|
+
import useResizeObserver from '../../hooks/useResizeObserver';
|
|
7
|
+
import { useDebounce } from '../../hooks/useDebounce';
|
|
8
|
+
import { DrawMode } from '../../enum/common';
|
|
9
|
+
import { createHistory } from './_utils/createHistory';
|
|
10
|
+
import { clearCanvasRect, drawCanvas, resolutionCanvas } from '../../utils/canvas';
|
|
11
|
+
const AnnotationCanvas = ({ image, coordinates, setCoordinates, options, drawing, events, editable = false, }) => {
|
|
12
|
+
const { panZoomEnabled, zoom, ZoomButton, resetOnImageChange, timeout = 10000 } = options;
|
|
13
|
+
const { onImageLoadSuccess, onImageLoadError } = events;
|
|
14
|
+
const canvasRef = useRef(null);
|
|
15
|
+
const imageRef = useRef(new Image());
|
|
16
|
+
const historyRef = useRef(createHistory());
|
|
17
|
+
const [displayCoordinates, setDisplayCoordinates] = useState();
|
|
18
|
+
useResizeObserver({
|
|
19
|
+
ref: canvasRef,
|
|
20
|
+
onResize: useDebounce((size) => {
|
|
21
|
+
if (image && size.width && size.height && canvasRef.current && imageRef.current.src) {
|
|
22
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
23
|
+
if (canvas)
|
|
24
|
+
initZoomAndPosition(canvas, imageRef.current);
|
|
25
|
+
}
|
|
26
|
+
}, 150),
|
|
27
|
+
});
|
|
28
|
+
const { canvasState, initZoomAndPosition, initCanvas, preserveZoomAndPosition, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, } = useImagePanZoom({ canvasRef, imageRef, panZoomEnabled, zoom, editable });
|
|
29
|
+
const createEmptyImage = () => {
|
|
30
|
+
const img = new Image();
|
|
31
|
+
img.width = img.height = 0;
|
|
32
|
+
return img;
|
|
33
|
+
};
|
|
34
|
+
const resetCanvas = (errorMsg) => {
|
|
35
|
+
clearCanvasRect(canvasRef.current);
|
|
36
|
+
setDisplayCoordinates([]);
|
|
37
|
+
imageRef.current = createEmptyImage();
|
|
38
|
+
if (errorMsg)
|
|
39
|
+
onImageLoadError?.(new Error(errorMsg));
|
|
40
|
+
};
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!image?.trim())
|
|
43
|
+
return void resetCanvas();
|
|
44
|
+
let cancelled = false;
|
|
45
|
+
const tempImage = new Image();
|
|
46
|
+
tempImage.onload = () => {
|
|
47
|
+
if (cancelled || !canvasRef.current)
|
|
48
|
+
return;
|
|
49
|
+
onImageLoadSuccess?.();
|
|
50
|
+
if (resetOnImageChange) {
|
|
51
|
+
setTimeout(() => initCanvas(), 0);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
preserveZoomAndPosition(canvasRef.current, tempImage, canvasState.zoom / (canvasState.initZoom || 1));
|
|
55
|
+
}
|
|
56
|
+
imageRef.current = tempImage;
|
|
57
|
+
setDisplayCoordinates(coordinates?.map((coordinate) => {
|
|
58
|
+
return {
|
|
59
|
+
...coordinate,
|
|
60
|
+
type: coordinate.type ? coordinate.type : DrawMode.RECTANGLE,
|
|
61
|
+
};
|
|
62
|
+
}) || []);
|
|
63
|
+
};
|
|
64
|
+
tempImage.onerror = () => !cancelled && resetCanvas(`Failed to load image: ${image}`);
|
|
65
|
+
tempImage.src = image;
|
|
66
|
+
return () => {
|
|
67
|
+
cancelled = true;
|
|
68
|
+
};
|
|
69
|
+
}, [image, resetOnImageChange]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!canvasRef.current)
|
|
72
|
+
return;
|
|
73
|
+
const redraw = (canvas) => {
|
|
74
|
+
const { moveX, moveY, zoomX, zoomY, zoom, dx, dy } = canvasState;
|
|
75
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvas, imageRef.current, true);
|
|
76
|
+
};
|
|
77
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
78
|
+
if (canvas)
|
|
79
|
+
redraw(canvas);
|
|
80
|
+
}, [canvasState]);
|
|
81
|
+
return (_jsx("div", { style: { width: '100%', height: '100%', display: 'flex', flex: 1 }, children: _jsxs("div", { onWheel: handleWheel, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave, onContextMenu: (e) => e.preventDefault(), style: { flex: 1, position: 'relative', cursor: editable ? 'default' : 'grab' }, children: [_jsx("canvas", { ref: canvasRef, style: { position: 'absolute', width: '100%', height: '100%', left: 0, top: 0 } }), _jsx(AnnotationLayer, { canvasState: canvasState, coordinates: displayCoordinates, setCoordinates: setCoordinates, historyRef: historyRef, drawing: drawing, editable: editable }), ZoomButton && canvasState.initZoom > 0 && (_jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((canvasState.zoom / canvasState.initZoom) * 100)}%` }))] }) }));
|
|
82
|
+
};
|
|
83
|
+
export default AnnotationCanvas;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DrawEventsParams } from './useDrawEvents';
|
|
2
|
+
export declare const handleMouseDownRectangle: ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, startMousePointRef, currentCoordinateRef, coordinatesRef, rectangleAnchorRef, }: DrawEventsParams) => (event: globalThis.MouseEvent) => void;
|
|
3
|
+
export declare const handleMouseMoveRectangle: ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, prevMousePointRef, currentCoordinateRef, coordinatesRef, rectangleAnchorRef, }: DrawEventsParams) => (event: globalThis.MouseEvent) => void;
|
|
4
|
+
export declare const handleMouseUpRectangle: ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, startMousePointRef, prevMousePointRef, currentCoordinateRef, coordinatesRef, setCoordinates, rectangleAnchorRef, historyRef, drawLabel, }: DrawEventsParams) => (event: globalThis.MouseEvent) => void;
|
|
5
|
+
export declare const handleMouseLeaveRectangle: ({ canvasRef, canvasStateRef, setMousePoint, startMousePointRef, prevMousePointRef, mouseActionRef, rectangleAnchorRef, }: DrawEventsParams) => (event: globalThis.MouseEvent) => void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DrawMode } from '../../../../enum/common';
|
|
2
|
+
import { cloneDeep } from '../../../../utils/common/cloneDeep';
|
|
3
|
+
import { isMouseClickAction, MouseAction } from '../../../../utils/mouseActions';
|
|
4
|
+
import { ACTIVE_POINT_SIZE, clampBoundingBoxToImage, createCoordinate, getCanvasMousePoint, handleBBoxOperations, isInsideImage, updateActiveRectangleAnchor, selectRectangleAtPoint, } from './rectangleUtils';
|
|
5
|
+
export const handleMouseDownRectangle = ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, startMousePointRef, currentCoordinateRef, coordinatesRef, rectangleAnchorRef, }) => (event) => {
|
|
6
|
+
if (!canvasRef.current || !isMouseClickAction(event.button, MouseAction.LEFT))
|
|
7
|
+
return;
|
|
8
|
+
const mousePoint = getCanvasMousePoint(event, canvasRef.current, canvasStateRef.current);
|
|
9
|
+
if (!isInsideImage(mousePoint, canvasStateRef.current.dw, canvasStateRef.current.dh))
|
|
10
|
+
return;
|
|
11
|
+
mouseActionRef.current = MouseAction.LEFT;
|
|
12
|
+
setMousePoint(mousePoint);
|
|
13
|
+
startMousePointRef.current = mousePoint;
|
|
14
|
+
if (currentCoordinateRef.current) {
|
|
15
|
+
const { zoom } = canvasStateRef.current;
|
|
16
|
+
selectRectangleAtPoint(zoom, mousePointRef.current, coordinatesRef, currentCoordinateRef);
|
|
17
|
+
updateActiveRectangleAnchor(zoom, mousePointRef.current, currentCoordinateRef, rectangleAnchorRef);
|
|
18
|
+
}
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
};
|
|
21
|
+
export const handleMouseMoveRectangle = ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, prevMousePointRef, currentCoordinateRef, coordinatesRef, rectangleAnchorRef, }) => (event) => {
|
|
22
|
+
if (!canvasRef.current || !isMouseClickAction(event.button, MouseAction.LEFT))
|
|
23
|
+
return;
|
|
24
|
+
prevMousePointRef.current = cloneDeep(mousePointRef.current);
|
|
25
|
+
const mousePoint = getCanvasMousePoint(event, canvasRef.current, canvasStateRef.current);
|
|
26
|
+
setMousePoint(mousePoint);
|
|
27
|
+
handleBBoxOperations({
|
|
28
|
+
coordinatesRef,
|
|
29
|
+
dw: canvasStateRef.current.dw,
|
|
30
|
+
dh: canvasStateRef.current.dh,
|
|
31
|
+
mousePoint: mousePointRef.current,
|
|
32
|
+
prevMousePoint: prevMousePointRef.current,
|
|
33
|
+
mouseAction: mouseActionRef.current,
|
|
34
|
+
currentCoordinate: currentCoordinateRef.current,
|
|
35
|
+
rectangleAnchor: rectangleAnchorRef.current,
|
|
36
|
+
});
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
};
|
|
39
|
+
export const handleMouseUpRectangle = ({ canvasRef, canvasStateRef, mouseActionRef, mousePointRef, setMousePoint, startMousePointRef, prevMousePointRef, currentCoordinateRef, coordinatesRef, setCoordinates, rectangleAnchorRef, historyRef, drawLabel, }) => (event) => {
|
|
40
|
+
if (!canvasRef.current || !isMouseClickAction(event.button, MouseAction.LEFT))
|
|
41
|
+
return;
|
|
42
|
+
const mousePoint = getCanvasMousePoint(event, canvasRef.current, canvasStateRef.current);
|
|
43
|
+
setMousePoint(mousePoint);
|
|
44
|
+
if (startMousePointRef.current) {
|
|
45
|
+
const movedWidth = Math.abs(startMousePointRef.current.x - mousePoint.x);
|
|
46
|
+
const movedHeight = Math.abs(startMousePointRef.current.y - mousePoint.y);
|
|
47
|
+
const { zoom, dw, dh } = canvasStateRef.current;
|
|
48
|
+
if (movedWidth > ACTIVE_POINT_SIZE || movedHeight > ACTIVE_POINT_SIZE) {
|
|
49
|
+
const { x, y, width, height } = clampBoundingBoxToImage(Math.min(startMousePointRef.current.x, mousePoint.x), Math.min(startMousePointRef.current.y, mousePoint.y), movedWidth, movedHeight, dw, dh);
|
|
50
|
+
if (!currentCoordinateRef.current) {
|
|
51
|
+
const newCoordinate = { label: drawLabel, type: DrawMode.RECTANGLE, x, y, width, height, selected: false };
|
|
52
|
+
createCoordinate(newCoordinate, setCoordinates, coordinatesRef, historyRef, currentCoordinateRef);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
selectRectangleAtPoint(zoom, mousePointRef.current, coordinatesRef, currentCoordinateRef);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
mouseActionRef.current = null;
|
|
60
|
+
startMousePointRef.current = null;
|
|
61
|
+
prevMousePointRef.current = null;
|
|
62
|
+
rectangleAnchorRef.current = null;
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
};
|
|
65
|
+
export const handleMouseLeaveRectangle = ({ canvasRef, canvasStateRef, setMousePoint, startMousePointRef, prevMousePointRef, mouseActionRef, rectangleAnchorRef, }) => (event) => {
|
|
66
|
+
if (!canvasRef.current)
|
|
67
|
+
return;
|
|
68
|
+
const mousePoint = getCanvasMousePoint(event, canvasRef.current, canvasStateRef.current);
|
|
69
|
+
setMousePoint(mousePoint);
|
|
70
|
+
mouseActionRef.current = null;
|
|
71
|
+
startMousePointRef.current = null;
|
|
72
|
+
prevMousePointRef.current = null;
|
|
73
|
+
rectangleAnchorRef.current = null;
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { Coordinate, Point, Rectangle, CanvasState } from '../../../../types';
|
|
3
|
+
import { CoordinateHistory } from '../../../AnnotationCanvas/_utils/createHistory';
|
|
4
|
+
import { MouseAction } from '../../../../utils/mouseActions';
|
|
5
|
+
import { RectangleAnchor } from '../../../../enum/common';
|
|
6
|
+
export declare const ACTIVE_POINT_SIZE = 10;
|
|
7
|
+
export declare const getRectangleCorners: ({ x, y, width, height }: Rectangle) => {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}[];
|
|
11
|
+
export declare const selectRectangleAtPoint: (zoom: number, mousePoint: Point, coordinatesRef: RefObject<Coordinate[]>, currentCoordinateRef: RefObject<Coordinate | null>) => void;
|
|
12
|
+
export declare const updateActiveRectangleAnchor: (zoom: number, mousePointRef: Point, currentCoordinateRef: RefObject<Coordinate | null>, rectangleAnchorRef: RefObject<RectangleAnchor | null>) => void;
|
|
13
|
+
export declare const createCoordinate: (coordinate: Coordinate, setCoordinates: (dataSet: Coordinate[]) => void, coordinatesRef: RefObject<Coordinate[]>, historyRef: RefObject<CoordinateHistory>, currentCoordinateRef: RefObject<Coordinate | null>) => void;
|
|
14
|
+
export declare const clampBoundingBoxToImage: (x: number, y: number, width: number, height: number, dw: number, dh: number) => Rectangle & {
|
|
15
|
+
selected?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare const getCanvasMousePoint: (event: globalThis.MouseEvent | React.MouseEvent, canvas: HTMLCanvasElement, canvasState: CanvasState) => Point;
|
|
18
|
+
type BBoxParams = {
|
|
19
|
+
coordinatesRef: RefObject<Coordinate[]>;
|
|
20
|
+
dw: number;
|
|
21
|
+
dh: number;
|
|
22
|
+
mousePoint: Point;
|
|
23
|
+
prevMousePoint: Point;
|
|
24
|
+
mouseAction: MouseAction | null;
|
|
25
|
+
currentCoordinate: Coordinate | null;
|
|
26
|
+
rectangleAnchor: RectangleAnchor | null;
|
|
27
|
+
};
|
|
28
|
+
export declare const handleBBoxOperations: (params: BBoxParams) => void;
|
|
29
|
+
export declare const isInsideImage: (point: Point, imageWidth: number, imageHeight: number) => boolean;
|
|
30
|
+
export {};
|