@deepnoid/canvas 0.1.19 → 0.1.20
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/hooks/usePanZoom.js +95 -18
- package/package.json +1 -1
package/dist/hooks/usePanZoom.js
CHANGED
|
@@ -14,10 +14,38 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
14
14
|
const [dh, setDh] = useState(0);
|
|
15
15
|
const [status, setStatus] = useState();
|
|
16
16
|
const [startMousePoint, setStartMousePoint] = useState();
|
|
17
|
+
const animationFrameRef = useRef(null);
|
|
18
|
+
const currentTransformRef = useRef({
|
|
19
|
+
moveX: 0,
|
|
20
|
+
moveY: 0,
|
|
21
|
+
zoom: 1,
|
|
22
|
+
zoomX: 0,
|
|
23
|
+
zoomY: 0,
|
|
24
|
+
dx: 0,
|
|
25
|
+
dy: 0,
|
|
26
|
+
});
|
|
17
27
|
const ZOOM_UNIT = 0.9;
|
|
18
28
|
const MAX_ZOOM = 4;
|
|
19
29
|
const MIN_ZOOM = 0.5;
|
|
20
30
|
const prevImageRef = useRef(null);
|
|
31
|
+
const immediateRender = useCallback((moveX, moveY, zoomX, zoomY, zoom, dx, dy) => {
|
|
32
|
+
const canvasEl = canvasRef.current;
|
|
33
|
+
if (!canvasEl || !image)
|
|
34
|
+
return;
|
|
35
|
+
const resolvedCanvas = resolutionCanvas(canvasEl);
|
|
36
|
+
if (!resolvedCanvas)
|
|
37
|
+
return;
|
|
38
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolvedCanvas, image, true);
|
|
39
|
+
}, [canvasRef, image]);
|
|
40
|
+
const scheduleRender = useCallback(() => {
|
|
41
|
+
if (animationFrameRef.current) {
|
|
42
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
43
|
+
}
|
|
44
|
+
animationFrameRef.current = requestAnimationFrame(() => {
|
|
45
|
+
const current = currentTransformRef.current;
|
|
46
|
+
immediateRender(current.moveX, current.moveY, current.zoomX, current.zoomY, current.zoom, current.dx, current.dy);
|
|
47
|
+
});
|
|
48
|
+
}, [immediateRender]);
|
|
21
49
|
const initCanvas = useCallback((img) => {
|
|
22
50
|
const canvasEl = canvasRef.current;
|
|
23
51
|
if (!canvasEl)
|
|
@@ -41,6 +69,15 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
41
69
|
setZoomY(0);
|
|
42
70
|
setMoveX(0);
|
|
43
71
|
setMoveY(0);
|
|
72
|
+
currentTransformRef.current = {
|
|
73
|
+
moveX: 0,
|
|
74
|
+
moveY: 0,
|
|
75
|
+
zoom: scale,
|
|
76
|
+
zoomX: 0,
|
|
77
|
+
zoomY: 0,
|
|
78
|
+
dx,
|
|
79
|
+
dy,
|
|
80
|
+
};
|
|
44
81
|
}, [canvasRef]);
|
|
45
82
|
useEffect(() => {
|
|
46
83
|
const canvasEl = canvasRef.current;
|
|
@@ -63,23 +100,36 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
63
100
|
setDh(dh);
|
|
64
101
|
setDx(dx);
|
|
65
102
|
setDy(dy);
|
|
103
|
+
currentTransformRef.current.dx = dx;
|
|
104
|
+
currentTransformRef.current.dy = dy;
|
|
66
105
|
}
|
|
67
106
|
prevImageRef.current = image;
|
|
68
107
|
}, [image, resetOnImageChange, initCanvas, canvasRef]);
|
|
69
108
|
const handleWheel = useCallback((e) => {
|
|
70
|
-
|
|
109
|
+
const canvasEl = canvasRef.current;
|
|
110
|
+
if (!panZoomable || !canvasRef.current || !canvasEl || !image)
|
|
71
111
|
return;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
const mouseY = e.clientY - rect.top;
|
|
75
|
-
let nextZoom = e.deltaY < 0 ? zoom / ZOOM_UNIT : zoom * ZOOM_UNIT;
|
|
112
|
+
const currentZoom = currentTransformRef.current.zoom;
|
|
113
|
+
let nextZoom = e.deltaY < 0 ? currentZoom / ZOOM_UNIT : currentZoom * ZOOM_UNIT;
|
|
76
114
|
nextZoom = Math.min(Math.max(nextZoom, MIN_ZOOM), MAX_ZOOM);
|
|
77
|
-
if ((
|
|
115
|
+
if ((currentZoom <= MIN_ZOOM && e.deltaY > 0) || (currentZoom >= MAX_ZOOM && e.deltaY < 0))
|
|
116
|
+
return;
|
|
117
|
+
const resolvedCanvas = resolutionCanvas(canvasEl);
|
|
118
|
+
if (!resolvedCanvas)
|
|
78
119
|
return;
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
const resolution_ratio_x = resolvedCanvas.width / canvasEl.clientWidth;
|
|
121
|
+
const resolution_ratio_y = resolvedCanvas.height / canvasEl.clientHeight;
|
|
122
|
+
const current = currentTransformRef.current;
|
|
123
|
+
const centerX = resolvedCanvas.width / 2 - current.dx * resolution_ratio_x - current.moveX * resolution_ratio_x;
|
|
124
|
+
const centerY = resolvedCanvas.height / 2 - current.dy * resolution_ratio_y - current.moveY * resolution_ratio_y;
|
|
125
|
+
currentTransformRef.current.zoom = nextZoom;
|
|
126
|
+
currentTransformRef.current.zoomX = centerX;
|
|
127
|
+
currentTransformRef.current.zoomY = centerY;
|
|
128
|
+
immediateRender(current.moveX, current.moveY, centerX, centerY, nextZoom, current.dx, current.dy);
|
|
129
|
+
setZoomX(centerX);
|
|
130
|
+
setZoomY(centerY);
|
|
81
131
|
setZoom(nextZoom);
|
|
82
|
-
}, [canvasRef, panZoomable,
|
|
132
|
+
}, [canvasRef, panZoomable, image, immediateRender]);
|
|
83
133
|
const handleMouseDown = useCallback((e) => {
|
|
84
134
|
if (!panZoomable || !canvasRef.current)
|
|
85
135
|
return;
|
|
@@ -87,25 +137,32 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
87
137
|
const rect = canvasRef.current.getBoundingClientRect();
|
|
88
138
|
const mouseX = e.clientX - rect.left;
|
|
89
139
|
const mouseY = e.clientY - rect.top;
|
|
90
|
-
const
|
|
140
|
+
const current = currentTransformRef.current;
|
|
141
|
+
const transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: current.moveX, y: current.moveY }, { x: current.zoomX, y: current.zoomY }, { x: current.dx, y: current.dy }, current.zoom, canvasRef.current);
|
|
91
142
|
setStartMousePoint({ x: transformed.x, y: transformed.y });
|
|
92
143
|
e.preventDefault();
|
|
93
|
-
}, [canvasRef, panZoomable
|
|
144
|
+
}, [canvasRef, panZoomable]);
|
|
94
145
|
const handleMouseMove = useCallback((e) => {
|
|
95
146
|
if (!panZoomable || status !== MouseStatus.MOVE || !canvasRef.current)
|
|
96
147
|
return;
|
|
97
148
|
const rect = canvasRef.current.getBoundingClientRect();
|
|
98
149
|
const mouseX = e.clientX - rect.left;
|
|
99
150
|
const mouseY = e.clientY - rect.top;
|
|
100
|
-
const
|
|
151
|
+
const current = currentTransformRef.current;
|
|
152
|
+
const transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: current.moveX, y: current.moveY }, { x: current.zoomX, y: current.zoomY }, { x: current.dx, y: current.dy }, current.zoom, canvasRef.current);
|
|
101
153
|
if (startMousePoint) {
|
|
102
154
|
const deltaX = transformed.x - startMousePoint.x;
|
|
103
155
|
const deltaY = transformed.y - startMousePoint.y;
|
|
104
|
-
|
|
105
|
-
|
|
156
|
+
const newMoveX = current.moveX + deltaX;
|
|
157
|
+
const newMoveY = current.moveY + deltaY;
|
|
158
|
+
currentTransformRef.current.moveX = newMoveX;
|
|
159
|
+
currentTransformRef.current.moveY = newMoveY;
|
|
160
|
+
immediateRender(newMoveX, newMoveY, current.zoomX, current.zoomY, current.zoom, current.dx, current.dy);
|
|
161
|
+
setMoveX(newMoveX);
|
|
162
|
+
setMoveY(newMoveY);
|
|
106
163
|
}
|
|
107
164
|
e.preventDefault();
|
|
108
|
-
}, [canvasRef, panZoomable, status,
|
|
165
|
+
}, [canvasRef, panZoomable, status, startMousePoint, immediateRender]);
|
|
109
166
|
const handleMouseUp = useCallback(() => {
|
|
110
167
|
if (!panZoomable)
|
|
111
168
|
return;
|
|
@@ -117,14 +174,34 @@ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImage
|
|
|
117
174
|
setStatus(MouseStatus.STOP);
|
|
118
175
|
}, [panZoomable]);
|
|
119
176
|
useEffect(() => {
|
|
120
|
-
if (!canvasRef.current || !image
|
|
177
|
+
if (!canvasRef.current || !image)
|
|
121
178
|
return;
|
|
122
|
-
|
|
123
|
-
|
|
179
|
+
if (dx !== 0 || dy !== 0 || zoom !== 1) {
|
|
180
|
+
scheduleRender();
|
|
181
|
+
}
|
|
182
|
+
}, [canvasRef, image, moveX, moveY, zoomX, zoomY, zoom, dx, dy, scheduleRender]);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
currentTransformRef.current = {
|
|
185
|
+
moveX,
|
|
186
|
+
moveY,
|
|
187
|
+
zoom,
|
|
188
|
+
zoomX,
|
|
189
|
+
zoomY,
|
|
190
|
+
dx,
|
|
191
|
+
dy,
|
|
192
|
+
};
|
|
193
|
+
}, [moveX, moveY, zoom, zoomX, zoomY, dx, dy]);
|
|
124
194
|
const resetView = useCallback(() => {
|
|
125
195
|
if (image)
|
|
126
196
|
initCanvas(image);
|
|
127
197
|
}, [image, initCanvas]);
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
return () => {
|
|
200
|
+
if (animationFrameRef.current) {
|
|
201
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}, []);
|
|
128
205
|
return {
|
|
129
206
|
moveX,
|
|
130
207
|
moveY,
|