Feat: 캔버스 폴리곤 그리기 기능 구현
This commit is contained in:
parent
e7a70d303c
commit
28a7e91390
@ -1,7 +1,7 @@
|
||||
import useCanvasStore from '@/stores/useCanvasStore';
|
||||
import Konva from 'konva';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Image, Layer, Rect, Stage } from 'react-konva';
|
||||
import { Circle, Image, Layer, Line, Rect, Stage } from 'react-konva';
|
||||
import useImage from 'use-image';
|
||||
import { Label } from '@/types';
|
||||
import LabelRect from './LabelRect';
|
||||
@ -47,7 +47,9 @@ export default function ImageCanvas() {
|
||||
const [selectedId, setSelectedId] = useState<number | null>(null);
|
||||
const [image, imageStatus] = useImage(imageUrl);
|
||||
const [rectPoints, setRectPoints] = useState<[number, number][]>([]);
|
||||
const [polygonPoints, setPolygonPoints] = useState<[number, number][]>([]);
|
||||
const drawState = useCanvasStore((state) => state.drawState);
|
||||
const setDrawState = useCanvasStore((state) => state.setDrawState);
|
||||
const addLabel = useCanvasStore((state) => state.addLabel);
|
||||
const startDrawRect = () => {
|
||||
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
|
||||
@ -56,6 +58,59 @@ export default function ImageCanvas() {
|
||||
[x, y],
|
||||
]);
|
||||
};
|
||||
const addPointToPolygon = () => {
|
||||
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
|
||||
if (polygonPoints.length === 0) {
|
||||
setPolygonPoints([
|
||||
[x, y],
|
||||
[x, y],
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const diff = Math.max(Math.abs(x - polygonPoints[0][0]), Math.abs(y - polygonPoints[0][1]));
|
||||
|
||||
if (diff === 0) return;
|
||||
|
||||
const scale = stageRef.current!.getAbsoluteScale().x;
|
||||
const clickedFirstPoint = polygonPoints.length > 1 && diff * scale < 5;
|
||||
|
||||
if (clickedFirstPoint) {
|
||||
endDrawPolygon();
|
||||
return;
|
||||
}
|
||||
setPolygonPoints([...polygonPoints, [x, y]]);
|
||||
};
|
||||
const removeLastPointOfPolygon = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (polygonPoints.length === 0) return;
|
||||
setPolygonPoints(polygonPoints.slice(0, -1));
|
||||
};
|
||||
const moveLastPointOfPolygon = () => {
|
||||
if (polygonPoints.length < 2) return;
|
||||
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
|
||||
setPolygonPoints([...polygonPoints.slice(0, -1), [x, y]]);
|
||||
};
|
||||
const endDrawPolygon = () => {
|
||||
if (drawState !== 'pen' || polygonPoints.length === 0) return;
|
||||
setDrawState('pointer');
|
||||
setPolygonPoints([]);
|
||||
if (polygonPoints.length < 4) return;
|
||||
|
||||
const color = Math.floor(Math.random() * 65535)
|
||||
.toString(16)
|
||||
.padStart(6, '0');
|
||||
const id = labels.length;
|
||||
addLabel({
|
||||
id: id,
|
||||
name: 'label',
|
||||
type: 'polygon',
|
||||
color: `#${color}`,
|
||||
coordinates: polygonPoints.slice(0, -1),
|
||||
});
|
||||
setDrawState('pointer');
|
||||
setSelectedId(id);
|
||||
};
|
||||
const updateDrawingRect = () => {
|
||||
if (rectPoints.length === 0) return;
|
||||
|
||||
@ -71,23 +126,32 @@ export default function ImageCanvas() {
|
||||
setRectPoints([]);
|
||||
const color = Math.floor(Math.random() * 65535)
|
||||
.toString(16)
|
||||
.padStart(4, '0');
|
||||
.padStart(6, '0');
|
||||
const id = labels.length;
|
||||
addLabel({
|
||||
id: labels.length,
|
||||
id: id,
|
||||
name: 'label',
|
||||
type: 'rect',
|
||||
color: `#${color}ff`,
|
||||
color: `#${color}`,
|
||||
coordinates: rectPoints,
|
||||
});
|
||||
setDrawState('pointer');
|
||||
setSelectedId(id);
|
||||
};
|
||||
const handleClick = (e: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||
const isLeftMouseClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 0;
|
||||
e.evt.preventDefault();
|
||||
e.evt.stopPropagation();
|
||||
const isLeftClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 0;
|
||||
const isRightClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 2;
|
||||
|
||||
if (drawState !== 'pointer' && isLeftMouseClicked) {
|
||||
if (drawState !== 'pointer' && (isLeftClicked || isRightClicked)) {
|
||||
stageRef.current?.stopDrag();
|
||||
if (drawState === 'rect') {
|
||||
startDrawRect();
|
||||
}
|
||||
if (drawState === 'pen') {
|
||||
isRightClicked ? removeLastPointOfPolygon(e.evt as MouseEvent) : addPointToPolygon();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (e.target === e.target.getStage() || e.target.getClassName() === 'Image') {
|
||||
@ -98,6 +162,9 @@ export default function ImageCanvas() {
|
||||
if (drawState === 'rect' && rectPoints.length) {
|
||||
updateDrawingRect();
|
||||
}
|
||||
if (drawState === 'pen' && polygonPoints.length) {
|
||||
moveLastPointOfPolygon();
|
||||
}
|
||||
};
|
||||
const handleZoom = (e: Konva.KonvaEventObject<WheelEvent>) => {
|
||||
const scaleBy = 1.05;
|
||||
@ -172,6 +239,7 @@ export default function ImageCanvas() {
|
||||
onMouseUp={endDrawRect}
|
||||
onTouchEnd={endDrawRect}
|
||||
scale={getScale()}
|
||||
onContextMenu={(e) => e.evt.preventDefault()}
|
||||
>
|
||||
<Layer>
|
||||
<Image image={image} />
|
||||
@ -203,15 +271,47 @@ export default function ImageCanvas() {
|
||||
y={rectPoints[0][1]}
|
||||
width={rectPoints[1][0] - rectPoints[0][0]}
|
||||
height={rectPoints[1][1] - rectPoints[0][1]}
|
||||
stroke={'#ff0000'}
|
||||
stroke={'#00a1ff'}
|
||||
strokeWidth={1}
|
||||
strokeScaleEnabled={false}
|
||||
fillAfterStrokeEnabled={false}
|
||||
fill="#ff000033"
|
||||
fill="#00a1ff33"
|
||||
shadowForStrokeEnabled={false}
|
||||
listening={false}
|
||||
/>
|
||||
) : null}
|
||||
{polygonPoints.length ? (
|
||||
<>
|
||||
<Line
|
||||
points={polygonPoints.flat()}
|
||||
stroke={'#00a1ff'}
|
||||
strokeWidth={1}
|
||||
strokeScaleEnabled={false}
|
||||
fill="#00a1ff33"
|
||||
closed
|
||||
listening={false}
|
||||
/>
|
||||
{polygonPoints.map((point, index) => (
|
||||
<Circle
|
||||
key={index}
|
||||
x={point[0]}
|
||||
y={point[1]}
|
||||
radius={5}
|
||||
stroke="#00a1ff"
|
||||
strokeWidth={1}
|
||||
fill="white"
|
||||
strokeScaleEnabled={false}
|
||||
listening={false}
|
||||
scale={{
|
||||
x: 1 / stageRef.current!.getAbsoluteScale().x,
|
||||
y: 1 / stageRef.current!.getAbsoluteScale().y,
|
||||
}}
|
||||
perfectDrawEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</Layer>
|
||||
|
||||
<Layer ref={dragLayerRef} />
|
||||
|
Loading…
Reference in New Issue
Block a user