Skip to content

Commit ecb6ace

Browse files
committed
πŸ§³πŸ§‘πŸΌβ€πŸ’Ό ↝ [SSM-23 SSC-41 SSG-66]: Canvas & annotation, working in vite (just not in next, which is kind of the point)
1 parent 6e83f34 commit ecb6ace

File tree

12 files changed

+665
-597
lines changed

12 files changed

+665
-597
lines changed

β€Žapp/starnet/annotation/page.tsx

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
'use client';
2+
3+
import React, { useState, useRef } from 'react';
4+
import Canvas from '@/components/Projects/(classifications)/Annotation/Canvas';
5+
import Toolbar from '@/components/Projects/(classifications)/Annotation/Toolbar';
6+
import AnnotationList from '@/components/Projects/(classifications)/Annotation/AnnotationList';
7+
import { Annotation } from '@/types/Annotation';
8+
import { Upload } from 'lucide-react';
9+
import dynamic from 'next/dynamic';
10+
11+
function AnnotationCreate() {
12+
const [imageUrl, setImageUrl] = useState<string>('');
13+
const [currentTool, setCurrentTool] = useState<string>('');
14+
const [isDrawing, setIsDrawing] = useState(false);
15+
const [annotations, setAnnotations] = useState<Annotation[]>([]);
16+
const containerRef = useRef<HTMLDivElement>(null);
17+
const canvasRef = useRef<HTMLCanvasElement>(null);
18+
19+
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
20+
const file = event.target.files?.[0];
21+
if (file) {
22+
const url = URL.createObjectURL(file);
23+
setImageUrl(url);
24+
}
25+
};
26+
27+
const handleAddAnnotation = (annotation: Annotation) => {
28+
setAnnotations([...annotations, annotation]);
29+
};
30+
31+
const handleLabelChange = (index: number, newLabel: string) => {
32+
const newAnnotations = [...annotations];
33+
newAnnotations[index].label = newLabel;
34+
setAnnotations(newAnnotations);
35+
};
36+
37+
const handleDownload = () => {
38+
if (!canvasRef.current) return;
39+
40+
const canvas = canvasRef.current;
41+
const ctx = canvas.getContext('2d');
42+
if (!ctx) return;
43+
44+
// Draw the image onto the canvas
45+
const img = new Image();
46+
img.src = imageUrl;
47+
img.onload = () => {
48+
// Clear the canvas before drawing
49+
ctx.clearRect(0, 0, canvas.width, canvas.height);
50+
ctx.drawImage(img, 0, 0);
51+
52+
// Now draw the annotations (example, adjust based on how you manage annotations)
53+
annotations.forEach((annotation) => {
54+
const x = annotation.x ?? 0;
55+
const y = annotation.y ?? 0;
56+
const width = annotation.width ?? 0;
57+
const height = annotation.height ?? 0;
58+
59+
ctx.beginPath();
60+
ctx.rect(x, y, width, height); // Safe to call now
61+
ctx.strokeStyle = 'red';
62+
ctx.lineWidth = 2;
63+
ctx.stroke();
64+
ctx.fillStyle = 'red';
65+
ctx.fillText(annotation.label, x, y - 5); // Add label with default x and y
66+
});
67+
68+
// Generate the data URL and trigger download
69+
const dataUrl = canvas.toDataURL('image/png');
70+
const link = document.createElement('a');
71+
link.download = 'annotated-image.png';
72+
link.href = dataUrl;
73+
link.click();
74+
};
75+
};
76+
77+
const handleShare = async () => {
78+
if (!canvasRef.current) return;
79+
80+
const canvas = canvasRef.current;
81+
const dataUrl = canvas.toDataURL('image/png');
82+
const blob = await (await fetch(dataUrl)).blob();
83+
const file = new File([blob], 'annotated-image.png', { type: 'image/png' });
84+
85+
if (navigator.share) {
86+
await navigator.share({
87+
files: [file],
88+
title: 'Annotated Image',
89+
});
90+
} else {
91+
alert('Web Share API is not supported in your browser');
92+
}
93+
};
94+
95+
return (
96+
<div className="min-h-screen bg-gray-50 p-8">
97+
<div className="max-w-6xl mx-auto">
98+
<h1 className="text-3xl font-bold text-gray-900 mb-8">Image Annotator</h1>
99+
100+
<div className="flex gap-8">
101+
<div className="flex-1">
102+
{!imageUrl ? (
103+
<label className="flex flex-col items-center justify-center w-full h-[600px] border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50">
104+
<div className="flex flex-col items-center justify-center pt-5 pb-6">
105+
<Upload className="w-12 h-12 text-gray-400 mb-4" />
106+
<p className="mb-2 text-sm text-gray-500">
107+
<span className="font-semibold">Click to upload</span> or drag and drop
108+
</p>
109+
<p className="text-xs text-gray-500">PNG, JPG or GIF</p>
110+
</div>
111+
<input
112+
type="file"
113+
className="hidden"
114+
accept="image/*"
115+
onChange={handleFileUpload}
116+
/>
117+
</label>
118+
) : (
119+
<div className="space-y-4">
120+
<Toolbar
121+
currentTool={currentTool}
122+
onToolSelect={setCurrentTool}
123+
onDownload={handleDownload}
124+
onShare={handleShare}
125+
/>
126+
<div ref={containerRef}>
127+
{/* <Canvas
128+
imageUrl={imageUrl}
129+
annotations={annotations}
130+
currentTool={currentTool}
131+
isDrawing={isDrawing}
132+
setIsDrawing={setIsDrawing}
133+
onAddAnnotation={handleAddAnnotation}
134+
/> */}
135+
</div>
136+
{/* Create a hidden canvas to draw the image and annotations */}
137+
<canvas ref={canvasRef} style={{ display: 'none' }} width={800} height={600} />
138+
</div>
139+
)}
140+
</div>
141+
142+
<AnnotationList
143+
annotations={annotations}
144+
onLabelChange={handleLabelChange}
145+
/>
146+
</div>
147+
</div>
148+
</div>
149+
);
150+
}
151+
152+
export default AnnotationCreate;

0 commit comments

Comments
Β (0)