Merge branch 'fe/feat/canvas-overflow-click' into 'fe/develop'

Feat: 캔버스에 도형 겹칠 때 anchor 선택 가능하도록 변경

See merge request s11-s-project/S11P21S002!40
This commit is contained in:
홍창기 2024-09-04 13:15:57 +09:00
commit 89026be422
5 changed files with 74 additions and 44 deletions

View File

@ -0,0 +1,10 @@
import { Stage } from 'konva/lib/Stage';
import { useEffect } from 'react';
export default function CanvasControlBar({ stage }: { stage: Stage }) {
useEffect(() => {
console.log(stage);
}, [stage]);
return <div>CanvasControlBar</div>;
}

View File

@ -1,7 +1,7 @@
import { Label } from '@/types';
import Konva from 'konva';
import { useRef, useState } from 'react';
import { Group, Line } from 'react-konva';
import { useState } from 'react';
import { Line } from 'react-konva';
import PolygonTransformer from './PolygonTransformer';
export default function LabelPolygon({
@ -9,26 +9,25 @@ export default function LabelPolygon({
onSelect,
info,
stage,
dragLayer,
}: {
isSelected: boolean;
onSelect: () => void;
info: Label;
stage: Konva.Stage;
dragLayer: Konva.Layer;
}) {
const polyRef = useRef<Konva.Line>(null);
const [coordinates, setCoordinates] = useState<Array<[number, number]>>(info.coordinates);
return (
<Group zIndex={isSelected ? 2 : 1}>
<>
<Line
points={coordinates.flat()}
stroke={info.color}
strokeWidth={1}
ref={polyRef}
onMouseDown={onSelect}
onTouchStart={onSelect}
strokeScaleEnabled={false}
fillAfterStrokeEnabled={false}
fill={`${info.color}33`}
closed
/>
@ -37,8 +36,9 @@ export default function LabelPolygon({
coordinates={coordinates}
setCoordinates={setCoordinates}
stage={stage}
dragLayer={dragLayer}
/>
)}
</Group>
</>
);
}

View File

@ -1,16 +1,18 @@
import { Label } from '@/types';
import Konva from 'konva';
import { useEffect, useRef } from 'react';
import { Group, Line, Transformer } from 'react-konva';
import { Line, Transformer } from 'react-konva';
export default function LabelRect({
isSelected,
onSelect,
info,
dragLayer,
}: {
isSelected: boolean;
onSelect: () => void;
onSelect: (evt: Konva.KonvaEventObject<TouchEvent | MouseEvent>) => void;
info: Label;
dragLayer: Konva.Layer;
}) {
const rectRef = useRef<Konva.Line>(null);
const trRef = useRef<Konva.Transformer>(null);
@ -20,6 +22,11 @@ export default function LabelRect({
info.coordinates[1],
[info.coordinates[1][0], info.coordinates[0][1]],
].flat();
const handleSelect = (evt: Konva.KonvaEventObject<TouchEvent | MouseEvent>): void => {
onSelect(evt);
rectRef.current?.moveToTop();
trRef.current?.moveToTop();
};
const handleMoveEnd = () => {
const rectPoints = rectRef.current?.points();
const points = [
@ -33,23 +40,25 @@ export default function LabelRect({
useEffect(() => {
if (isSelected) {
trRef.current?.nodes([rectRef.current as Konva.Node]);
trRef.current?.moveTo(dragLayer);
trRef.current?.getLayer()?.batchDraw();
}
}, [isSelected]);
}, [dragLayer, isSelected]);
return (
<Group zIndex={isSelected ? 2 : 1}>
<>
<Line
points={coordinates}
stroke={info.color}
strokeWidth={1}
ref={rectRef}
onMouseDown={onSelect}
onTouchStart={onSelect}
onMouseDown={handleSelect}
onTouchStart={handleSelect}
strokeScaleEnabled={false}
fillAfterStrokeEnabled={false}
fill={`${info.color}33`}
onDragEnd={handleMoveEnd}
shadowForStrokeEnabled={false}
closed
draggable
/>
@ -66,6 +75,6 @@ export default function LabelRect({
onTransformEnd={handleMoveEnd}
/>
)}
</Group>
</>
);
}

View File

@ -6,6 +6,7 @@ interface PolygonTransformerProps {
coordinates: Array<[number, number]>;
setCoordinates: (coordinates: Array<[number, number]>) => void;
stage: Konva.Stage;
dragLayer: Konva.Layer;
}
const TRANSFORM_CHANGE_STR = [
@ -21,8 +22,8 @@ const TRANSFORM_CHANGE_STR = [
'strokeWidthChange',
];
export default function PolygonTransformer({ coordinates, setCoordinates, stage }: PolygonTransformerProps) {
const trRef = useRef<Konva.Group>(null);
export default function PolygonTransformer({ coordinates, setCoordinates, stage, dragLayer }: PolygonTransformerProps) {
const anchorsRef = useRef<Konva.Group>(null);
const handleDragMove = (index: number) => (e: Konva.KonvaEventObject<DragEvent>) => {
const circle = e.target as Konva.Circle;
const pos = circle.position();
@ -47,13 +48,13 @@ export default function PolygonTransformer({ coordinates, setCoordinates, stage
useEffect(() => {
const transformEvents = TRANSFORM_CHANGE_STR.join(' ');
anchorsRef.current?.moveTo(dragLayer);
stage.on(transformEvents, () => {
if (!trRef.current) return;
if (!anchorsRef.current) return;
const [line, ...anchors] = trRef.current!.children as Konva.Shape[];
const anchors = anchorsRef.current!.children as Konva.Shape[];
line.strokeWidth(1 / stage.getAbsoluteScale().x);
anchors.forEach((anchor) => {
anchor.scale({
x: 1 / stage.getAbsoluteScale().x,
@ -65,38 +66,44 @@ export default function PolygonTransformer({ coordinates, setCoordinates, stage
return () => {
stage.off(transformEvents);
};
}, [stage]);
}, [dragLayer, stage]);
return (
<Group ref={trRef}>
<>
<Line
points={coordinates.flat()}
stroke="#00a1ff"
strokeWidth={1 / stage.getAbsoluteScale().x}
strokeScaleEnabled={true}
// strokeWidth={1 / stage.getAbsoluteScale().x}
strokeWidth={1}
strokeScaleEnabled={false}
closed
zIndex={1}
perfectDrawEnabled={false}
shadowForStrokeEnabled={false}
listening={false}
/>
{coordinates.map((point, index) => {
return (
<Circle
key={index}
x={point[0]}
y={point[1]}
radius={5}
stroke="#00a1ff"
strokeWidth={1}
fill="white"
draggable
onDragMove={handleDragMove(index)}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
scale={{ x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }}
perfectDrawEnabled={false}
/>
);
})}
</Group>
<Group ref={anchorsRef}>
{coordinates.map((point, index) => {
return (
<Circle
key={index}
x={point[0]}
y={point[1]}
radius={5}
stroke="#00a1ff"
strokeWidth={1}
fill="white"
draggable
strokeScaleEnabled={false}
onDragMove={handleDragMove(index)}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
scale={{ x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }}
perfectDrawEnabled={false}
shadowForStrokeEnabled={false}
/>
);
})}
</Group>
</>
);
}

View File

@ -39,6 +39,7 @@ export default function ImageCanvas() {
const stageWidth = window.innerWidth;
const stageHeight = window.innerHeight;
const stageRef = useRef<Konva.Stage>(null);
const dragLayerRef = useRef<Konva.Layer>(null);
const scale = useRef<number>(0);
const imageUrl = '/sample.jpg';
const labels = useCanvasStore((state) => state.labels) ?? [];
@ -120,6 +121,7 @@ export default function ImageCanvas() {
isSelected={label.id === selectedId}
onSelect={() => setSelectedId(label.id)}
info={label}
dragLayer={dragLayerRef.current as Konva.Layer}
/>
) : (
<LabelPolygon
@ -128,10 +130,12 @@ export default function ImageCanvas() {
onSelect={() => setSelectedId(label.id)}
info={label}
stage={stageRef.current as Konva.Stage}
dragLayer={dragLayerRef.current as Konva.Layer}
/>
)
)}
</Layer>
<Layer ref={dragLayerRef} />
</Stage>
) : (
<div></div>