@deepnoid/canvas 0.1.2 → 0.1.3
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/Canvas.js +34 -0
- package/dist/components/XrayViewer.js +192 -0
- package/dist/constants/graphic.js +1 -0
- package/dist/hooks/useResizeObserver.js +124 -0
- package/dist/index.js +1 -0
- package/dist/types/coordinate.js +1 -0
- package/dist/utils/graphic.js +121 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { drawCanvas, drawRect, resolutionCanvas } from '../utils/graphic';
|
|
4
|
+
const Canvas = ({ moveX, moveY, zoom, zoomX, zoomY, dx, dy, dw, dh, coordinates }) => {
|
|
5
|
+
const canvas = useRef(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const _redraw = () => {
|
|
8
|
+
const canvas_el = resolutionCanvas(canvas.current);
|
|
9
|
+
if (canvas_el) {
|
|
10
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvas_el, undefined, true);
|
|
11
|
+
const context = canvas_el.getContext('2d');
|
|
12
|
+
if (context && coordinates) {
|
|
13
|
+
const deep_copy = JSON.parse(JSON.stringify(coordinates));
|
|
14
|
+
deep_copy.forEach(coordinate => {
|
|
15
|
+
drawRect(context, coordinate);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
_redraw();
|
|
21
|
+
window.addEventListener('resize', _redraw);
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener('resize', _redraw);
|
|
24
|
+
};
|
|
25
|
+
}, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, coordinates]);
|
|
26
|
+
return (_jsx("canvas", { ref: canvas, style: {
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
height: '100%',
|
|
29
|
+
width: '100%',
|
|
30
|
+
backgroundColor: 'transparent',
|
|
31
|
+
transition: 'all 500ms'
|
|
32
|
+
} }));
|
|
33
|
+
};
|
|
34
|
+
export default Canvas;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import useResizeObserver from '../hooks/useResizeObserver';
|
|
4
|
+
import { canvasCenterPoint, drawCanvas, getMousePointTransform, resolutionCanvas } from '../utils/graphic';
|
|
5
|
+
import Canvas from './Canvas';
|
|
6
|
+
const XrayViewer = ({ image, hasZoom, coordinates }) => {
|
|
7
|
+
const wrapper = useRef(null);
|
|
8
|
+
const viewer = useRef(null);
|
|
9
|
+
const { width, height } = useResizeObserver({ ref: viewer });
|
|
10
|
+
const ZOOM_UNIT = 0.9;
|
|
11
|
+
const MAX_ZOOM = 4;
|
|
12
|
+
const MIN_ZOOM = 0.5;
|
|
13
|
+
const [initZoom, setInitZoom] = useState(0);
|
|
14
|
+
const [zoom, setZoom] = useState(0);
|
|
15
|
+
const [zoomX, setZoomX] = useState(0);
|
|
16
|
+
const [zoomY, setZoomY] = useState(0);
|
|
17
|
+
const [dx, setDx] = useState(0);
|
|
18
|
+
const [dy, setDy] = useState(0);
|
|
19
|
+
const [dw, setDw] = useState(0);
|
|
20
|
+
const [dh, setDh] = useState(0);
|
|
21
|
+
const [moveX, setMoveX] = useState(0);
|
|
22
|
+
const [moveY, setMoveY] = useState(0);
|
|
23
|
+
const [status, setStatus] = useState('');
|
|
24
|
+
const [imageOnloadCount, setImageOnloadCount] = useState(0);
|
|
25
|
+
const [_imageInfo, _setImageInfo] = useState();
|
|
26
|
+
const imageRef = useRef(new Image());
|
|
27
|
+
const prevImageInfo = useRef(undefined);
|
|
28
|
+
const calculatorZoomPoint = useCallback((canvas_el, movementX, movementY, dx, dy) => {
|
|
29
|
+
let x = 0;
|
|
30
|
+
let y = 0;
|
|
31
|
+
if (viewer.current) {
|
|
32
|
+
x = -dx - movementX + canvas_el.width / 2;
|
|
33
|
+
y = -dy - movementY + canvas_el.height / 2;
|
|
34
|
+
}
|
|
35
|
+
return { x, y };
|
|
36
|
+
}, []);
|
|
37
|
+
const calculateInitZoom = useCallback((image, canvas_el) => {
|
|
38
|
+
if (canvas_el.clientWidth < canvas_el.clientHeight) {
|
|
39
|
+
return canvas_el.clientWidth / canvas_el.clientHeight < image.width / image.height
|
|
40
|
+
? canvas_el.clientWidth / image.width
|
|
41
|
+
: canvas_el.clientHeight / image.height;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return canvas_el.clientWidth / canvas_el.clientHeight > image.width / image.height
|
|
45
|
+
? canvas_el.clientHeight / image.height
|
|
46
|
+
: canvas_el.clientWidth / image.width;
|
|
47
|
+
}
|
|
48
|
+
}, []);
|
|
49
|
+
const init = (canvas_el, image) => {
|
|
50
|
+
const point = canvasCenterPoint(canvas_el, image);
|
|
51
|
+
setDx(point.x);
|
|
52
|
+
setDy(point.y);
|
|
53
|
+
setDw(image.width);
|
|
54
|
+
setDh(image.height);
|
|
55
|
+
setMoveX(0);
|
|
56
|
+
setMoveY(0);
|
|
57
|
+
const init_zoom = calculateInitZoom(image, canvas_el);
|
|
58
|
+
setZoom(init_zoom);
|
|
59
|
+
setInitZoom(init_zoom);
|
|
60
|
+
const zoomPoint = calculatorZoomPoint(canvas_el, 0, 0, point.x, point.y);
|
|
61
|
+
setZoomX(zoomPoint.x);
|
|
62
|
+
setZoomY(zoomPoint.y);
|
|
63
|
+
};
|
|
64
|
+
const initCanvas = () => {
|
|
65
|
+
if (image) {
|
|
66
|
+
const canvas_el = resolutionCanvas(viewer.current);
|
|
67
|
+
if (canvas_el)
|
|
68
|
+
init(canvas_el, imageRef.current);
|
|
69
|
+
setImageOnloadCount(prev => prev + 1);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (image) {
|
|
74
|
+
imageRef.current.src = image.imagePath;
|
|
75
|
+
const imageInfo = `${image.itemId}-${image.type}`;
|
|
76
|
+
imageRef.current.onload = () => {
|
|
77
|
+
if (prevImageInfo.current !== imageInfo || ['REALITY'].includes(image.type)) {
|
|
78
|
+
prevImageInfo.current = imageInfo;
|
|
79
|
+
const canvas_el = resolutionCanvas(viewer.current);
|
|
80
|
+
if (canvas_el)
|
|
81
|
+
init(canvas_el, imageRef.current);
|
|
82
|
+
}
|
|
83
|
+
setImageOnloadCount(prev => prev + 1);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}, [image]);
|
|
87
|
+
const [startMousePoint, setStartMousePoint] = useState();
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const _redraw = () => {
|
|
90
|
+
if (viewer.current) {
|
|
91
|
+
drawCanvas(moveX, moveY, zoomX, zoomY, zoom, dx, dy, resolutionCanvas(viewer.current), imageRef.current, true);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
_redraw();
|
|
95
|
+
}, [moveX, moveY, zoomX, zoomY, zoom, dx, dy, dw, dh, imageOnloadCount]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (image) {
|
|
98
|
+
imageRef.current.src = image.imagePath;
|
|
99
|
+
const imageInfo = `${image.itemId}-${image.type}`;
|
|
100
|
+
if (prevImageInfo.current === imageInfo) {
|
|
101
|
+
const canvas_el = resolutionCanvas(viewer.current);
|
|
102
|
+
if (canvas_el)
|
|
103
|
+
init(canvas_el, imageRef.current);
|
|
104
|
+
setImageOnloadCount(prev => prev + 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, [width, height]);
|
|
108
|
+
const handleWheel = (event) => {
|
|
109
|
+
if (!hasZoom)
|
|
110
|
+
return;
|
|
111
|
+
if (viewer.current) {
|
|
112
|
+
let calc_zoom = event.deltaY < 0 ? (zoom || 1) * (1 / ZOOM_UNIT) : (zoom || 1) * ZOOM_UNIT;
|
|
113
|
+
if (initZoom * MAX_ZOOM < zoom * ZOOM_UNIT)
|
|
114
|
+
calc_zoom = calc_zoom * ZOOM_UNIT;
|
|
115
|
+
if (initZoom * MIN_ZOOM > zoom * ZOOM_UNIT)
|
|
116
|
+
calc_zoom = calc_zoom * (1 / ZOOM_UNIT);
|
|
117
|
+
const canvas_el = viewer.current;
|
|
118
|
+
const zoomPoint = calculatorZoomPoint(canvas_el, moveX, moveY, dx, dy);
|
|
119
|
+
setZoomX(zoomPoint.x);
|
|
120
|
+
setZoomY(zoomPoint.y);
|
|
121
|
+
setZoom(calc_zoom);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const handleMouseMove = (event) => {
|
|
125
|
+
if (!hasZoom)
|
|
126
|
+
return;
|
|
127
|
+
if (status === 'MOVE') {
|
|
128
|
+
if (viewer.current) {
|
|
129
|
+
const canvas_el = viewer.current;
|
|
130
|
+
const rect = canvas_el.getBoundingClientRect();
|
|
131
|
+
const mouseX = event.clientX - rect.left;
|
|
132
|
+
const mouseY = event.clientY - rect.top;
|
|
133
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas_el);
|
|
134
|
+
let x = mouse.x;
|
|
135
|
+
let y = mouse.y;
|
|
136
|
+
if (startMousePoint) {
|
|
137
|
+
x = x - startMousePoint.x;
|
|
138
|
+
y = y - startMousePoint.y;
|
|
139
|
+
}
|
|
140
|
+
setMoveX(moveX + x);
|
|
141
|
+
setMoveY(moveY + y);
|
|
142
|
+
const zoomPoint = calculatorZoomPoint(canvas_el, moveX + x, moveY + y, dx, dy);
|
|
143
|
+
setZoomX(zoomPoint.x);
|
|
144
|
+
setZoomY(zoomPoint.y);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
};
|
|
149
|
+
const handleMouseDown = (event) => {
|
|
150
|
+
setStatus('MOVE');
|
|
151
|
+
if (viewer.current) {
|
|
152
|
+
const canvas_el = viewer.current;
|
|
153
|
+
const rect = canvas_el.getBoundingClientRect();
|
|
154
|
+
const mouseX = event.clientX - rect.left;
|
|
155
|
+
const mouseY = event.clientY - rect.top;
|
|
156
|
+
const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas_el);
|
|
157
|
+
const x = mouse.x;
|
|
158
|
+
const y = mouse.y;
|
|
159
|
+
setStartMousePoint({
|
|
160
|
+
x: x,
|
|
161
|
+
y: y,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
};
|
|
166
|
+
const handleMouseUp = (event) => {
|
|
167
|
+
setStatus('STOP');
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
};
|
|
170
|
+
const handleMouseLeave = (event) => {
|
|
171
|
+
setStatus('STOP');
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
};
|
|
174
|
+
return (_jsx("div", { style: {
|
|
175
|
+
width: '100%',
|
|
176
|
+
height: '100%',
|
|
177
|
+
display: 'flex',
|
|
178
|
+
flex: 1,
|
|
179
|
+
backgroundColor: '#f5f5f5'
|
|
180
|
+
}, children: _jsxs("div", { ref: wrapper, onWheel: handleWheel, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave, style: {
|
|
181
|
+
flex: 1,
|
|
182
|
+
position: 'relative'
|
|
183
|
+
}, children: [_jsx("canvas", { ref: viewer, style: {
|
|
184
|
+
position: 'absolute',
|
|
185
|
+
width: '100%',
|
|
186
|
+
height: '100%',
|
|
187
|
+
left: 0,
|
|
188
|
+
top: 0,
|
|
189
|
+
transition: 'all 500ms'
|
|
190
|
+
} }), _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 && (_jsx(hasZoom.ZoomButton, { onClick: initCanvas, children: `${Math.round((zoom / initZoom) * 100)}%` }))] }) }));
|
|
191
|
+
};
|
|
192
|
+
export default XrayViewer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DRAW_LINE_SIZE = 4;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
const useResizeObserver = (opts) => {
|
|
3
|
+
const onResize = opts.onResize;
|
|
4
|
+
const onResizeRef = useRef(undefined);
|
|
5
|
+
onResizeRef.current = onResize;
|
|
6
|
+
const round = opts.round || Math.round;
|
|
7
|
+
const resizeObserverRef = useRef({});
|
|
8
|
+
const [size, setSize] = useState({
|
|
9
|
+
width: undefined,
|
|
10
|
+
height: undefined,
|
|
11
|
+
});
|
|
12
|
+
const previous = useRef({
|
|
13
|
+
width: undefined,
|
|
14
|
+
height: undefined,
|
|
15
|
+
});
|
|
16
|
+
const didUnmount = useRef(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
didUnmount.current = false;
|
|
19
|
+
return () => {
|
|
20
|
+
didUnmount.current = true;
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
const refCallback = useResolvedElement(useCallback(element => {
|
|
24
|
+
if (!resizeObserverRef.current ||
|
|
25
|
+
resizeObserverRef.current.box !== opts.box ||
|
|
26
|
+
resizeObserverRef.current.round !== round) {
|
|
27
|
+
resizeObserverRef.current = {
|
|
28
|
+
box: opts.box,
|
|
29
|
+
round,
|
|
30
|
+
instance: new ResizeObserver(entries => {
|
|
31
|
+
const entry = entries[0];
|
|
32
|
+
const boxProp = opts.box === 'border-box'
|
|
33
|
+
? 'borderBoxSize'
|
|
34
|
+
: opts.box === 'device-pixel-content-box'
|
|
35
|
+
? 'devicePixelContentBoxSize'
|
|
36
|
+
: 'contentBoxSize';
|
|
37
|
+
const reportedWidth = extractSize(entry, boxProp, 'inlineSize');
|
|
38
|
+
const reportedHeight = extractSize(entry, boxProp, 'blockSize');
|
|
39
|
+
const newWidth = reportedWidth ? round(reportedWidth) : undefined;
|
|
40
|
+
const newHeight = reportedHeight ? round(reportedHeight) : undefined;
|
|
41
|
+
if (previous.current.width !== newWidth || previous.current.height !== newHeight) {
|
|
42
|
+
const newSize = { width: newWidth, height: newHeight };
|
|
43
|
+
previous.current.width = newWidth;
|
|
44
|
+
previous.current.height = newHeight;
|
|
45
|
+
if (onResizeRef.current) {
|
|
46
|
+
onResizeRef.current(newSize);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (!didUnmount.current) {
|
|
50
|
+
setSize(newSize);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
resizeObserverRef.current.instance?.observe(element, { box: opts.box });
|
|
58
|
+
return () => {
|
|
59
|
+
if (resizeObserverRef.current) {
|
|
60
|
+
resizeObserverRef.current.instance?.unobserve(element);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}, [opts.box, round]), opts.ref);
|
|
64
|
+
return useMemo(() => ({
|
|
65
|
+
ref: refCallback,
|
|
66
|
+
width: size.width,
|
|
67
|
+
height: size.height,
|
|
68
|
+
}), [refCallback, size.width, size.height]);
|
|
69
|
+
};
|
|
70
|
+
export default useResizeObserver;
|
|
71
|
+
function useResolvedElement(subscriber, refOrElement) {
|
|
72
|
+
const lastReportRef = useRef(null);
|
|
73
|
+
const refOrElementRef = useRef(null);
|
|
74
|
+
refOrElementRef.current = refOrElement;
|
|
75
|
+
const cbElementRef = useRef(null);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
evaluateSubscription();
|
|
78
|
+
});
|
|
79
|
+
const evaluateSubscription = useCallback(() => {
|
|
80
|
+
const cbElement = cbElementRef.current;
|
|
81
|
+
const refOrElement = refOrElementRef.current;
|
|
82
|
+
const element = cbElement
|
|
83
|
+
? cbElement
|
|
84
|
+
: refOrElement
|
|
85
|
+
? refOrElement instanceof Element
|
|
86
|
+
? refOrElement
|
|
87
|
+
: refOrElement.current
|
|
88
|
+
: null;
|
|
89
|
+
if (lastReportRef.current &&
|
|
90
|
+
lastReportRef.current.element === element &&
|
|
91
|
+
lastReportRef.current.subscriber === subscriber) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (lastReportRef.current && lastReportRef.current.cleanup) {
|
|
95
|
+
lastReportRef.current.cleanup();
|
|
96
|
+
}
|
|
97
|
+
lastReportRef.current = {
|
|
98
|
+
element,
|
|
99
|
+
subscriber,
|
|
100
|
+
cleanup: element ? subscriber(element) : undefined,
|
|
101
|
+
};
|
|
102
|
+
}, [subscriber]);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
return () => {
|
|
105
|
+
if (lastReportRef.current && lastReportRef.current.cleanup) {
|
|
106
|
+
lastReportRef.current.cleanup();
|
|
107
|
+
lastReportRef.current = null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}, []);
|
|
111
|
+
return useCallback(element => {
|
|
112
|
+
cbElementRef.current = element;
|
|
113
|
+
evaluateSubscription();
|
|
114
|
+
}, [evaluateSubscription]);
|
|
115
|
+
}
|
|
116
|
+
function extractSize(entry, boxProp, sizeType) {
|
|
117
|
+
const box = Array.isArray(entry[boxProp])
|
|
118
|
+
? entry[boxProp][0]
|
|
119
|
+
: undefined;
|
|
120
|
+
if (!box) {
|
|
121
|
+
return entry.contentRect[sizeType === 'inlineSize' ? 'width' : 'height'];
|
|
122
|
+
}
|
|
123
|
+
return box[sizeType];
|
|
124
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as XrayViewer } from './components/XrayViewer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { DRAW_LINE_SIZE } from '../constants/graphic';
|
|
2
|
+
// [캔버스] 중앙포인트
|
|
3
|
+
export const canvasCenterPoint = (canvas, image) => {
|
|
4
|
+
const centerX = canvas.width / 2 - image.width / 2;
|
|
5
|
+
const centerY = canvas.height / 2 - image.height / 2;
|
|
6
|
+
return { x: centerX, y: centerY };
|
|
7
|
+
};
|
|
8
|
+
// [캔버스] 그리기
|
|
9
|
+
export const drawCanvas = (moveX, moveY, zoomX, zoomY, zoom, dx, dy, canvas_el, image, isClear) => {
|
|
10
|
+
if (canvas_el) {
|
|
11
|
+
const context = canvas_el.getContext('2d');
|
|
12
|
+
if (isClear)
|
|
13
|
+
context?.clearRect(0, 0, canvas_el.width, canvas_el.height);
|
|
14
|
+
const resolution_ratio_x = canvas_el.width / canvas_el.clientWidth;
|
|
15
|
+
const resolution_ratio_y = canvas_el.height / canvas_el.clientHeight;
|
|
16
|
+
context?.translate(dx * resolution_ratio_x, dy * resolution_ratio_y);
|
|
17
|
+
context?.translate(moveX * resolution_ratio_x, moveY * resolution_ratio_y);
|
|
18
|
+
context?.translate(zoomX, zoomY);
|
|
19
|
+
context?.scale(zoom, zoom);
|
|
20
|
+
context?.translate(-zoomX, -zoomY);
|
|
21
|
+
try {
|
|
22
|
+
if (image)
|
|
23
|
+
context?.drawImage(image, 0, 0, image.width, image.height);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error('Failed to draw image', error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export const drawRect = (context, coordinate) => {
|
|
31
|
+
setRectangleStyle(context, coordinate);
|
|
32
|
+
const { x, y, width, height } = coordinate;
|
|
33
|
+
context.lineWidth = DRAW_LINE_SIZE;
|
|
34
|
+
context.strokeRect(x, y, width, height);
|
|
35
|
+
};
|
|
36
|
+
export const setRectangleStyle = (context, coordinate) => {
|
|
37
|
+
if (coordinate.color === 'danger') {
|
|
38
|
+
context.strokeStyle = 'rgba(255, 70, 132, 1)';
|
|
39
|
+
}
|
|
40
|
+
if (coordinate.color === 'warning') {
|
|
41
|
+
context.strokeStyle = 'rgba(237, 164, 16, 1)';
|
|
42
|
+
}
|
|
43
|
+
if (coordinate.color === 'success') {
|
|
44
|
+
context.strokeStyle = 'rgba(36, 162, 91, 1)';
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
// [캔버스] 해상도
|
|
48
|
+
export const resolutionCanvas = (canvas, width, height) => {
|
|
49
|
+
if (canvas) {
|
|
50
|
+
const canvas_el = canvas;
|
|
51
|
+
canvas_el.width = width ? width : canvas_el.clientWidth;
|
|
52
|
+
canvas_el.height = height ? height : canvas_el.clientHeight;
|
|
53
|
+
return canvas_el;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// [좌표 변형] 원점
|
|
60
|
+
export const __origin = (mouse, origin) => {
|
|
61
|
+
const x = mouse.x - origin.x;
|
|
62
|
+
const y = mouse.y - origin.y;
|
|
63
|
+
return {
|
|
64
|
+
x,
|
|
65
|
+
y,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
// [좌표 변형] 이동
|
|
69
|
+
export const __move = (mouse, move) => {
|
|
70
|
+
const x = mouse.x - move.x;
|
|
71
|
+
const y = mouse.y - move.y;
|
|
72
|
+
return {
|
|
73
|
+
x,
|
|
74
|
+
y,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
// [좌표 변형] 확대, 축소
|
|
78
|
+
export const __zoom = (mouse, zoomPoint, zoom, canvas_el) => {
|
|
79
|
+
const resolution_ratio_x = canvas_el.width / canvas_el.clientWidth;
|
|
80
|
+
const resolution_ratio_y = canvas_el.height / canvas_el.clientHeight;
|
|
81
|
+
let x = mouse.x * (1 / zoom) * resolution_ratio_x;
|
|
82
|
+
let y = mouse.y * (1 / zoom) * resolution_ratio_y;
|
|
83
|
+
x = x + zoomPoint.x;
|
|
84
|
+
y = y + zoomPoint.y;
|
|
85
|
+
x = x - zoomPoint.x * (1 / zoom);
|
|
86
|
+
y = y - zoomPoint.y * (1 / zoom);
|
|
87
|
+
return {
|
|
88
|
+
x,
|
|
89
|
+
y,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
// [좌표 변형] 회전
|
|
93
|
+
export const __rotate = (mouse, rotate, center) => {
|
|
94
|
+
const rad = (rotate * Math.PI) / 180; // Convert degree to radian
|
|
95
|
+
const cos = Math.cos(rad);
|
|
96
|
+
const sin = Math.sin(rad);
|
|
97
|
+
const x = cos * (mouse.x - center.x) - sin * (mouse.y - center.y) + center.x;
|
|
98
|
+
const y = sin * (mouse.x - center.x) + cos * (mouse.y - center.y) + center.y;
|
|
99
|
+
return { x, y };
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* [좌표 변형] 마우스 포인트 형태 변형
|
|
103
|
+
* 실행 순서 중요(graphic draw 순서와 동일)
|
|
104
|
+
*
|
|
105
|
+
* @param mousePoint: Point 현재 마우스 좌표
|
|
106
|
+
* @param movePoint: Point 이동 좌표
|
|
107
|
+
* @param zoomPoint: Point 확대 좌표
|
|
108
|
+
* @param originPoint: Point 원점 좌표
|
|
109
|
+
* @param zoom: number 확대 비율
|
|
110
|
+
* @param canvas_el: 캔버스 엘리먼트
|
|
111
|
+
* @param rotate: number 회전 각도 0~360
|
|
112
|
+
* @returns 캔버스 크기 해상도에 맞춰 변형된 마우스 좌표
|
|
113
|
+
*/
|
|
114
|
+
export const getMousePointTransform = (mousePoint, movePoint, zoomPoint, originPoint, zoom, canvas_el, rotate = 0) => {
|
|
115
|
+
let mouse = mousePoint;
|
|
116
|
+
mouse = __rotate(mouse, rotate, { x: canvas_el.width / 2, y: canvas_el.height / 2 });
|
|
117
|
+
mouse = __origin(mouse, originPoint);
|
|
118
|
+
mouse = __move(mouse, movePoint);
|
|
119
|
+
mouse = __zoom(mouse, zoomPoint, zoom, canvas_el);
|
|
120
|
+
return mouse;
|
|
121
|
+
};
|