@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.
@@ -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
- if (!panZoomable || !canvasRef.current || !image)
109
+ const canvasEl = canvasRef.current;
110
+ if (!panZoomable || !canvasRef.current || !canvasEl || !image)
71
111
  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;
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 ((zoom <= MIN_ZOOM && e.deltaY > 0) || (zoom >= MAX_ZOOM && e.deltaY < 0))
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
- setZoomX(mouseX);
80
- setZoomY(mouseY);
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, zoom, image]);
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 transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom, canvasRef.current);
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, moveX, moveY, zoomX, zoomY, dx, dy, zoom]);
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 transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom, canvasRef.current);
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
- setMoveX((prev) => prev + deltaX);
105
- setMoveY((prev) => prev + deltaY);
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, moveX, moveY, zoomX, zoomY, dx, dy, zoom, startMousePoint]);
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 || (dx === 0 && dy === 0 && zoom === 1))
177
+ if (!canvasRef.current || !image)
121
178
  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]);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnoid/canvas",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",