@deepnoid/canvas 0.1.26 → 0.1.28

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.
@@ -6,8 +6,9 @@ import { useImageLoader } from '../hooks/useImageLoader';
6
6
  import { usePanZoom } from '../hooks/usePanZoom';
7
7
  const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton, resetOnImageChange = true, editable = false, onImageLoadError, onImageLoadSuccess, }) => {
8
8
  const canvasRef = useRef(null);
9
+ const imageRef = useRef(new Image());
9
10
  const [displayCoordinates, setDisplayCoordinates] = useState();
10
- const { image: loadedImage, isLoading, error, imageRef, } = useImageLoader(image, 10000, onImageLoadSuccess, onImageLoadError);
11
+ const { image: loadedImage, isLoading, error, } = useImageLoader(image, imageRef, 10000, onImageLoadSuccess, onImageLoadError);
11
12
  const { moveX, moveY, zoom, initZoom, zoomX, zoomY, dx, dy, dw, dh, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, initCanvas, } = usePanZoom({
12
13
  canvasRef,
13
14
  imageRef,
@@ -17,7 +18,7 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
17
18
  const loaded = loadedImage && !isLoading;
18
19
  useEffect(() => {
19
20
  if (loaded) {
20
- setDisplayCoordinates(loaded ? coordinates : undefined);
21
+ setDisplayCoordinates(coordinates);
21
22
  }
22
23
  }, [loaded, coordinates]);
23
24
  if (!image || error)
@@ -26,6 +27,6 @@ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton,
26
27
  flex: 1,
27
28
  position: 'relative',
28
29
  cursor: panZoomable ? 'grab' : 'default',
29
- }, 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)}%` }))] })) }) }));
30
+ }, 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 && zoom > 0 && (_jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((zoom / initZoom) * 100)}%` }))] })) }) }));
30
31
  };
31
32
  export default AnnotatedCanvas;
@@ -1,6 +1,6 @@
1
- export declare const useImageLoader: (src: string, timeout?: number, onLoadSuccess?: () => void, onLoadError?: (error: Error) => void) => {
1
+ import { RefObject } from 'react';
2
+ export declare const useImageLoader: (src: string, imageRef: RefObject<HTMLImageElement>, timeout?: number, onLoadSuccess?: () => void, onLoadError?: (error: Error) => void) => {
2
3
  image: HTMLImageElement | null;
3
4
  isLoading: boolean;
4
5
  error: string | null;
5
- imageRef: import("react").RefObject<HTMLImageElement>;
6
6
  };
@@ -1,10 +1,9 @@
1
1
  'use client';
2
- import { useEffect, useRef, useState } from 'react';
3
- export const useImageLoader = (src, timeout = 10000, onLoadSuccess, onLoadError) => {
2
+ import { useEffect, useState } from 'react';
3
+ export const useImageLoader = (src, imageRef, timeout = 10000, onLoadSuccess, onLoadError) => {
4
4
  const [image, setImage] = useState(null);
5
5
  const [isLoading, setIsLoading] = useState(false);
6
6
  const [error, setError] = useState(null);
7
- const imageRef = useRef(new Image());
8
7
  useEffect(() => {
9
8
  if (!src || src.trim() === '') {
10
9
  setImage(null);
@@ -12,46 +11,61 @@ export const useImageLoader = (src, timeout = 10000, onLoadSuccess, onLoadError)
12
11
  setError(null);
13
12
  return;
14
13
  }
14
+ const controller = new AbortController();
15
+ const { signal } = controller;
15
16
  setIsLoading(true);
16
17
  setError(null);
17
18
  setImage(null);
18
19
  const img = new Image();
19
20
  img.crossOrigin = 'anonymous';
20
- const timer = setTimeout(() => {
21
- const e = new Error(`Image load timeout: ${src}`);
22
- setError(e.message);
23
- setImage(null);
24
- onLoadError?.(e);
25
- setIsLoading(false);
26
- }, timeout);
27
- img.onload = () => {
28
- clearTimeout(timer);
29
- if (img.naturalWidth === 0 || img.naturalHeight === 0) {
30
- const e = new Error(`Invalid image dimensions: ${src}`);
21
+ img.src = src;
22
+ const timeoutId = setTimeout(() => {
23
+ if (!signal.aborted) {
24
+ controller.abort();
25
+ const e = new Error(`Image load timeout: ${src}`);
31
26
  setError(e.message);
32
- setImage(null);
27
+ setIsLoading(false);
33
28
  onLoadError?.(e);
29
+ }
30
+ }, timeout);
31
+ const load = async () => {
32
+ try {
33
+ await img.decode();
34
+ if (signal.aborted)
35
+ return;
36
+ if (img.naturalWidth === 0 || img.naturalHeight === 0) {
37
+ throw new Error(`Invalid image dimensions: ${src}`);
38
+ }
39
+ try {
40
+ imageRef.current = img;
41
+ }
42
+ catch { }
43
+ setImage(img);
34
44
  setIsLoading(false);
35
- return;
45
+ setError(null);
46
+ onLoadSuccess?.();
47
+ }
48
+ catch (err) {
49
+ if (signal.aborted)
50
+ return;
51
+ const e = new Error(`Failed to load image: ${src}`);
52
+ setError(e.message);
53
+ setIsLoading(false);
54
+ onLoadError?.(err instanceof Error ? err : e);
55
+ }
56
+ finally {
57
+ clearTimeout(timeoutId);
36
58
  }
37
- imageRef.current = img;
38
- setImage(img);
39
- setError(null);
40
- onLoadSuccess?.();
41
- setIsLoading(false);
42
59
  };
43
- img.onerror = () => {
44
- clearTimeout(timer);
45
- const e = new Error(`Failed to load image: ${src}`);
46
- setError(e.message);
47
- setImage(null);
48
- onLoadError?.(e);
49
- setIsLoading(false);
60
+ load();
61
+ return () => {
62
+ controller.abort();
63
+ clearTimeout(timeoutId);
64
+ try {
65
+ img.src = '';
66
+ }
67
+ catch { }
50
68
  };
51
- setTimeout(() => {
52
- img.src = src;
53
- }, 0);
54
- return () => clearTimeout(timer);
55
- }, [src, timeout, onLoadError, onLoadSuccess]);
56
- return { image, isLoading, error, imageRef };
69
+ }, [src, timeout, onLoadError, onLoadSuccess, imageRef]);
70
+ return { image, isLoading, error };
57
71
  };
@@ -8,7 +8,6 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
8
8
  const MAX_ZOOM = 4;
9
9
  const MIN_ZOOM = 0.5;
10
10
  const { width, height } = useResizeObserver({ ref: canvasRef });
11
- console.log('> width', width, ', height :', height);
12
11
  const [initZoom, setInitZoom] = useState(0);
13
12
  const [zoom, setZoom] = useState(0);
14
13
  const [zoomX, setZoomX] = useState(0);
@@ -55,9 +54,7 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
55
54
  setZoomX(zoomPoint.x);
56
55
  setZoomY(zoomPoint.y);
57
56
  };
58
- console.log('. imageOnloadCount ', imageOnloadCount);
59
57
  const initCanvas = () => {
60
- console.log('> initCanvas !!!!!!!!! imageRef?.current : ', imageRef?.current);
61
58
  if (imageRef?.current) {
62
59
  const canvasEl = resolutionCanvas(canvasRef.current);
63
60
  if (canvasEl)
@@ -132,63 +129,25 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
132
129
  redraw();
133
130
  }, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount, canvasRef, imageRef]);
134
131
  useEffect(() => {
135
- if (!imageRef?.current || !canvasRef?.current)
132
+ if (!imageRef?.current || !canvasRef?.current || !width || !height)
136
133
  return;
137
134
  const canvasEl = resolutionCanvas(canvasRef.current);
138
135
  if (!canvasEl)
139
136
  return;
137
+ const isFirstLoad = imageOnloadCount === 0;
138
+ if (isFirstLoad) {
139
+ init(canvasEl, imageRef.current);
140
+ setImageOnloadCount((prev) => prev + 1);
141
+ return;
142
+ }
140
143
  if (resetOnImageChange) {
141
- if (width && height) {
142
- console.log('> init (resetOnImageChange true)');
143
- init(canvasEl, imageRef.current);
144
- setImageOnloadCount((prev) => prev + 1);
145
- }
144
+ init(canvasEl, imageRef.current);
146
145
  }
147
146
  else {
148
147
  drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvasEl, imageRef.current, true);
149
148
  }
149
+ setImageOnloadCount((prev) => prev + 1);
150
150
  }, [imageRef?.current, width, height, resetOnImageChange]);
151
- useEffect(() => {
152
- if (!imageRef?.current || !canvasRef?.current)
153
- return;
154
- const canvasEl = resolutionCanvas(canvasRef.current);
155
- if (!canvasEl)
156
- return;
157
- if (!resetOnImageChange) {
158
- if (width && height) {
159
- console.log('> init (resetOnImageChange false, width/height ready)');
160
- init(canvasEl, imageRef.current);
161
- setImageOnloadCount((prev) => prev + 1);
162
- }
163
- else {
164
- drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvasEl, imageRef.current, true);
165
- }
166
- }
167
- }, [width, height, resetOnImageChange]);
168
- // useEffect(() => {
169
- // if (!imageRef?.current || !canvasRef?.current || !width || !height) {
170
- // console.log(888888888888888888888);
171
- // return;
172
- // }
173
- // console.log(11111111111111111111111111111);
174
- // if (resetOnImageChange) {
175
- // const canvasEl = resolutionCanvas(canvasRef.current);
176
- // if (canvasEl) init(canvasEl, imageRef.current);
177
- // }
178
- // setImageOnloadCount((prev) => prev + 1);
179
- // }, [imageRef?.current]);
180
- // useEffect(() => {
181
- // if (!imageRef?.current || !canvasRef?.current || !width || !height) {
182
- // console.log(99999999999999999999999);
183
- // return;
184
- // }
185
- // const canvasEl = resolutionCanvas(canvasRef.current);
186
- // if (canvasEl && !resetOnImageChange) {
187
- // console.log(333333333333333333333333333333);
188
- // init(canvasEl, imageRef.current);
189
- // }
190
- // setImageOnloadCount((prev) => prev + 1);
191
- // }, [width, height]);
192
151
  return {
193
152
  moveX,
194
153
  moveY,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnoid/canvas",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",