@deepnoid/canvas 0.1.19 → 0.1.21
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.
|
@@ -8,29 +8,29 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
|
|
|
8
8
|
const canvasRef = useRef(null);
|
|
9
9
|
const [displayCoordinates, setDisplayCoordinates] = useState();
|
|
10
10
|
const { image: loadedImage, isLoading, error } = useImageLoader(image, 10000, onImageLoadSuccess, onImageLoadError);
|
|
11
|
-
const { moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave,
|
|
11
|
+
const { moveX, moveY, zoom, initZoom, zoomX, zoomY, dx, dy, dw, dh, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, initCanvas, } = usePanZoom({
|
|
12
12
|
canvasRef,
|
|
13
13
|
image: loadedImage,
|
|
14
14
|
panZoomable,
|
|
15
15
|
resetOnImageChange,
|
|
16
16
|
});
|
|
17
|
+
const loaded = loadedImage && !isLoading;
|
|
17
18
|
useEffect(() => {
|
|
18
|
-
if (
|
|
19
|
+
if (loaded) {
|
|
19
20
|
setDisplayCoordinates(coordinates);
|
|
20
21
|
}
|
|
21
|
-
}, [
|
|
22
|
+
}, [loaded, coordinates]);
|
|
22
23
|
useEffect(() => {
|
|
23
24
|
if (isLoading) {
|
|
24
25
|
setDisplayCoordinates(undefined);
|
|
25
26
|
}
|
|
26
27
|
}, [isLoading]);
|
|
27
|
-
if (!image ||
|
|
28
|
+
if (!image || error)
|
|
28
29
|
return null;
|
|
29
|
-
}
|
|
30
30
|
return (_jsx("div", { style: { width: '100%', height: '100%', display: 'flex', flex: 1 }, children: _jsx("div", { onWheel: handleWheel, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave, style: {
|
|
31
31
|
flex: 1,
|
|
32
32
|
position: 'relative',
|
|
33
33
|
cursor: panZoomable ? 'grab' : 'default',
|
|
34
|
-
}, children:
|
|
34
|
+
}, children: loaded && (_jsxs(_Fragment, { children: [_jsx("canvas", { ref: canvasRef, style: { position: 'absolute', width: '100%', height: '100%', left: 0, top: 0 } }), _jsx(Canvas, { moveX: moveX, moveY: moveY, zoomX: zoomX, zoomY: zoomY, zoom: zoom, dx: dx, dy: dy, dw: dw, dh: dh, coordinates: displayCoordinates, editable: editable }), ZoomButton && initZoom > 0 && (_jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((zoom / initZoom) * 100)}%` }))] })) }) }));
|
|
35
35
|
};
|
|
36
36
|
export default AnnotatedCanvas;
|
|
@@ -9,17 +9,18 @@ export declare const usePanZoom: ({ canvasRef, image, panZoomable, resetOnImageC
|
|
|
9
9
|
moveX: number;
|
|
10
10
|
moveY: number;
|
|
11
11
|
zoom: number;
|
|
12
|
+
initZoom: number;
|
|
12
13
|
zoomX: number;
|
|
13
14
|
zoomY: number;
|
|
14
15
|
dx: number;
|
|
15
16
|
dy: number;
|
|
16
17
|
dw: number;
|
|
17
18
|
dh: number;
|
|
18
|
-
handleWheel: (
|
|
19
|
-
handleMouseDown: (
|
|
20
|
-
handleMouseMove: (
|
|
21
|
-
handleMouseUp: () => void;
|
|
22
|
-
handleMouseLeave: () => void;
|
|
23
|
-
|
|
19
|
+
handleWheel: (event: WheelEvent) => void;
|
|
20
|
+
handleMouseDown: (event: MouseEvent) => false | undefined;
|
|
21
|
+
handleMouseMove: (event: MouseEvent) => void;
|
|
22
|
+
handleMouseUp: (event: MouseEvent) => void;
|
|
23
|
+
handleMouseLeave: (event: MouseEvent) => void;
|
|
24
|
+
initCanvas: () => void;
|
|
24
25
|
};
|
|
25
26
|
export {};
|
package/dist/hooks/usePanZoom.js
CHANGED
|
@@ -1,134 +1,179 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback, useEffect, useState, useRef } from 'react';
|
|
3
|
-
import { resolutionCanvas, drawCanvas, getMousePointTransform } from '../utils/graphic';
|
|
3
|
+
import { resolutionCanvas, drawCanvas, getMousePointTransform, canvasCenterPoint } from '../utils/graphic';
|
|
4
4
|
import { MouseStatus } from '../enum/common';
|
|
5
|
+
import useResizeObserver from './useResizeObserver';
|
|
5
6
|
export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImageChange = true }) => {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const ZOOM_UNIT = 0.9;
|
|
8
|
+
const MAX_ZOOM = 4;
|
|
9
|
+
const MIN_ZOOM = 0.5;
|
|
10
|
+
const { width, height } = useResizeObserver({ ref: canvasRef });
|
|
11
|
+
const [initZoom, setInitZoom] = useState(0);
|
|
12
|
+
const [zoom, setZoom] = useState(0);
|
|
9
13
|
const [zoomX, setZoomX] = useState(0);
|
|
10
14
|
const [zoomY, setZoomY] = useState(0);
|
|
11
15
|
const [dx, setDx] = useState(0);
|
|
12
16
|
const [dy, setDy] = useState(0);
|
|
13
17
|
const [dw, setDw] = useState(0);
|
|
14
18
|
const [dh, setDh] = useState(0);
|
|
15
|
-
const [
|
|
16
|
-
const [
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
const [moveX, setMoveX] = useState(0);
|
|
20
|
+
const [moveY, setMoveY] = useState(0);
|
|
21
|
+
const [status, setStatus] = useState('');
|
|
22
|
+
const [imageOnloadCount, setImageOnloadCount] = useState(0);
|
|
23
|
+
const [_imageInfo, _setImageInfo] = useState();
|
|
24
|
+
const imageRef = useRef(new Image());
|
|
25
|
+
const calculatorZoomPoint = useCallback((canvasEl, movementX, movementY, dx, dy) => {
|
|
26
|
+
let x = 0;
|
|
27
|
+
let y = 0;
|
|
28
|
+
if (canvasRef.current) {
|
|
29
|
+
x = -dx - movementX + canvasEl.width / 2;
|
|
30
|
+
y = -dy - movementY + canvasEl.height / 2;
|
|
31
|
+
}
|
|
32
|
+
return { x, y };
|
|
33
|
+
}, []);
|
|
34
|
+
const calculateInitZoom = useCallback((image, canvasEl) => {
|
|
35
|
+
if (canvasEl.clientWidth < canvasEl.clientHeight) {
|
|
36
|
+
return canvasEl.clientWidth / canvasEl.clientHeight < image.width / image.height
|
|
37
|
+
? canvasEl.clientWidth / image.width
|
|
38
|
+
: canvasEl.clientHeight / image.height;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return canvasEl.clientWidth / canvasEl.clientHeight > image.width / image.height
|
|
42
|
+
? canvasEl.clientHeight / image.height
|
|
43
|
+
: canvasEl.clientWidth / image.width;
|
|
30
44
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
setDh(dh);
|
|
39
|
-
setZoom(scale);
|
|
40
|
-
setZoomX(0);
|
|
41
|
-
setZoomY(0);
|
|
45
|
+
}, []);
|
|
46
|
+
const init = (canvasEl, image) => {
|
|
47
|
+
const point = canvasCenterPoint(canvasEl, image);
|
|
48
|
+
setDx(point.x);
|
|
49
|
+
setDy(point.y);
|
|
50
|
+
setDw(image.width);
|
|
51
|
+
setDh(image.height);
|
|
42
52
|
setMoveX(0);
|
|
43
53
|
setMoveY(0);
|
|
44
|
-
|
|
54
|
+
const init_zoom = calculateInitZoom(image, canvasEl);
|
|
55
|
+
setZoom(init_zoom);
|
|
56
|
+
setInitZoom(init_zoom);
|
|
57
|
+
const zoomPoint = calculatorZoomPoint(canvasEl, 0, 0, point.x, point.y);
|
|
58
|
+
setZoomX(zoomPoint.x);
|
|
59
|
+
setZoomY(zoomPoint.y);
|
|
60
|
+
};
|
|
61
|
+
const initCanvas = () => {
|
|
62
|
+
if (image) {
|
|
63
|
+
const canvasEl = resolutionCanvas(canvasRef.current);
|
|
64
|
+
if (canvasEl)
|
|
65
|
+
init(canvasEl, imageRef.current);
|
|
66
|
+
setImageOnloadCount((prev) => prev + 1);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
45
69
|
useEffect(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
if (image) {
|
|
71
|
+
imageRef.current.src = image.src;
|
|
72
|
+
imageRef.current.onload = () => {
|
|
73
|
+
if (resetOnImageChange) {
|
|
74
|
+
const canvasEl = resolutionCanvas(canvasRef.current);
|
|
75
|
+
if (canvasEl)
|
|
76
|
+
init(canvasEl, imageRef.current);
|
|
77
|
+
}
|
|
78
|
+
setImageOnloadCount((prev) => prev + 1);
|
|
79
|
+
};
|
|
55
80
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
}, [image]);
|
|
82
|
+
const [startMousePoint, setStartMousePoint] = useState();
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const redraw = () => {
|
|
85
|
+
if (canvasRef.current) {
|
|
86
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(canvasRef.current), imageRef.current, true);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
redraw();
|
|
90
|
+
}, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (image) {
|
|
93
|
+
imageRef.current.src = image.src;
|
|
94
|
+
const canvasEl = resolutionCanvas(canvasRef.current);
|
|
95
|
+
if (canvasEl)
|
|
96
|
+
init(canvasEl, imageRef.current);
|
|
97
|
+
setImageOnloadCount((prev) => prev + 1);
|
|
66
98
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!panZoomable || !canvasRef.current || !image)
|
|
71
|
-
return;
|
|
72
|
-
const rect = canvasRef.current.getBoundingClientRect();
|
|
73
|
-
const mouseX = e.clientX - rect.left;
|
|
74
|
-
const mouseY = e.clientY - rect.top;
|
|
75
|
-
let nextZoom = e.deltaY < 0 ? zoom / ZOOM_UNIT : zoom * ZOOM_UNIT;
|
|
76
|
-
nextZoom = Math.min(Math.max(nextZoom, MIN_ZOOM), MAX_ZOOM);
|
|
77
|
-
if ((zoom <= MIN_ZOOM && e.deltaY > 0) || (zoom >= MAX_ZOOM && e.deltaY < 0))
|
|
99
|
+
}, [width, height]);
|
|
100
|
+
const handleWheel = (event) => {
|
|
101
|
+
if (!panZoomable)
|
|
78
102
|
return;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
103
|
+
if (canvasRef.current) {
|
|
104
|
+
let calc_zoom = event.deltaY < 0 ? (zoom || 1) * (1 / ZOOM_UNIT) : (zoom || 1) * ZOOM_UNIT;
|
|
105
|
+
if (initZoom * MAX_ZOOM < zoom * ZOOM_UNIT)
|
|
106
|
+
calc_zoom = calc_zoom * ZOOM_UNIT;
|
|
107
|
+
if (initZoom * MIN_ZOOM > zoom * ZOOM_UNIT)
|
|
108
|
+
calc_zoom = calc_zoom * (1 / ZOOM_UNIT);
|
|
109
|
+
const canvasEl = canvasRef.current;
|
|
110
|
+
const zoomPoint = calculatorZoomPoint(canvasEl, moveX, moveY, dx, dy);
|
|
111
|
+
setZoomX(zoomPoint.x);
|
|
112
|
+
setZoomY(zoomPoint.y);
|
|
113
|
+
setZoom(calc_zoom);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const handleMouseMove = (event) => {
|
|
117
|
+
if (!panZoomable)
|
|
85
118
|
return;
|
|
119
|
+
if (status === MouseStatus.MOVE) {
|
|
120
|
+
if (canvasRef.current) {
|
|
121
|
+
const canvasEl = canvasRef.current;
|
|
122
|
+
const rect = canvasEl.getBoundingClientRect();
|
|
123
|
+
const mouseX = event.clientX - rect.left;
|
|
124
|
+
const mouseY = event.clientY - rect.top;
|
|
125
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvasEl);
|
|
126
|
+
let x = mouse.x;
|
|
127
|
+
let y = mouse.y;
|
|
128
|
+
if (startMousePoint) {
|
|
129
|
+
x = x - startMousePoint.x;
|
|
130
|
+
y = y - startMousePoint.y;
|
|
131
|
+
}
|
|
132
|
+
setMoveX(moveX + x);
|
|
133
|
+
setMoveY(moveY + y);
|
|
134
|
+
const zoomPoint = calculatorZoomPoint(canvasEl, moveX + x, moveY + y, dx, dy);
|
|
135
|
+
setZoomX(zoomPoint.x);
|
|
136
|
+
setZoomY(zoomPoint.y);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
};
|
|
141
|
+
const handleMouseDown = (event) => {
|
|
142
|
+
if (!panZoomable)
|
|
143
|
+
return false;
|
|
86
144
|
setStatus(MouseStatus.MOVE);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const mouseY = e.clientY - rect.top;
|
|
100
|
-
const transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom, canvasRef.current);
|
|
101
|
-
if (startMousePoint) {
|
|
102
|
-
const deltaX = transformed.x - startMousePoint.x;
|
|
103
|
-
const deltaY = transformed.y - startMousePoint.y;
|
|
104
|
-
setMoveX((prev) => prev + deltaX);
|
|
105
|
-
setMoveY((prev) => prev + deltaY);
|
|
145
|
+
if (canvasRef.current) {
|
|
146
|
+
const canvasEl = canvasRef.current;
|
|
147
|
+
const rect = canvasEl.getBoundingClientRect();
|
|
148
|
+
const mouseX = event.clientX - rect.left;
|
|
149
|
+
const mouseY = event.clientY - rect.top;
|
|
150
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvasEl);
|
|
151
|
+
const x = mouse.x;
|
|
152
|
+
const y = mouse.y;
|
|
153
|
+
setStartMousePoint({
|
|
154
|
+
x: x,
|
|
155
|
+
y: y,
|
|
156
|
+
});
|
|
106
157
|
}
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
const handleMouseUp =
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
};
|
|
160
|
+
const handleMouseUp = (event) => {
|
|
110
161
|
if (!panZoomable)
|
|
111
162
|
return;
|
|
112
163
|
setStatus(MouseStatus.STOP);
|
|
113
|
-
|
|
114
|
-
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
};
|
|
166
|
+
const handleMouseLeave = (event) => {
|
|
115
167
|
if (!panZoomable)
|
|
116
168
|
return;
|
|
117
169
|
setStatus(MouseStatus.STOP);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!canvasRef.current || !image || (dx === 0 && dy === 0 && zoom === 1))
|
|
121
|
-
return;
|
|
122
|
-
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(canvasRef.current), image, true);
|
|
123
|
-
}, [canvasRef, image, moveX, moveY, zoomX, zoomY, zoom, dx, dy]);
|
|
124
|
-
const resetView = useCallback(() => {
|
|
125
|
-
if (image)
|
|
126
|
-
initCanvas(image);
|
|
127
|
-
}, [image, initCanvas]);
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
};
|
|
128
172
|
return {
|
|
129
173
|
moveX,
|
|
130
174
|
moveY,
|
|
131
175
|
zoom,
|
|
176
|
+
initZoom,
|
|
132
177
|
zoomX,
|
|
133
178
|
zoomY,
|
|
134
179
|
dx,
|
|
@@ -140,6 +185,6 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
140
185
|
handleMouseMove,
|
|
141
186
|
handleMouseUp,
|
|
142
187
|
handleMouseLeave,
|
|
143
|
-
|
|
188
|
+
initCanvas,
|
|
144
189
|
};
|
|
145
190
|
};
|