@deepnoid/canvas 0.1.11 → 0.1.13

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.
@@ -0,0 +1,21 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+ import { Coordinate } from '../types/coordinate';
3
+ export type ZoomButtonType = {
4
+ onClick: () => void;
5
+ children: ReactNode;
6
+ };
7
+ type Props = {
8
+ image: string;
9
+ coordinates: Coordinate[];
10
+ panZoomable?: boolean;
11
+ ZoomButton?: ComponentType<{
12
+ onClick: () => void;
13
+ children: ReactNode;
14
+ }>;
15
+ resetOnImageChange?: boolean;
16
+ editable?: boolean;
17
+ onImageLoadError?: (error: Error) => void;
18
+ onImageLoadSuccess?: () => void;
19
+ };
20
+ declare const AnnotatedCanvas: ({ image, coordinates, panZoomable, ZoomButton, resetOnImageChange, editable, onImageLoadError, onImageLoadSuccess, }: Props) => import("react/jsx-runtime").JSX.Element | null;
21
+ export default AnnotatedCanvas;
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef } from 'react';
4
+ import Canvas from './Canvas';
5
+ import { useImageLoader } from '../hooks/useImageLoader';
6
+ import { usePanZoom } from '../hooks/usePanZoom';
7
+ const AnnotatedCanvas = ({ image, coordinates, panZoomable = false, ZoomButton, resetOnImageChange = true, editable = false, onImageLoadError, onImageLoadSuccess, }) => {
8
+ const canvasRef = useRef(null);
9
+ const { image: loadedImage, isLoading } = useImageLoader(image, 10000, onImageLoadSuccess, onImageLoadError);
10
+ const { moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, handleWheel, handleMouseDown, handleMouseMove, handleMouseUp, handleMouseLeave, resetView, } = usePanZoom({
11
+ canvasRef,
12
+ image: loadedImage,
13
+ panZoomable,
14
+ resetOnImageChange,
15
+ });
16
+ if (!loadedImage)
17
+ return null;
18
+ return (_jsx("div", { style: { width: '100%', height: '100%', display: 'flex', flex: 1 }, children: _jsxs("div", { onWheel: handleWheel, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave, style: {
19
+ flex: 1,
20
+ position: 'relative',
21
+ cursor: panZoomable ? 'grab' : 'default',
22
+ }, children: [_jsx("canvas", { ref: canvasRef, style: { position: 'absolute', width: '100%', height: '100%', left: 0, top: 0 } }), !isLoading && (_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 && !isLoading && _jsx(ZoomButton, { onClick: resetView, children: `${Math.round(zoom * 100)}%` })] }) }));
23
+ };
24
+ export default AnnotatedCanvas;
@@ -10,6 +10,7 @@ type Props = {
10
10
  dw: number;
11
11
  dh: number;
12
12
  coordinates?: Coordinate[];
13
+ editable?: boolean;
13
14
  };
14
15
  declare const Canvas: ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }: Props) => import("react/jsx-runtime").JSX.Element;
15
16
  export default Canvas;
@@ -4,23 +4,23 @@ import { drawCanvas, drawRect, resolutionCanvas } from '../utils/graphic';
4
4
  const Canvas = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }) => {
5
5
  const canvas = useRef(null);
6
6
  useEffect(() => {
7
- const _redraw = () => {
7
+ const redraw = () => {
8
8
  const canvas_el = resolutionCanvas(canvas.current);
9
9
  if (canvas_el) {
10
10
  drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvas_el, undefined, true);
11
11
  const context = canvas_el.getContext('2d');
12
12
  if (context && coordinates) {
13
13
  const deep_copy = JSON.parse(JSON.stringify(coordinates));
14
- deep_copy.forEach(coordinate => {
14
+ deep_copy.forEach((coordinate) => {
15
15
  drawRect(context, coordinate);
16
16
  });
17
17
  }
18
18
  }
19
19
  };
20
- _redraw();
21
- window.addEventListener('resize', _redraw);
20
+ redraw();
21
+ window.addEventListener('resize', redraw);
22
22
  return () => {
23
- window.removeEventListener('resize', _redraw);
23
+ window.removeEventListener('resize', redraw);
24
24
  };
25
25
  }, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, coordinates]);
26
26
  return (_jsx("canvas", { ref: canvas, style: {
@@ -28,7 +28,8 @@ const Canvas = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates
28
28
  height: '100%',
29
29
  width: '100%',
30
30
  backgroundColor: 'transparent',
31
- transition: 'all 500ms'
31
+ transition: 'all 500ms',
32
+ willChange: 'transform',
32
33
  } }));
33
34
  };
34
35
  export default Canvas;
@@ -0,0 +1,4 @@
1
+ export declare enum MouseStatus {
2
+ MOVE = "MOVE",
3
+ STOP = "STOP"
4
+ }
@@ -0,0 +1,5 @@
1
+ export var MouseStatus;
2
+ (function (MouseStatus) {
3
+ MouseStatus["MOVE"] = "MOVE";
4
+ MouseStatus["STOP"] = "STOP";
5
+ })(MouseStatus || (MouseStatus = {}));
@@ -0,0 +1,6 @@
1
+ export declare const useImageLoader: (src: string, timeout?: number, onLoadSuccess?: () => void, onLoadError?: (error: Error) => void) => {
2
+ image: HTMLImageElement | null;
3
+ isLoading: boolean;
4
+ error: string | null;
5
+ imageRef: import("react").RefObject<HTMLImageElement>;
6
+ };
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+ import { useEffect, useRef, useState } from 'react';
3
+ export const useImageLoader = (src, timeout = 10000, onLoadSuccess, onLoadError) => {
4
+ const [image, setImage] = useState(null);
5
+ const [isLoading, setIsLoading] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ const imageRef = useRef(new Image());
8
+ useEffect(() => {
9
+ if (!src)
10
+ return;
11
+ setIsLoading(true);
12
+ setError(null);
13
+ const img = new Image();
14
+ img.crossOrigin = 'anonymous';
15
+ const timer = setTimeout(() => {
16
+ const e = new Error(`Image load timeout: ${src}`);
17
+ setError(e.message);
18
+ onLoadError?.(e);
19
+ setIsLoading(false);
20
+ }, timeout);
21
+ img.onload = () => {
22
+ clearTimeout(timer);
23
+ if (img.naturalWidth === 0 || img.naturalHeight === 0) {
24
+ const e = new Error(`Invalid image dimensions: ${src}`);
25
+ setError(e.message);
26
+ onLoadError?.(e);
27
+ setIsLoading(false);
28
+ return;
29
+ }
30
+ imageRef.current = img;
31
+ setImage(img);
32
+ onLoadSuccess?.();
33
+ setIsLoading(false);
34
+ };
35
+ img.onerror = () => {
36
+ clearTimeout(timer);
37
+ const e = new Error(`Failed to load image: ${src}`);
38
+ setError(e.message);
39
+ onLoadError?.(e);
40
+ setIsLoading(false);
41
+ };
42
+ setTimeout(() => {
43
+ img.src = src;
44
+ }, 0);
45
+ return () => clearTimeout(timer);
46
+ }, [src, timeout, onLoadError, onLoadSuccess]);
47
+ return { image, isLoading, error, imageRef };
48
+ };
@@ -0,0 +1,25 @@
1
+ import { RefObject, WheelEvent, MouseEvent } from 'react';
2
+ type UsePanZoomProps = {
3
+ canvasRef: RefObject<HTMLCanvasElement | null>;
4
+ image: HTMLImageElement | null;
5
+ panZoomable?: boolean;
6
+ resetOnImageChange?: boolean;
7
+ };
8
+ export declare const usePanZoom: ({ canvasRef, image, panZoomable, resetOnImageChange }: UsePanZoomProps) => {
9
+ moveX: number;
10
+ moveY: number;
11
+ zoom: number;
12
+ zoomX: number;
13
+ zoomY: number;
14
+ dx: number;
15
+ dy: number;
16
+ dw: number;
17
+ dh: number;
18
+ handleWheel: (e: WheelEvent) => void;
19
+ handleMouseDown: (e: MouseEvent) => void;
20
+ handleMouseMove: (e: MouseEvent) => void;
21
+ handleMouseUp: () => void;
22
+ handleMouseLeave: () => void;
23
+ resetView: () => void;
24
+ };
25
+ export {};
@@ -0,0 +1,115 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { canvasCenterPoint, drawCanvas, getMousePointTransform, resolutionCanvas } from '../utils/graphic';
4
+ import { MouseStatus } from '../enum/common';
5
+ export const usePanZoom = ({ canvasRef, image, panZoomable = false, resetOnImageChange = true }) => {
6
+ const [moveX, setMoveX] = useState(0);
7
+ const [moveY, setMoveY] = useState(0);
8
+ const [zoom, setZoom] = useState(1);
9
+ const [zoomX, setZoomX] = useState(0);
10
+ const [zoomY, setZoomY] = useState(0);
11
+ const [dx, setDx] = useState(0);
12
+ const [dy, setDy] = useState(0);
13
+ const [dw, setDw] = useState(0);
14
+ const [dh, setDh] = useState(0);
15
+ const [status, setStatus] = useState();
16
+ const [startMousePoint, setStartMousePoint] = useState();
17
+ const ZOOM_UNIT = 0.9;
18
+ const MAX_ZOOM = 4;
19
+ const MIN_ZOOM = 0.5;
20
+ const initCanvas = useCallback(() => {
21
+ if (!canvasRef.current || !image)
22
+ return;
23
+ const canvasEl = resolutionCanvas(canvasRef.current);
24
+ const center = canvasCenterPoint(canvasEl, image);
25
+ setDx(center.x);
26
+ setDy(center.y);
27
+ setDw(image.width);
28
+ setDh(image.height);
29
+ setMoveX(0);
30
+ setMoveY(0);
31
+ setZoom(1);
32
+ setZoomX(0);
33
+ setZoomY(0);
34
+ }, [canvasRef, image]);
35
+ useEffect(() => {
36
+ if (resetOnImageChange) {
37
+ initCanvas();
38
+ }
39
+ }, [image, initCanvas, resetOnImageChange]);
40
+ const handleWheel = useCallback((e) => {
41
+ if (!panZoomable || !canvasRef.current || !image)
42
+ return;
43
+ let nextZoom = e.deltaY < 0 ? zoom / ZOOM_UNIT : zoom * ZOOM_UNIT;
44
+ nextZoom = Math.min(Math.max(nextZoom, MIN_ZOOM), MAX_ZOOM);
45
+ const canvasEl = resolutionCanvas(canvasRef.current);
46
+ const zoomPoint = {
47
+ x: -dx - moveX + canvasEl.width / 2,
48
+ y: -dy - moveY + canvasEl.height / 2,
49
+ };
50
+ setZoomX(zoomPoint.x);
51
+ setZoomY(zoomPoint.y);
52
+ setZoom(nextZoom);
53
+ }, [canvasRef, panZoomable, zoom, dx, dy, moveX, moveY, image]);
54
+ const handleMouseDown = useCallback((e) => {
55
+ if (!panZoomable || !canvasRef.current)
56
+ return;
57
+ setStatus(MouseStatus.MOVE);
58
+ const rect = canvasRef.current.getBoundingClientRect();
59
+ const mouseX = e.clientX - rect.left;
60
+ const mouseY = e.clientY - rect.top;
61
+ const transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom, canvasRef.current);
62
+ setStartMousePoint({ x: transformed.x, y: transformed.y });
63
+ e.preventDefault();
64
+ }, [canvasRef, panZoomable, moveX, moveY, zoomX, zoomY, dx, dy, zoom]);
65
+ const handleMouseMove = useCallback((e) => {
66
+ if (!panZoomable || status !== MouseStatus.MOVE || !canvasRef.current)
67
+ return;
68
+ const rect = canvasRef.current.getBoundingClientRect();
69
+ const mouseX = e.clientX - rect.left;
70
+ const mouseY = e.clientY - rect.top;
71
+ const transformed = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom, canvasRef.current);
72
+ if (startMousePoint) {
73
+ const deltaX = transformed.x - startMousePoint.x;
74
+ const deltaY = transformed.y - startMousePoint.y;
75
+ setMoveX((prev) => prev + deltaX);
76
+ setMoveY((prev) => prev + deltaY);
77
+ }
78
+ e.preventDefault();
79
+ }, [canvasRef, panZoomable, status, moveX, moveY, zoomX, zoomY, dx, dy, zoom, startMousePoint]);
80
+ const handleMouseUp = useCallback(() => {
81
+ if (!panZoomable)
82
+ return;
83
+ setStatus(MouseStatus.STOP);
84
+ }, [panZoomable]);
85
+ const handleMouseLeave = useCallback(() => {
86
+ if (!panZoomable)
87
+ return;
88
+ setStatus(MouseStatus.STOP);
89
+ }, [panZoomable]);
90
+ useEffect(() => {
91
+ if (!canvasRef.current || !image)
92
+ return;
93
+ drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(canvasRef.current), image, true);
94
+ }, [canvasRef, image, moveX, moveY, zoomX, zoomY, zoom, dx, dy]);
95
+ const resetView = useCallback(() => {
96
+ initCanvas();
97
+ }, [initCanvas]);
98
+ return {
99
+ moveX,
100
+ moveY,
101
+ zoom,
102
+ zoomX,
103
+ zoomY,
104
+ dx,
105
+ dy,
106
+ dw,
107
+ dh,
108
+ handleWheel,
109
+ handleMouseDown,
110
+ handleMouseMove,
111
+ handleMouseUp,
112
+ handleMouseLeave,
113
+ resetView,
114
+ };
115
+ };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import XrayViewer from './components/XrayViewer';
2
- export { XrayViewer };
3
- export type { ZoomButtonType } from './components/XrayViewer';
1
+ import AnnotatedCanvas from './components/AnnotatedCanvas';
2
+ export { AnnotatedCanvas };
3
+ export type { ZoomButtonType } from './components/AnnotatedCanvas';
4
4
  export type { Rectangle, Coordinate } from './types/coordinate';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import XrayViewer from './components/XrayViewer';
2
- export { XrayViewer };
1
+ import AnnotatedCanvas from './components/AnnotatedCanvas';
2
+ export { AnnotatedCanvas };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnoid/canvas",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",
@@ -1,24 +0,0 @@
1
- import { ComponentType, ReactNode } from 'react';
2
- import { Coordinate } from '../types/coordinate';
3
- export type ZoomButtonType = {
4
- onClick: () => void;
5
- children: ReactNode;
6
- };
7
- type Props = {
8
- image?: {
9
- type: 'TOP' | 'SIDE' | 'REALITY';
10
- imagePath: string;
11
- itemId: number;
12
- } | null;
13
- coordinates?: Coordinate[];
14
- hasZoom?: {
15
- ZoomButton: ComponentType<{
16
- onClick: () => void;
17
- children: ReactNode;
18
- }>;
19
- };
20
- onImageLoadError?: (error: Error) => void;
21
- onImageLoadSuccess?: () => void;
22
- };
23
- declare const XrayViewer: ({ image, hasZoom, coordinates, onImageLoadError, onImageLoadSuccess }: Props) => import("react/jsx-runtime").JSX.Element;
24
- export default XrayViewer;
@@ -1,282 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from 'react';
4
- import useResizeObserver from '../hooks/useResizeObserver';
5
- import { canvasCenterPoint, drawCanvas, getMousePointTransform, resolutionCanvas } from '../utils/graphic';
6
- import Canvas from './Canvas';
7
- const XrayViewer = ({ image, hasZoom, coordinates, onImageLoadError, onImageLoadSuccess }) => {
8
- const wrapper = useRef(null);
9
- const viewer = useRef(null);
10
- const { width, height } = useResizeObserver({ ref: viewer });
11
- const ZOOM_UNIT = 0.9;
12
- const MAX_ZOOM = 4;
13
- const MIN_ZOOM = 0.5;
14
- const IMAGE_LOAD_TIMEOUT = 10000;
15
- const [initZoom, setInitZoom] = useState(0);
16
- const [zoom, setZoom] = useState(0);
17
- const [zoomX, setZoomX] = useState(0);
18
- const [zoomY, setZoomY] = useState(0);
19
- const [dx, setDx] = useState(0);
20
- const [dy, setDy] = useState(0);
21
- const [dw, setDw] = useState(0);
22
- const [dh, setDh] = useState(0);
23
- const [moveX, setMoveX] = useState(0);
24
- const [moveY, setMoveY] = useState(0);
25
- const [status, setStatus] = useState('');
26
- const [imageOnloadCount, setImageOnloadCount] = useState(0);
27
- const [isImageLoading, setIsImageLoading] = useState(false);
28
- const [imageError, setImageError] = useState(null);
29
- const imageRef = useRef(new Image());
30
- const prevImageInfo = useRef(undefined);
31
- const loadTimeoutRef = useRef(0);
32
- const [startMousePoint, setStartMousePoint] = useState();
33
- const loadImage = useCallback((src) => {
34
- return new Promise((resolve, reject) => {
35
- const img = new Image();
36
- img.crossOrigin = 'anonymous';
37
- const timeout = setTimeout(() => {
38
- reject(new Error(`Image load timeout: ${src}`));
39
- }, IMAGE_LOAD_TIMEOUT);
40
- img.onload = () => {
41
- clearTimeout(timeout);
42
- if (img.naturalWidth === 0 || img.naturalHeight === 0) {
43
- reject(new Error(`Invalid image dimensions: ${src}`));
44
- return;
45
- }
46
- resolve(img);
47
- };
48
- img.onerror = () => {
49
- clearTimeout(timeout);
50
- reject(new Error(`Failed to load image: ${src}`));
51
- };
52
- setTimeout(() => {
53
- img.src = src;
54
- }, 0);
55
- });
56
- }, []);
57
- const calculatorZoomPoint = useCallback((canvas_el, movementX, movementY, dx, dy) => {
58
- let x = 0;
59
- let y = 0;
60
- if (viewer.current) {
61
- x = -dx - movementX + canvas_el.width / 2;
62
- y = -dy - movementY + canvas_el.height / 2;
63
- }
64
- return { x, y };
65
- }, []);
66
- const calculateInitZoom = useCallback((image, canvas_el) => {
67
- if (canvas_el.clientWidth < canvas_el.clientHeight) {
68
- return canvas_el.clientWidth / canvas_el.clientHeight < image.width / image.height
69
- ? canvas_el.clientWidth / image.width
70
- : canvas_el.clientHeight / image.height;
71
- }
72
- else {
73
- return canvas_el.clientWidth / canvas_el.clientHeight > image.width / image.height
74
- ? canvas_el.clientHeight / image.height
75
- : canvas_el.clientWidth / image.width;
76
- }
77
- }, []);
78
- const init = useCallback((canvas_el, image) => {
79
- try {
80
- const point = canvasCenterPoint(canvas_el, image);
81
- setDx(point.x);
82
- setDy(point.y);
83
- setDw(image.width);
84
- setDh(image.height);
85
- setMoveX(0);
86
- setMoveY(0);
87
- const init_zoom = calculateInitZoom(image, canvas_el);
88
- setZoom(init_zoom);
89
- setInitZoom(init_zoom);
90
- const zoomPoint = calculatorZoomPoint(canvas_el, 0, 0, point.x, point.y);
91
- setZoomX(zoomPoint.x);
92
- setZoomY(zoomPoint.y);
93
- setImageError(null);
94
- }
95
- catch (error) {
96
- const errorMessage = error instanceof Error ? error.message : 'Canvas initialization failed';
97
- setImageError(errorMessage);
98
- onImageLoadError?.(new Error(errorMessage));
99
- }
100
- }, [calculateInitZoom, calculatorZoomPoint, onImageLoadError]);
101
- const initCanvas = useCallback(async () => {
102
- if (!image)
103
- return;
104
- try {
105
- setIsImageLoading(true);
106
- setImageError(null);
107
- if (loadTimeoutRef.current) {
108
- clearTimeout(loadTimeoutRef.current);
109
- }
110
- const loadedImage = await loadImage(image.imagePath);
111
- imageRef.current = loadedImage;
112
- const canvas_el = resolutionCanvas(viewer.current);
113
- if (canvas_el) {
114
- init(canvas_el, loadedImage);
115
- setImageOnloadCount((prev) => prev + 1);
116
- onImageLoadSuccess?.();
117
- }
118
- }
119
- catch (error) {
120
- const errorMessage = error instanceof Error ? error.message : 'Unknown image load error';
121
- setImageError(errorMessage);
122
- onImageLoadError?.(new Error(errorMessage));
123
- console.error('Image load failed:', error);
124
- }
125
- finally {
126
- setIsImageLoading(false);
127
- }
128
- }, [image, loadImage, init, onImageLoadError, onImageLoadSuccess]);
129
- useEffect(() => {
130
- if (!image)
131
- return;
132
- const imageInfo = `${image.itemId}-${image.type}`;
133
- const loadImageAsync = async () => {
134
- try {
135
- setIsImageLoading(true);
136
- setImageError(null);
137
- const loadedImage = await loadImage(image.imagePath);
138
- imageRef.current = loadedImage;
139
- if (prevImageInfo.current !== imageInfo || image.type === 'REALITY') {
140
- prevImageInfo.current = imageInfo;
141
- const canvas_el = resolutionCanvas(viewer.current);
142
- if (canvas_el) {
143
- init(canvas_el, loadedImage);
144
- }
145
- }
146
- setImageOnloadCount((prev) => prev + 1);
147
- onImageLoadSuccess?.();
148
- }
149
- catch (error) {
150
- const errorMessage = error instanceof Error ? error.message : 'Image load failed';
151
- setImageError(errorMessage);
152
- imageRef.current = new Image();
153
- prevImageInfo.current = undefined;
154
- setZoom(0);
155
- setInitZoom(0);
156
- setMoveX(0);
157
- setMoveY(0);
158
- setDx(0);
159
- setDy(0);
160
- setDw(0);
161
- setDh(0);
162
- if (viewer.current) {
163
- const canvas = viewer.current;
164
- const ctx = canvas.getContext('2d');
165
- if (ctx) {
166
- canvas.width = canvas.offsetWidth;
167
- canvas.height = canvas.offsetHeight;
168
- ctx.clearRect(0, 0, canvas.width, canvas.height);
169
- }
170
- }
171
- setImageOnloadCount((prev) => prev + 1);
172
- onImageLoadError?.(new Error(errorMessage));
173
- console.error('Image load failed:', error);
174
- }
175
- finally {
176
- setIsImageLoading(false);
177
- }
178
- };
179
- loadImageAsync();
180
- return () => {
181
- if (loadTimeoutRef.current) {
182
- clearTimeout(loadTimeoutRef.current);
183
- }
184
- };
185
- }, [image, loadImage, init, onImageLoadError, onImageLoadSuccess]);
186
- useEffect(() => {
187
- const _redraw = () => {
188
- if (viewer.current && imageRef.current && !isImageLoading && !imageError) {
189
- try {
190
- drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(viewer.current), imageRef.current, true);
191
- }
192
- catch {
193
- setImageError('Canvas drawing failed');
194
- }
195
- }
196
- };
197
- _redraw();
198
- }, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount, isImageLoading, imageError]);
199
- useEffect(() => {
200
- if (image && imageRef.current && !isImageLoading) {
201
- const imageInfo = `${image.itemId}-${image.type}`;
202
- if (prevImageInfo.current === imageInfo) {
203
- const canvas_el = resolutionCanvas(viewer.current);
204
- if (canvas_el) {
205
- init(canvas_el, imageRef.current);
206
- setImageOnloadCount((prev) => prev + 1);
207
- }
208
- }
209
- }
210
- }, [width, height, image, init, isImageLoading]);
211
- const handleWheel = (event) => {
212
- if (!hasZoom || isImageLoading || imageError)
213
- return;
214
- if (viewer.current) {
215
- let calc_zoom = event.deltaY < 0 ? (zoom || 1) * (1 / ZOOM_UNIT) : (zoom || 1) * ZOOM_UNIT;
216
- if (initZoom * MAX_ZOOM < zoom * ZOOM_UNIT)
217
- calc_zoom = calc_zoom * ZOOM_UNIT;
218
- if (initZoom * MIN_ZOOM > zoom * ZOOM_UNIT)
219
- calc_zoom = calc_zoom * (1 / ZOOM_UNIT);
220
- const canvas_el = viewer.current;
221
- const zoomPoint = calculatorZoomPoint(canvas_el, moveX, moveY, dx, dy);
222
- setZoomX(zoomPoint.x);
223
- setZoomY(zoomPoint.y);
224
- setZoom(calc_zoom);
225
- }
226
- };
227
- const handleMouseMove = (event) => {
228
- if (!hasZoom || isImageLoading || imageError)
229
- return;
230
- if (status === 'MOVE') {
231
- if (viewer.current) {
232
- const canvas_el = viewer.current;
233
- const rect = canvas_el.getBoundingClientRect();
234
- const mouseX = event.clientX - rect.left;
235
- const mouseY = event.clientY - rect.top;
236
- const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas_el);
237
- let x = mouse.x;
238
- let y = mouse.y;
239
- if (startMousePoint) {
240
- x = x - startMousePoint.x;
241
- y = y - startMousePoint.y;
242
- }
243
- setMoveX(moveX + x);
244
- setMoveY(moveY + y);
245
- const zoomPoint = calculatorZoomPoint(canvas_el, moveX + x, moveY + y, dx, dy);
246
- setZoomX(zoomPoint.x);
247
- setZoomY(zoomPoint.y);
248
- }
249
- }
250
- event.preventDefault();
251
- };
252
- const handleMouseDown = (event) => {
253
- if (isImageLoading || imageError)
254
- return;
255
- setStatus('MOVE');
256
- if (viewer.current) {
257
- const canvas_el = viewer.current;
258
- const rect = canvas_el.getBoundingClientRect();
259
- const mouseX = event.clientX - rect.left;
260
- const mouseY = event.clientY - rect.top;
261
- const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas_el);
262
- setStartMousePoint({ x: mouse.x, y: mouse.y });
263
- }
264
- event.preventDefault();
265
- };
266
- const handleMouseUp = (event) => {
267
- setStatus('STOP');
268
- event.preventDefault();
269
- };
270
- const handleMouseLeave = (event) => {
271
- setStatus('STOP');
272
- event.preventDefault();
273
- };
274
- return (_jsx("div", { style: { width: '100%', height: '100%', display: 'flex', flex: 1 }, children: _jsxs("div", { ref: wrapper, onWheel: handleWheel, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave, style: { flex: 1, position: 'relative' }, children: [_jsx("canvas", { ref: viewer, style: {
275
- position: 'absolute',
276
- width: '100%',
277
- height: '100%',
278
- left: 0,
279
- top: 0,
280
- } }), !isImageLoading && !imageError && (_jsx(Canvas, { moveX: moveX, moveY: moveY, zoomX: zoomX, zoomY: zoomY, zoom: zoom, dx: dx, dy: dy, dw: dw, dh: dh, coordinates: coordinates })), hasZoom && initZoom !== 0 && !isImageLoading && !imageError && (_jsx(hasZoom.ZoomButton, { onClick: initCanvas, children: `${Math.round((zoom / initZoom) * 100)}%` }))] }) }));
281
- };
282
- export default XrayViewer;