Skip to content

Commit a9a432b

Browse files
authored
Merge pull request #62 from OPCODE-Open-Spring-Fest/revert-60-image_upload
Revert "feat: add image upload functionality to canvas and toolbar"
2 parents e747427 + b8abb3d commit a9a432b

File tree

2 files changed

+21
-148
lines changed

2 files changed

+21
-148
lines changed

client/src/components/Canvas.jsx

Lines changed: 19 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const SHAPE_TYPE = {
1515
PEN: "pen",
1616
CIRCLE: "circle",
1717
ERASER: "eraser",
18-
IMAGE: 'image',
1918
};
2019

2120
export const Canvas = () => {
@@ -83,30 +82,6 @@ export const Canvas = () => {
8382
}
8483
};
8584

86-
const handleImageUpload = (file) => {
87-
if (!file) return;
88-
const reader = new FileReader();
89-
reader.onload = (event) => {
90-
const img = new Image();
91-
img.onload = () => {
92-
const newShape = {
93-
id: Date.now().toString(),
94-
type: SHAPE_TYPE.IMAGE,
95-
image: img,
96-
start: { x: 100, y: 100 },
97-
end: { x: 100 + img.width, y: 100 + img.height },
98-
width: img.width,
99-
height: img.height,
100-
};
101-
setShapes((prev) => [...prev, newShape]);
102-
};
103-
img.src = event.target.result;
104-
};
105-
reader.readAsDataURL(file);
106-
};
107-
108-
109-
11085
// --- Helpers ---
11186
const getWorldPoint = (e) => {
11287
const canvas = canvasRef.current;
@@ -161,14 +136,6 @@ const handleImageUpload = (file) => {
161136
const maxY = Math.max(...shape.path.map(p => p.y));
162137
return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
163138
}
164-
if (shape.type === SHAPE_TYPE.IMAGE) {
165-
const minX = Math.min(shape.start.x, shape.end.x);
166-
const maxX = Math.max(shape.start.x, shape.end.x);
167-
const minY = Math.min(shape.start.y, shape.end.y);
168-
const maxY = Math.max(shape.start.y, shape.end.y);
169-
return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
170-
}
171-
172139

173140
return null;
174141
}, []);
@@ -225,7 +192,7 @@ const handleImageUpload = (file) => {
225192
if (isSelected) {
226193
// draw purple glow under the stroke for visibility
227194
ctx.save();
228-
ctx.lineWidth = shape.type === SHAPE_TYPE.IMAGE ? 4 : shape.width + 4;
195+
ctx.lineWidth = shape.width + 4;
229196
ctx.strokeStyle = "rgba(76,29,149,1)";
230197
switch (shape.type) {
231198
case SHAPE_TYPE.LINE:
@@ -278,7 +245,7 @@ const handleImageUpload = (file) => {
278245

279246
// then draw the actual shape on top
280247
ctx.strokeStyle = shape.color;
281-
ctx.lineWidth = shape.type === SHAPE_TYPE.IMAGE ? 1 : shape.width;
248+
ctx.lineWidth = shape.width;
282249
}
283250

284251
switch (shape.type) {
@@ -550,15 +517,6 @@ const handleImageUpload = (file) => {
550517
ctx.globalAlpha = 1;
551518
}
552519
break;
553-
case SHAPE_TYPE.IMAGE: {
554-
const { image, start, end } = shape;
555-
if (image) {
556-
const width = end.x - start.x;
557-
const height = end.y - start.y;
558-
ctx.drawImage(image, start.x, start.y, width, height);
559-
}
560-
break;
561-
}
562520
default:
563521
break;
564522
}
@@ -589,15 +547,12 @@ const handleImageUpload = (file) => {
589547
const ctx = canvas?.getContext("2d");
590548
if (!ctx) return;
591549

592-
// clear and reset transform
593-
ctx.setTransform(1, 0, 0, 1, 0, 0);
594-
ctx.clearRect(0, 0, canvas.width, canvas.height);
595-
ctx.globalCompositeOperation = "source-over";
596-
ctx.globalAlpha = 1;
597-
ctx.shadowBlur = 0;
550+
// clear and reset transform
551+
ctx.setTransform(1, 0, 0, 1, 0, 0);
552+
ctx.clearRect(0, 0, canvas.width, canvas.height);
598553

599-
// apply pan & zoom
600-
ctx.setTransform(1, 0, 0, 1, offset.x, offset.y);
554+
// apply pan & zoom
555+
ctx.setTransform(1, 0, 0, 1, offset.x, offset.y);
601556
ctx.scale(scale, scale);
602557

603558
// draw non-selected shapes first
@@ -667,29 +622,18 @@ const handleImageUpload = (file) => {
667622
if (handle && hitShape && hitShape.id === selectedShapeId) {
668623
// begin resize
669624
const origShape = JSON.parse(JSON.stringify(hitShape));
670-
if (hitShape.type === SHAPE_TYPE.IMAGE) origShape.image = hitShape.image;
671625
const origBBox = getShapeBBox(origShape);
672626
manipulationMode.current = { mode: 'resize', dir: handle.dir, origShape, origBBox };
673627
setIsDrawing(true);
674628
return;
675629
}
676630

677631
if (hitShape) {
678-
try {
679-
const _ctx = canvasRef.current?.getContext('2d');
680-
if (_ctx) {
681-
_ctx.globalCompositeOperation = 'source-over';
682-
_ctx.globalAlpha = 1;
683-
_ctx.shadowBlur = 0;
684-
}
685-
} catch (err) {
686-
console.debug('[canvas] composite reset failed', err);
687-
}
688632
setSelectedShapeId(hitShape.id);
689633
setIsDrawing(true);
690-
manipulationMode.current = { mode: "pending-move" };
634+
manipulationMode.current = { mode: "move" };
691635
setActiveColor(hitShape.color);
692-
if (hitShape.type === SHAPE_TYPE.PEN) setStrokeWidth(hitShape.width);
636+
setStrokeWidth(hitShape.width);
693637
} else {
694638
setSelectedShapeId(null);
695639
setIsDrawing(false);
@@ -721,7 +665,7 @@ const handleImageUpload = (file) => {
721665

722666

723667
// creation tools
724-
if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('brush-')) && activeTool !== SHAPE_TYPE.IMAGE) {
668+
if (Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('brush-')) {
725669
setSelectedShapeId(null);
726670
setIsDrawing(true);
727671
manipulationMode.current = { mode: "create" };
@@ -742,15 +686,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
742686
newShape.brush = brushType || "solid";
743687
newShape._seed = Math.floor(Math.random() * 0xffffffff);
744688
}
745-
if (activeTool === SHAPE_TYPE.IMAGE) {
746-
const hitShape = shapes.slice().reverse().find((shape) => isPointInShape(worldPoint, shape));
747-
if (hitShape && hitShape.type === SHAPE_TYPE.IMAGE) {
748-
// Just select, don't draw a new one
749-
setSelectedShapeId(hitShape.id);
750-
setIsDrawing(false);
751-
return;
752-
}
753-
}
754689
if (activeTool === SHAPE_TYPE.CIRCLE) {
755690
newShape.radius = 0;
756691
}
@@ -767,13 +702,8 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
767702
if (!shape) return false;
768703
const bbox = getShapeBBox(shape);
769704
if (!bbox) return false;
770-
const tol = shape.type === SHAPE_TYPE.IMAGE ? 8 : (shape.width || 0) + 6;
771-
return (
772-
point.x >= bbox.minX - tol &&
773-
point.x <= bbox.maxX + tol &&
774-
point.y >= bbox.minY - tol &&
775-
point.y <= bbox.maxY + tol
776-
);
705+
const tol = shape.width + 6;
706+
return (point.x >= bbox.minX - tol && point.x <= bbox.maxX + tol && point.y >= bbox.minY - tol && point.y <= bbox.maxY + tol);
777707
};
778708

779709
const draw = (e) => {
@@ -792,24 +722,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
792722
}
793723

794724
if (!isDrawing) return;
795-
if (
796-
activeTool === 'select' &&
797-
selectedShapeId &&
798-
manipulationMode.current &&
799-
manipulationMode.current.mode === 'pending-move'
800-
) {
801-
const dx0 = worldPoint.x - pointerStart.current.x;
802-
const dy0 = worldPoint.y - pointerStart.current.y;
803-
const distSq0 = dx0 * dx0 + dy0 * dy0;
804-
const threshold = 4 * 4; // squared threshold in world coords
805-
if (distSq0 > threshold) {
806-
// begin move: set pointerStart so subsequent deltas work from here
807-
manipulationMode.current.mode = 'move';
808-
pointerStart.current = worldPoint;
809-
} else {
810-
return;
811-
}
812-
}
813725

814726
// MOVE
815727
if (activeTool === 'select' && selectedShapeId && manipulationMode.current && manipulationMode.current.mode === 'move') {
@@ -841,9 +753,8 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
841753
const shapeIndex = shapes.findIndex((s) => s.id === selectedShapeId);
842754
if (shapeIndex === -1) return;
843755

844-
const newShapes = [...shapes];
845-
const sh = JSON.parse(JSON.stringify(origShape));
846-
if (origShape && origShape.type === SHAPE_TYPE.IMAGE) sh.image = origShape.image;
756+
const newShapes = [...shapes];
757+
const sh = JSON.parse(JSON.stringify(origShape));
847758

848759
// We'll compute a new bounding box keeping the opposite corner fixed depending on dir
849760
let { minX, minY, maxX, maxY } = origBBox;
@@ -962,15 +873,14 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
962873
setIsPointerDown(false);
963874
if (!isDrawing) return;
964875
setIsDrawing(false);
965-
const prevMode = manipulationMode.current?.mode;
966876
newShapeId.current = null;
967877
manipulationMode.current = null;
968-
if (prevMode === "erase") {
969-
const ctx = canvasRef.current?.getContext("2d");
970-
if (ctx) ctx.globalCompositeOperation = "source-over";
971-
}
878+
if (manipulationMode.current?.mode === "erase") {
879+
const ctx = canvasRef.current.getContext("2d");
880+
ctx.globalCompositeOperation = "source-over";
881+
}
972882

973-
};
883+
};
974884

975885
// delete
976886
const handleDeleteSelectedShape = useCallback(() => {
@@ -1143,7 +1053,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
11431053
onToolChange={handleToolChange}
11441054
onClear={handleClear}
11451055
onExport={handleExport}
1146-
onImageUpload={handleImageUpload}
11471056
/>
11481057

11491058
{joined ? (

client/src/components/Toolbar.jsx

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,13 @@ import {
1111
FileType,
1212
Brush,
1313
SquareDashed, // New import for the area select tool
14-
ImagePlus,
1514
} from "lucide-react";
1615
import { cn } from "../lib/utils";
1716
import { Button } from "./ui/Button";
1817
import { Separator } from "./ui/Separator";
1918
import { DropdownMenu, DropdownMenuItem } from "./ui/DropdownMenu";
20-
import { useRef } from "react";
21-
22-
export const Toolbar = ({
23-
activeTool,
24-
onToolChange,
25-
onClear,
26-
onExport,
27-
onImageUpload,
28-
}) => {
29-
const imageInputRef = useRef(null);
3019

20+
export const Toolbar = ({ activeTool, onToolChange, onClear, onExport }) => {
3121
const tools = [
3222
{ type: "select", icon: MousePointer2 },
3323
{ type: "area-select", icon: SquareDashed }, // New Area Select Tool
@@ -40,14 +30,7 @@ export const Toolbar = ({
4030

4131
const handleExport = (format) => {
4232
onExport(format);
43-
};
44-
45-
const handleImageSelect = (e) => {
46-
const file = e.target.files?.[0];
47-
if (file && onImageUpload) {
48-
onImageUpload(file);
49-
}
50-
};
33+
}
5134

5235
const brushTypes = [
5336
{
@@ -140,25 +123,6 @@ export const Toolbar = ({
140123
</DropdownMenu>
141124

142125
<Separator orientation="vertical" className="h-8 mx-1" />
143-
<input
144-
type="file"
145-
accept="image/*"
146-
ref={imageInputRef}
147-
style={{ display: "none" }}
148-
onChange={handleImageSelect}
149-
/>
150-
<Button
151-
variant="ghost"
152-
size="icon"
153-
onClick={() => imageInputRef.current?.click()}
154-
className="h-10 w-10 transition-all duration-200 hover:bg-secondary active:scale-95"
155-
aria-label="Upload image"
156-
>
157-
<ImagePlus className="h-5 w-5" />
158-
</Button>
159-
160-
<Separator orientation="vertical" className="h-8 mx-1" />
161-
162126
<Button
163127
variant="ghost"
164128
size="icon"

0 commit comments

Comments
 (0)