Feat: 리뷰와 레이블을 같이 볼 수 있게 컴포넌트 추가
This commit is contained in:
parent
4e148ccce3
commit
67b4ac78bb
102
frontend/src/components/ImageWithLabels/index.tsx
Normal file
102
frontend/src/components/ImageWithLabels/index.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Image, Layer, Stage, Line, Rect } from 'react-konva';
|
||||
import useImage from 'use-image';
|
||||
import { Label } from '@/types';
|
||||
|
||||
interface Shape {
|
||||
label: string;
|
||||
color: string;
|
||||
points: [number, number][];
|
||||
shape_type: 'polygon' | 'rectangle';
|
||||
}
|
||||
|
||||
interface ImageWithLabelsProps {
|
||||
imagePath: string;
|
||||
labelData: string;
|
||||
}
|
||||
|
||||
export default function ImageWithLabels({ imagePath, labelData }: ImageWithLabelsProps) {
|
||||
const [image] = useImage(imagePath);
|
||||
const [labels, setLabels] = useState<Label[]>([]);
|
||||
const [stageDimensions, setStageDimensions] = useState({ width: window.innerWidth, height: window.innerHeight });
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLabelData = async () => {
|
||||
try {
|
||||
const response = await fetch(labelData);
|
||||
const json: { shapes: Shape[] } = await response.json();
|
||||
const shapes = json.shapes.map((shape, index) => ({
|
||||
id: index,
|
||||
name: shape.label,
|
||||
color: shape.color,
|
||||
type: shape.shape_type === 'polygon' ? 'polygon' : 'rect',
|
||||
coordinates: shape.points,
|
||||
})) as Label[];
|
||||
setLabels(shapes);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch label data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLabelData();
|
||||
}, [labelData]);
|
||||
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
setStageDimensions({
|
||||
width: window.innerWidth - 280,
|
||||
height: window.innerHeight - 64,
|
||||
});
|
||||
};
|
||||
window.addEventListener('resize', updateDimensions);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateDimensions);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getScale = () => {
|
||||
if (!image) return { x: 1, y: 1 };
|
||||
const widthRatio = stageDimensions.width / image.width;
|
||||
const heightRatio = stageDimensions.height / image.height;
|
||||
const scale = Math.min(widthRatio, heightRatio);
|
||||
return { x: scale, y: scale };
|
||||
};
|
||||
|
||||
return image ? (
|
||||
<Stage
|
||||
width={stageDimensions.width}
|
||||
height={stageDimensions.height}
|
||||
className="overflow-hidden bg-gray-200"
|
||||
scale={getScale()}
|
||||
>
|
||||
<Layer>{image && <Image image={image} />}</Layer>
|
||||
<Layer>
|
||||
{labels.map((label) =>
|
||||
label.type === 'rect' ? (
|
||||
<Rect
|
||||
key={label.id}
|
||||
x={label.coordinates[0][0]}
|
||||
y={label.coordinates[0][1]}
|
||||
width={label.coordinates[1][0] - label.coordinates[0][0]}
|
||||
height={label.coordinates[1][1] - label.coordinates[0][1]}
|
||||
stroke={label.color}
|
||||
strokeWidth={2}
|
||||
listening={false}
|
||||
/>
|
||||
) : (
|
||||
<Line
|
||||
key={label.id}
|
||||
points={label.coordinates.flat()}
|
||||
stroke={label.color}
|
||||
strokeWidth={2}
|
||||
closed
|
||||
listening={false}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Layer>
|
||||
</Stage>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
}
|
@ -7,6 +7,7 @@ import useAuthStore from '@/stores/useAuthStore';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import 'slick-carousel/slick/slick.css';
|
||||
import 'slick-carousel/slick/slick-theme.css';
|
||||
import ImageWithLabels from '@/components/ImageWithLabels';
|
||||
|
||||
export default function ReviewDetail(): JSX.Element {
|
||||
const { workspaceId, projectId, reviewId } = useParams<{
|
||||
@ -99,10 +100,9 @@ export default function ReviewDetail(): JSX.Element {
|
||||
<Slider {...settings}>
|
||||
{reviewDetail.images.map((image) => (
|
||||
<div key={image.id}>
|
||||
<img
|
||||
src={image.imagePath}
|
||||
alt="리뷰 이미지"
|
||||
className="h-auto w-full rounded"
|
||||
<ImageWithLabels
|
||||
imagePath={image.imagePath}
|
||||
labelData={image.dataPath}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
Loading…
Reference in New Issue
Block a user