@deepnoid/canvas 0.1.29 → 0.1.30
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/AnnotatedCanvas.d.ts +3 -2
- package/dist/components/AnnotatedCanvas.js +105 -13
- package/dist/hooks/usePanZoom.d.ts +7 -4
- package/dist/hooks/usePanZoom.js +51 -65
- package/package.json +1 -1
- package/dist/hooks/useImageLoader.d.ts +0 -6
- package/dist/hooks/useImageLoader.js +0 -71
|
@@ -14,8 +14,9 @@ type Props = {
|
|
|
14
14
|
}>;
|
|
15
15
|
resetOnImageChange?: boolean;
|
|
16
16
|
editable?: boolean;
|
|
17
|
-
|
|
17
|
+
timeout?: number;
|
|
18
18
|
onImageLoadSuccess?: () => void;
|
|
19
|
+
onImageLoadError?: (error: Error) => void;
|
|
19
20
|
};
|
|
20
|
-
declare const AnnotatedCanvas: ({ image, coordinates, panZoomable, ZoomButton, resetOnImageChange, editable,
|
|
21
|
+
declare const AnnotatedCanvas: ({ image, coordinates, panZoomable, ZoomButton, resetOnImageChange, editable, timeout, onImageLoadSuccess, onImageLoadError, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
21
22
|
export default AnnotatedCanvas;
|
|
@@ -2,31 +2,123 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import Canvas from './Canvas';
|
|
5
|
-
import { useImageLoader } from '../hooks/useImageLoader';
|
|
6
5
|
import { usePanZoom } from '../hooks/usePanZoom';
|
|
7
|
-
|
|
6
|
+
import useResizeObserver from '../hooks/useResizeObserver';
|
|
7
|
+
import { drawCanvas, resolutionCanvas } from '../utils/graphic';
|
|
8
|
+
const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton, resetOnImageChange = true, editable = false, timeout = 10000, onImageLoadSuccess, onImageLoadError, }) => {
|
|
8
9
|
const canvasRef = useRef(null);
|
|
9
10
|
const imageRef = useRef(new Image());
|
|
10
|
-
const [
|
|
11
|
-
const
|
|
12
|
-
const {
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
const [isError, setIsError] = useState(false);
|
|
13
|
+
const { width, height } = useResizeObserver({ ref: canvasRef });
|
|
14
|
+
const { moveX, moveY, zoom, initZoom, zoomX, zoomY, dx, dy, dw, dh, imageOnloadCount, setImageOnloadCount, init, initCanvas, clearCanvas, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, } = usePanZoom({
|
|
13
15
|
canvasRef,
|
|
14
16
|
imageRef,
|
|
15
17
|
panZoomable,
|
|
16
|
-
resetOnImageChange,
|
|
17
18
|
});
|
|
18
|
-
const loaded = loadedImage && !isLoading;
|
|
19
19
|
useEffect(() => {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const redraw = () => {
|
|
21
|
+
if (canvasRef.current) {
|
|
22
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(canvasRef.current), imageRef.current, true);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
redraw();
|
|
26
|
+
}, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount]);
|
|
27
|
+
// --------
|
|
28
|
+
// useEffect(() => {
|
|
29
|
+
// if (!imageRef?.current || !canvasRef?.current || !width || !height) return;
|
|
30
|
+
// const canvasEl = resolutionCanvas(canvasRef.current);
|
|
31
|
+
// if (!canvasEl) return;
|
|
32
|
+
// const isFirstLoad = imageOnloadCount === 0;
|
|
33
|
+
// if (isFirstLoad) {
|
|
34
|
+
// init(canvasEl, imageRef.current);
|
|
35
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
36
|
+
// return;
|
|
37
|
+
// }
|
|
38
|
+
// if (resetOnImageChange) {
|
|
39
|
+
// init(canvasEl, imageRef.current);
|
|
40
|
+
// } else {
|
|
41
|
+
// drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvasEl, imageRef.current, true);
|
|
42
|
+
// }
|
|
43
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
44
|
+
// }, [imageRef?.current, width, height, resetOnImageChange]);
|
|
45
|
+
// ----------
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!image || image.trim() === '') {
|
|
48
|
+
setIsLoading(false);
|
|
49
|
+
setIsError(false);
|
|
50
|
+
clearCanvas();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
setIsLoading(true);
|
|
54
|
+
imageRef.current.src = image;
|
|
55
|
+
imageRef.current.onload = () => {
|
|
56
|
+
console.log('> ======================= useEffect onload ============================');
|
|
57
|
+
setIsLoading(false);
|
|
58
|
+
setIsError(false);
|
|
59
|
+
onImageLoadSuccess?.();
|
|
60
|
+
// const canvas = resolutionCanvas(canvasRef.current);
|
|
61
|
+
// if (canvas) init(canvas, imageRef.current);
|
|
62
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
63
|
+
console.log('> canvasRef.current : ', canvasRef.current);
|
|
64
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
65
|
+
console.log(' > canvas :', canvas);
|
|
66
|
+
if (!canvas)
|
|
67
|
+
return;
|
|
68
|
+
if (resetOnImageChange)
|
|
69
|
+
init(canvas, imageRef.current);
|
|
70
|
+
setImageOnloadCount((prev) => prev + 1);
|
|
71
|
+
// if (prevImageRef.current !== image) {
|
|
72
|
+
// // prevImageRef.current = image;
|
|
73
|
+
// prevImageRef.current = imageRef.current.src;
|
|
74
|
+
// const canvas = resolutionCanvas(canvasRef.current);
|
|
75
|
+
// const isFirstLoad = imageOnloadCount === 0;
|
|
76
|
+
// console.log('> onload - isFirstLoad : ', isFirstLoad);
|
|
77
|
+
// // if (canvas && isFirstLoad) {
|
|
78
|
+
// // init(canvas, imageRef.current);
|
|
79
|
+
// // setImageOnloadCount((prev) => prev + 1);
|
|
80
|
+
// // }
|
|
81
|
+
// if (!canvas) return;
|
|
82
|
+
// if (isFirstLoad) {
|
|
83
|
+
// init(canvas, imageRef.current);
|
|
84
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
85
|
+
// return;
|
|
86
|
+
// }
|
|
87
|
+
// console.log('> !!!!!!!!!!!!!!!! resetOnImageChange : ', resetOnImageChange);
|
|
88
|
+
// if (resetOnImageChange) {
|
|
89
|
+
// init(canvas, imageRef.current);
|
|
90
|
+
// } else {
|
|
91
|
+
// // drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvas, imageRef.current, true);
|
|
92
|
+
// }
|
|
93
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
94
|
+
// // if (canvas) init(canvas, imageRef.current);
|
|
95
|
+
// }
|
|
96
|
+
// setImageOnloadCount((prev) => prev + 1);
|
|
97
|
+
};
|
|
98
|
+
imageRef.current.onerror = () => {
|
|
99
|
+
setIsLoading(false);
|
|
100
|
+
setIsError(true);
|
|
101
|
+
const err = new Error(`Failed to load image: ${image}`);
|
|
102
|
+
onImageLoadError?.(err);
|
|
103
|
+
clearCanvas();
|
|
104
|
+
};
|
|
105
|
+
}, [image, canvasRef.current]);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
console.log('> ======================= useEffect width, height !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
108
|
+
if (image || !isError) {
|
|
109
|
+
imageRef.current.src = image;
|
|
110
|
+
if (!resetOnImageChange) {
|
|
111
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
112
|
+
if (canvas)
|
|
113
|
+
init(canvas, imageRef.current);
|
|
114
|
+
setImageOnloadCount((prev) => prev + 1);
|
|
115
|
+
}
|
|
22
116
|
}
|
|
23
|
-
}, [
|
|
24
|
-
if (!image || error)
|
|
25
|
-
return null;
|
|
117
|
+
}, [width, height]);
|
|
26
118
|
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: {
|
|
27
119
|
flex: 1,
|
|
28
120
|
position: 'relative',
|
|
29
121
|
cursor: panZoomable ? 'grab' : 'default',
|
|
30
|
-
}, children:
|
|
122
|
+
}, children: imageRef.current.src && !isLoading && !isError && (_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: coordinates, editable: editable }), ZoomButton && _jsx(ZoomButton, { onClick: initCanvas, children: `${Math.round((zoom / initZoom) * 100)}%` })] })) }) }));
|
|
31
123
|
};
|
|
32
124
|
export default AnnotatedCanvas;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { RefObject, WheelEvent, MouseEvent } from 'react';
|
|
2
2
|
type UsePanZoomProps = {
|
|
3
3
|
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
4
|
-
imageRef: RefObject<HTMLImageElement
|
|
4
|
+
imageRef: RefObject<HTMLImageElement>;
|
|
5
5
|
panZoomable?: boolean;
|
|
6
|
-
resetOnImageChange?: boolean;
|
|
7
6
|
};
|
|
8
|
-
export declare
|
|
7
|
+
export declare function usePanZoom({ canvasRef, imageRef, panZoomable }: UsePanZoomProps): {
|
|
9
8
|
moveX: number;
|
|
10
9
|
moveY: number;
|
|
11
10
|
zoom: number;
|
|
@@ -16,11 +15,15 @@ export declare const usePanZoom: ({ canvasRef, imageRef, panZoomable, resetOnIma
|
|
|
16
15
|
dy: number;
|
|
17
16
|
dw: number;
|
|
18
17
|
dh: number;
|
|
18
|
+
imageOnloadCount: number;
|
|
19
|
+
setImageOnloadCount: import("react").Dispatch<import("react").SetStateAction<number>>;
|
|
20
|
+
init: (canvas: HTMLCanvasElement, image: HTMLImageElement) => void;
|
|
21
|
+
initCanvas: () => void;
|
|
22
|
+
clearCanvas: () => void;
|
|
19
23
|
handleWheel: (event: WheelEvent) => void;
|
|
20
24
|
handleMouseDown: (event: MouseEvent) => void;
|
|
21
25
|
handleMouseMove: (event: MouseEvent) => void;
|
|
22
26
|
handleMouseUp: (event: MouseEvent) => void;
|
|
23
27
|
handleMouseLeave: (event: MouseEvent) => void;
|
|
24
|
-
initCanvas: () => void;
|
|
25
28
|
};
|
|
26
29
|
export {};
|
package/dist/hooks/usePanZoom.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useCallback,
|
|
3
|
-
import { resolutionCanvas,
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
import { resolutionCanvas, getMousePointTransform, canvasCenterPoint } from '../utils/graphic';
|
|
4
4
|
import { MouseStatus } from '../enum/common';
|
|
5
|
-
|
|
6
|
-
export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnImageChange = true, }) => {
|
|
5
|
+
export function usePanZoom({ canvasRef, imageRef, panZoomable = false }) {
|
|
7
6
|
const ZOOM_UNIT = 0.9;
|
|
8
7
|
const MAX_ZOOM = 4;
|
|
9
8
|
const MIN_ZOOM = 0.5;
|
|
10
|
-
const { width, height } = useResizeObserver({ ref: canvasRef });
|
|
11
9
|
const [initZoom, setInitZoom] = useState(0);
|
|
12
10
|
const [zoom, setZoom] = useState(0);
|
|
13
11
|
const [zoomX, setZoomX] = useState(0);
|
|
@@ -21,49 +19,61 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
|
|
|
21
19
|
const [status, setStatus] = useState('');
|
|
22
20
|
const [imageOnloadCount, setImageOnloadCount] = useState(0);
|
|
23
21
|
const [startMousePoint, setStartMousePoint] = useState();
|
|
24
|
-
const calculatorZoomPoint = useCallback((
|
|
22
|
+
const calculatorZoomPoint = useCallback((canvas, movementX, movementY, dx, dy) => {
|
|
25
23
|
let x = 0;
|
|
26
24
|
let y = 0;
|
|
27
|
-
if (
|
|
28
|
-
x = -dx - movementX +
|
|
29
|
-
y = -dy - movementY +
|
|
25
|
+
if (canvas) {
|
|
26
|
+
x = -dx - movementX + canvas.width / 2;
|
|
27
|
+
y = -dy - movementY + canvas.height / 2;
|
|
30
28
|
}
|
|
31
29
|
return { x, y };
|
|
32
|
-
}, [
|
|
33
|
-
const calculateInitZoom = useCallback((
|
|
34
|
-
if (!image)
|
|
30
|
+
}, []);
|
|
31
|
+
const calculateInitZoom = useCallback((canvas, image) => {
|
|
32
|
+
if (!canvas || !image)
|
|
35
33
|
return 1;
|
|
36
|
-
const canvasRatio =
|
|
34
|
+
const canvasRatio = canvas.clientWidth / canvas.clientHeight;
|
|
37
35
|
const imageRatio = image.naturalWidth / image.naturalHeight;
|
|
38
36
|
return canvasRatio < imageRatio
|
|
39
|
-
?
|
|
40
|
-
:
|
|
37
|
+
? canvas.clientWidth / image.naturalWidth
|
|
38
|
+
: canvas.clientHeight / image.naturalHeight;
|
|
41
39
|
}, []);
|
|
42
|
-
const init = (
|
|
43
|
-
|
|
40
|
+
const init = (canvas, image) => {
|
|
41
|
+
if (!canvas || !image)
|
|
42
|
+
return;
|
|
43
|
+
const point = canvasCenterPoint(canvas, image);
|
|
44
44
|
setDx(point.x);
|
|
45
45
|
setDy(point.y);
|
|
46
46
|
setDw(image.width);
|
|
47
47
|
setDh(image.height);
|
|
48
48
|
setMoveX(0);
|
|
49
49
|
setMoveY(0);
|
|
50
|
-
const init_zoom = calculateInitZoom(
|
|
50
|
+
const init_zoom = calculateInitZoom(canvas, image);
|
|
51
51
|
setZoom(init_zoom);
|
|
52
52
|
setInitZoom(init_zoom);
|
|
53
|
-
const zoomPoint = calculatorZoomPoint(
|
|
53
|
+
const zoomPoint = calculatorZoomPoint(canvas, 0, 0, point.x, point.y);
|
|
54
54
|
setZoomX(zoomPoint.x);
|
|
55
55
|
setZoomY(zoomPoint.y);
|
|
56
56
|
};
|
|
57
57
|
const initCanvas = () => {
|
|
58
|
-
if (imageRef
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (!canvasRef.current || !imageRef.current)
|
|
59
|
+
return;
|
|
60
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
61
|
+
if (canvas) {
|
|
62
|
+
init(canvas, imageRef.current);
|
|
62
63
|
setImageOnloadCount((prev) => prev + 1);
|
|
63
64
|
}
|
|
64
65
|
};
|
|
66
|
+
const clearCanvas = () => {
|
|
67
|
+
if (!canvasRef.current)
|
|
68
|
+
return;
|
|
69
|
+
const canvas = resolutionCanvas(canvasRef.current);
|
|
70
|
+
if (canvas) {
|
|
71
|
+
const ctx = canvas.getContext('2d');
|
|
72
|
+
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
65
75
|
const handleWheel = (event) => {
|
|
66
|
-
if (!panZoomable || !canvasRef.current
|
|
76
|
+
if (!panZoomable || !canvasRef.current)
|
|
67
77
|
return;
|
|
68
78
|
let calc_zoom = event.deltaY < 0 ? (zoom || 1) * (1 / ZOOM_UNIT) : (zoom || 1) * ZOOM_UNIT;
|
|
69
79
|
if (initZoom * MAX_ZOOM < zoom * ZOOM_UNIT)
|
|
@@ -76,13 +86,13 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
|
|
|
76
86
|
setZoom(calc_zoom);
|
|
77
87
|
};
|
|
78
88
|
const handleMouseMove = (event) => {
|
|
79
|
-
if (!panZoomable || status !== MouseStatus.MOVE
|
|
89
|
+
if (!panZoomable || !canvasRef.current || status !== MouseStatus.MOVE)
|
|
80
90
|
return;
|
|
81
|
-
const
|
|
82
|
-
const rect =
|
|
91
|
+
const canvas = canvasRef.current;
|
|
92
|
+
const rect = canvas.getBoundingClientRect();
|
|
83
93
|
const mouseX = event.clientX - rect.left;
|
|
84
94
|
const mouseY = event.clientY - rect.top;
|
|
85
|
-
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1,
|
|
95
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas);
|
|
86
96
|
let x = mouse.x;
|
|
87
97
|
let y = mouse.y;
|
|
88
98
|
if (startMousePoint) {
|
|
@@ -91,63 +101,35 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
|
|
|
91
101
|
}
|
|
92
102
|
setMoveX(moveX + x);
|
|
93
103
|
setMoveY(moveY + y);
|
|
94
|
-
const zoomPoint = calculatorZoomPoint(
|
|
104
|
+
const zoomPoint = calculatorZoomPoint(canvas, moveX + x, moveY + y, dx, dy);
|
|
95
105
|
setZoomX(zoomPoint.x);
|
|
96
106
|
setZoomY(zoomPoint.y);
|
|
97
107
|
event.preventDefault();
|
|
98
108
|
};
|
|
99
109
|
const handleMouseDown = (event) => {
|
|
100
|
-
if (!panZoomable || !canvasRef.current
|
|
110
|
+
if (!panZoomable || !canvasRef.current)
|
|
101
111
|
return;
|
|
102
112
|
setStatus(MouseStatus.MOVE);
|
|
103
|
-
const
|
|
104
|
-
const rect =
|
|
113
|
+
const canvas = canvasRef.current;
|
|
114
|
+
const rect = canvas.getBoundingClientRect();
|
|
105
115
|
const mouseX = event.clientX - rect.left;
|
|
106
116
|
const mouseY = event.clientY - rect.top;
|
|
107
|
-
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1,
|
|
117
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas);
|
|
108
118
|
setStartMousePoint({ x: mouse.x, y: mouse.y });
|
|
109
119
|
event.preventDefault();
|
|
110
120
|
};
|
|
111
121
|
const handleMouseUp = (event) => {
|
|
112
|
-
if (!panZoomable)
|
|
122
|
+
if (!panZoomable || !canvasRef.current)
|
|
113
123
|
return;
|
|
114
124
|
setStatus(MouseStatus.STOP);
|
|
115
125
|
event.preventDefault();
|
|
116
126
|
};
|
|
117
127
|
const handleMouseLeave = (event) => {
|
|
118
|
-
if (!panZoomable)
|
|
128
|
+
if (!panZoomable || !canvasRef.current)
|
|
119
129
|
return;
|
|
120
130
|
setStatus(MouseStatus.STOP);
|
|
121
131
|
event.preventDefault();
|
|
122
132
|
};
|
|
123
|
-
useEffect(() => {
|
|
124
|
-
const redraw = () => {
|
|
125
|
-
if (canvasRef.current && imageRef?.current) {
|
|
126
|
-
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(canvasRef.current), imageRef.current, true);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
redraw();
|
|
130
|
-
}, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount, canvasRef, imageRef]);
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
if (!imageRef?.current || !canvasRef?.current || !width || !height)
|
|
133
|
-
return;
|
|
134
|
-
const canvasEl = resolutionCanvas(canvasRef.current);
|
|
135
|
-
if (!canvasEl)
|
|
136
|
-
return;
|
|
137
|
-
const isFirstLoad = imageOnloadCount === 0;
|
|
138
|
-
if (isFirstLoad) {
|
|
139
|
-
init(canvasEl, imageRef.current);
|
|
140
|
-
setImageOnloadCount((prev) => prev + 1);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (resetOnImageChange) {
|
|
144
|
-
init(canvasEl, imageRef.current);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvasEl, imageRef.current, true);
|
|
148
|
-
}
|
|
149
|
-
setImageOnloadCount((prev) => prev + 1);
|
|
150
|
-
}, [imageRef?.current, width, height, resetOnImageChange]);
|
|
151
133
|
return {
|
|
152
134
|
moveX,
|
|
153
135
|
moveY,
|
|
@@ -159,11 +141,15 @@ export const usePanZoom = ({ canvasRef, imageRef, panZoomable = false, resetOnIm
|
|
|
159
141
|
dy,
|
|
160
142
|
dw,
|
|
161
143
|
dh,
|
|
144
|
+
imageOnloadCount,
|
|
145
|
+
setImageOnloadCount,
|
|
146
|
+
init,
|
|
147
|
+
initCanvas,
|
|
148
|
+
clearCanvas,
|
|
162
149
|
handleWheel,
|
|
163
150
|
handleMouseDown,
|
|
164
151
|
handleMouseMove,
|
|
165
152
|
handleMouseUp,
|
|
166
153
|
handleMouseLeave,
|
|
167
|
-
initCanvas,
|
|
168
154
|
};
|
|
169
|
-
}
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react';
|
|
2
|
-
export declare const useImageLoader: (src: string, imageRef: RefObject<HTMLImageElement>, timeout?: number, onLoadSuccess?: () => void, onLoadError?: (error: Error) => void) => {
|
|
3
|
-
image: HTMLImageElement | null;
|
|
4
|
-
isLoading: boolean;
|
|
5
|
-
error: string | null;
|
|
6
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
export const useImageLoader = (src, imageRef, timeout = 10000, onLoadSuccess, onLoadError) => {
|
|
4
|
-
const [image, setImage] = useState(null);
|
|
5
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
-
const [error, setError] = useState(null);
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (!src || src.trim() === '') {
|
|
9
|
-
setImage(null);
|
|
10
|
-
setIsLoading(false);
|
|
11
|
-
setError(null);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
if (imageRef.current && imageRef.current.src === src) {
|
|
15
|
-
setImage(imageRef.current);
|
|
16
|
-
setIsLoading(false);
|
|
17
|
-
setError(null);
|
|
18
|
-
onLoadSuccess?.();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
setIsLoading(true);
|
|
22
|
-
setError(null);
|
|
23
|
-
setImage(null);
|
|
24
|
-
const controller = new AbortController();
|
|
25
|
-
const { signal } = controller;
|
|
26
|
-
const img = new Image();
|
|
27
|
-
img.crossOrigin = 'anonymous';
|
|
28
|
-
img.src = src;
|
|
29
|
-
const timeoutId = setTimeout(() => {
|
|
30
|
-
if (!signal.aborted) {
|
|
31
|
-
controller.abort();
|
|
32
|
-
const e = new Error(`Image load timeout: ${src}`);
|
|
33
|
-
setError(e.message);
|
|
34
|
-
setIsLoading(false);
|
|
35
|
-
onLoadError?.(e);
|
|
36
|
-
}
|
|
37
|
-
}, timeout);
|
|
38
|
-
const handleLoad = () => {
|
|
39
|
-
if (signal.aborted)
|
|
40
|
-
return;
|
|
41
|
-
clearTimeout(timeoutId);
|
|
42
|
-
imageRef.current = img;
|
|
43
|
-
setImage(img);
|
|
44
|
-
setIsLoading(false);
|
|
45
|
-
setError(null);
|
|
46
|
-
onLoadSuccess?.();
|
|
47
|
-
};
|
|
48
|
-
const handleError = (e) => {
|
|
49
|
-
if (signal.aborted)
|
|
50
|
-
return;
|
|
51
|
-
clearTimeout(timeoutId);
|
|
52
|
-
const err = e instanceof Error ? e : new Error(`Failed to load image: ${src}`);
|
|
53
|
-
setError(err.message);
|
|
54
|
-
setIsLoading(false);
|
|
55
|
-
onLoadError?.(err);
|
|
56
|
-
};
|
|
57
|
-
img.onload = handleLoad;
|
|
58
|
-
img.onerror = handleError;
|
|
59
|
-
return () => {
|
|
60
|
-
clearTimeout(timeoutId);
|
|
61
|
-
controller.abort();
|
|
62
|
-
img.onload = null;
|
|
63
|
-
img.onerror = null;
|
|
64
|
-
try {
|
|
65
|
-
img.src = '';
|
|
66
|
-
}
|
|
67
|
-
catch { }
|
|
68
|
-
};
|
|
69
|
-
}, [src, imageRef, timeout, onLoadSuccess, onLoadError]);
|
|
70
|
-
return { image, isLoading, error };
|
|
71
|
-
};
|