Kar 2025-07-08 17:05:32 +05:30
parent 2f32d1a166
commit 1b79fae385
1 changed files with 16 additions and 70 deletions

View File

@ -33,8 +33,6 @@ const DrawingCanvas = () => {
const canvasRef = useRef(null); const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false); const [isDrawing, setIsDrawing] = useState(false);
const [lastPosition, setLastPosition] = useState(null); const [lastPosition, setLastPosition] = useState(null);
const [history, setHistory] = useState([]);
const [undoHistory, setUndoHistory] = useState([]);
const [shapes, setShapes] = useState([]); const [shapes, setShapes] = useState([]);
const [currentTool, setCurrentTool] = useState('pencil'); const [currentTool, setCurrentTool] = useState('pencil');
const [color, setColor] = useState('#000000'); const [color, setColor] = useState('#000000');
@ -45,6 +43,7 @@ const DrawingCanvas = () => {
const [textInput, setTextInput] = useState(''); const [textInput, setTextInput] = useState('');
const [isTextInputVisible, setIsTextInputVisible] = useState(false); const [isTextInputVisible, setIsTextInputVisible] = useState(false);
const [textPosition, setTextPosition] = useState({ x: 0, y: 0 }); const [textPosition, setTextPosition] = useState({ x: 0, y: 0 });
const [showColorPicker, setShowColorPicker] = useState(false);
// Resize canvas on window resize // Resize canvas on window resize
useEffect(() => { useEffect(() => {
@ -68,11 +67,6 @@ const DrawingCanvas = () => {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
shapes.forEach(shape => drawShape(ctx, shape)); shapes.forEach(shape => drawShape(ctx, shape));
history.forEach(action => {
if (action.type === 'stroke') {
ctx.putImageData(action.imageData, 0, 0);
}
});
// After drawing all shapes // After drawing all shapes
if (selectedIndex !== null && shapes[selectedIndex]) { if (selectedIndex !== null && shapes[selectedIndex]) {
@ -81,40 +75,20 @@ const DrawingCanvas = () => {
ctx.save(); ctx.save();
ctx.strokeStyle = 'red'; ctx.strokeStyle = 'red';
ctx.lineWidth = 2; ctx.lineWidth = 2;
if (shape.type === 'rectangle' || shape.type === 'image' || shape.type === 'text') { if (shape.type === 'rectangle' || shape.type === 'image') {
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height); ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
// Draw resize handles (corners)
const handleSize = 8;
const handles = [
[shape.x, shape.y],
[shape.x + shape.width, shape.y],
[shape.x, shape.y + shape.height],
[shape.x + shape.width, shape.y + shape.height],
];
ctx.fillStyle = 'white';
ctx.strokeStyle = 'black';
handles.forEach(([hx, hy]) => {
ctx.fillRect(hx - handleSize/2, hy - handleSize/2, handleSize, handleSize);
ctx.strokeRect(hx - handleSize/2, hy - handleSize/2, handleSize, handleSize);
});
} else if (shape.type === 'circle') { } else if (shape.type === 'circle') {
ctx.beginPath(); ctx.beginPath();
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2); ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
ctx.stroke(); ctx.stroke();
// Draw handle at the edge (right)
const handleSize = 8;
ctx.fillStyle = 'white';
ctx.strokeStyle = 'black';
ctx.fillRect(shape.x + shape.radius - handleSize/2, shape.y - handleSize/2, handleSize, handleSize);
ctx.strokeRect(shape.x + shape.radius - handleSize/2, shape.y - handleSize/2, handleSize, handleSize);
} }
ctx.restore(); ctx.restore();
} }
}, [shapes, history, selectedIndex]); }, [shapes, selectedIndex]);
useEffect(() => { useEffect(() => {
redraw(); redraw();
}, [shapes, history, redraw]); }, [shapes, redraw]);
// Get mouse or touch position // Get mouse or touch position
const getPos = (e) => { const getPos = (e) => {
@ -253,33 +227,11 @@ const DrawingCanvas = () => {
} }
}; };
const deepCopyShapes = (shapesArr) => JSON.parse(JSON.stringify(shapesArr)); // Update handlePointerUp to not save history/undoHistory
const handlePointerUp = () => { const handlePointerUp = () => {
setIsDrawing(false); setIsDrawing(false);
setLastPosition(null); setLastPosition(null);
setSelectedIndex(null); setSelectedIndex(null);
// Save a deep copy of shapes for undo/redo
setHistory(prev => [...prev, deepCopyShapes(shapes)]);
setUndoHistory([]);
};
// Undo: restore the previous snapshot
const handleUndo = () => {
if (history.length === 0) return;
setUndoHistory(prev => [...prev, deepCopyShapes(shapes)]);
const prevShapes = history[history.length - 1];
setShapes(deepCopyShapes(prevShapes));
setHistory(prev => prev.slice(0, -1));
};
// Redo: restore the next snapshot
const handleRedo = () => {
if (undoHistory.length === 0) return;
setHistory(prev => [...prev, deepCopyShapes(shapes)]);
const nextShapes = undoHistory[undoHistory.length - 1];
setShapes(deepCopyShapes(nextShapes));
setUndoHistory(prev => prev.slice(0, -1));
}; };
// Touch events for mobile // Touch events for mobile
@ -316,9 +268,6 @@ const DrawingCanvas = () => {
image: img image: img
} }
]); ]);
// Save to history for undo
setHistory(prev => [...prev, deepCopyShapes([...shapes, { type: 'image', x, y, width, height, image: img }])]);
setUndoHistory([]);
}; };
img.src = URL.createObjectURL(file); img.src = URL.createObjectURL(file);
// Reset input so same file can be uploaded again // Reset input so same file can be uploaded again
@ -356,8 +305,6 @@ const DrawingCanvas = () => {
<button onClick={() => setCurrentTool('circle')} className={`tool-button ${currentTool === 'circle' ? 'active' : ''}`}></button> <button onClick={() => setCurrentTool('circle')} className={`tool-button ${currentTool === 'circle' ? 'active' : ''}`}></button>
<button onClick={() => setCurrentTool('select')} className={`tool-button ${currentTool === 'select' ? 'active' : ''}`}>🖱</button> <button onClick={() => setCurrentTool('select')} className={`tool-button ${currentTool === 'select' ? 'active' : ''}`}>🖱</button>
<button onClick={() => setCurrentTool('text')} className={`tool-button ${currentTool === 'text' ? 'active' : ''}`}>🔤</button> <button onClick={() => setCurrentTool('text')} className={`tool-button ${currentTool === 'text' ? 'active' : ''}`}>🔤</button>
<button onClick={handleUndo} className="tool-button" disabled={history.length === 0}></button>
<button onClick={handleRedo} className="tool-button" disabled={undoHistory.length === 0}></button>
<button onClick={() => fileInputRef.current.click()} className="tool-button">🖼 Insert Image</button> <button onClick={() => fileInputRef.current.click()} className="tool-button">🖼 Insert Image</button>
<input <input
type="file" type="file"
@ -367,9 +314,17 @@ const DrawingCanvas = () => {
onChange={handleInsertImage} onChange={handleInsertImage}
/> />
<button onClick={handleSaveCanvas} className="tool-button">💾 Save</button> <button onClick={handleSaveCanvas} className="tool-button">💾 Save</button>
<div className="color-picker"> <button
<ChromePicker color={color} onChangeComplete={(c) => setColor(c.hex)} /> onClick={() => setShowColorPicker(v => !v)}
</div> className="tool-button"
>
🎨 Color
</button>
{showColorPicker && (
<div className="color-picker">
<ChromePicker color={color} onChangeComplete={(c) => setColor(c.hex)} />
</div>
)}
<div className="thickness-controls"> <div className="thickness-controls">
<button onClick={() => setThickness(t => Math.max(1, t - 1))} disabled={thickness <= 1}></button> <button onClick={() => setThickness(t => Math.max(1, t - 1))} disabled={thickness <= 1}></button>
<span>{thickness}</span> <span>{thickness}</span>
@ -411,15 +366,6 @@ const DrawingCanvas = () => {
thickness, thickness,
} }
]); ]);
setHistory(prev => [...prev, deepCopyShapes([...shapes, {
type: 'text',
x: textPosition.x,
y: textPosition.y,
text: textInput,
color,
thickness,
}])]);
setUndoHistory([]);
} }
setIsTextInputVisible(false); setIsTextInputVisible(false);
setTextInput(''); setTextInput('');