main
Kar l5win 2025-07-07 22:38:33 -07:00
commit da2fe252b6
10 changed files with 20311 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# Drawing App
A modern drawing application built with React.
## Features
- Canvas drawing with mouse/touch support
- Color selection
- Drawing history
- Responsive design
## Getting Started
### Prerequisites
- Node.js (v14 or higher)
- npm or yarn
### Installation
1. Clone the repository
2. Install dependencies:
```bash
npm install
```
### Running the App
```bash
npm start
```
The app will open in your default browser at `http://localhost:3000`

19904
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

39
package.json Normal file
View File

@ -0,0 +1,39 @@
{
"name": "drawing-app",
"version": "1.0.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

20
public/index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Drawing App - Create and share your drawings"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Drawing App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

81
src/App.css Normal file
View File

@ -0,0 +1,81 @@
.App {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f2f5;
}
.app-container {
width: 90%;
max-width: 1200px;
padding: 2rem;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
color: #2c3e50;
margin-bottom: 2rem;
text-align: center;
}
.App-header {
background-color: #282c34;
padding: 20px;
color: white;
border-radius: 10px;
margin-bottom: 20px;
}
.drawing-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.drawing-canvas {
border: 2px solid #ddd;
border-radius: 8px;
background-color: white;
cursor: crosshair;
}
.toolbar {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
padding: 20px;
background-color: #f5f5f5;
border-radius: 8px;
}
.tool-button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #fff;
cursor: pointer;
transition: background-color 0.2s;
}
.tool-button:hover {
background-color: #e0e0e0;
}
.tool-button.active {
background-color: #4CAF50;
color: white;
}
.color-picker {
margin: 10px;
}
input[type="range"] {
width: 200px;
margin: 10px;
}

27
src/App.js Normal file
View File

@ -0,0 +1,27 @@
import React from 'react';
import './App.css';
import DrawingCanvas from './components/DrawingCanvas';
function App() {
const [color, setColor] = React.useState('#000000');
const [thickness, setThickness] = React.useState(5);
const [history, setHistory] = React.useState([]);
return (
<div className="App">
<div className="app-container">
<h1>Drawing App</h1>
<DrawingCanvas
color={color}
setColor={setColor}
thickness={thickness}
setThickness={setThickness}
history={history}
setHistory={setHistory}
/>
</div>
</div>
);
}
export default App;

View File

@ -0,0 +1,186 @@
import React, { useRef, useState, useEffect } from 'react';
import { ChromePicker } from 'react-color';
const DrawingCanvas = ({ color, setColor, thickness, setThickness, history, setHistory }) => {
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [currentTool, setCurrentTool] = useState('pencil');
const [shapes, setShapes] = useState([]);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
// Set initial canvas size
canvas.width = window.innerWidth - 300;
canvas.height = window.innerHeight - 100;
// Draw history
history.forEach((action) => {
if (action.type === 'draw') {
drawLine(ctx, action.x1, action.y1, action.x2, action.y2, action.color, action.thickness);
}
});
// Draw shapes
shapes.forEach((shape) => {
drawShape(ctx, shape);
});
}, [history, shapes]);
const drawLine = (ctx, x1, y1, x2, y2, color, thickness) => {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = color;
ctx.lineWidth = thickness;
ctx.lineCap = 'round';
ctx.stroke();
};
const drawShape = (ctx, shape) => {
ctx.beginPath();
ctx.strokeStyle = shape.color;
ctx.lineWidth = shape.thickness;
switch (shape.type) {
case 'rectangle':
ctx.rect(shape.x, shape.y, shape.width, shape.height);
break;
case 'circle':
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
break;
default:
break;
}
ctx.stroke();
};
const handleMouseDown = (e) => {
setIsDrawing(true);
const canvas = canvasRef.current;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (currentTool === 'rectangle') {
setShapes([...shapes, {
type: 'rectangle',
x,
y,
width: 0,
height: 0,
color,
thickness
}]);
} else if (currentTool === 'circle') {
setShapes([...shapes, {
type: 'circle',
x,
y,
radius: 0,
color,
thickness
}]);
}
};
const handleMouseMove = (e) => {
if (!isDrawing) return;
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (currentTool === 'pencil') {
const lastX = history[history.length - 1]?.x2 || x;
const lastY = history[history.length - 1]?.y2 || y;
setHistory([...history, {
type: 'draw',
x1: lastX,
y1: lastY,
x2: x,
y2: y,
color,
thickness
}]);
} else if (currentTool === 'rectangle' || currentTool === 'circle') {
const updatedShapes = shapes.map((shape, index) => {
if (index === shapes.length - 1) {
if (currentTool === 'rectangle') {
return {
...shape,
width: Math.abs(x - shape.x),
height: Math.abs(y - shape.y)
};
} else {
const radius = Math.sqrt(Math.pow(x - shape.x, 2) + Math.pow(y - shape.y, 2));
return {
...shape,
radius
};
}
}
return shape;
});
setShapes(updatedShapes);
}
};
const handleMouseUp = () => {
setIsDrawing(false);
};
const handleUndo = () => {
if (history.length > 0) {
setHistory(history.slice(0, -1));
}
};
const handleRedo = () => {
// Implement redo functionality
};
return (
<div className="drawing-container">
<canvas
ref={canvasRef}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
className="drawing-canvas"
/>
<div className="toolbar">
<button onClick={() => setCurrentTool('pencil')} className={`tool-button ${currentTool === 'pencil' ? 'active' : ''}`}>
Pencil
</button>
<button onClick={() => setCurrentTool('rectangle')} className={`tool-button ${currentTool === 'rectangle' ? 'active' : ''}`}>
Rectangle
</button>
<button onClick={() => setCurrentTool('circle')} className={`tool-button ${currentTool === 'circle' ? 'active' : ''}`}>
Circle
</button>
<button onClick={handleUndo}>Undo</button>
<button onClick={handleRedo}>Redo</button>
<div className="color-picker">
<ChromePicker color={color} onChangeComplete={(color) => setColor(color.hex)} />
</div>
<input
type="range"
min="1"
max="20"
value={thickness}
onChange={(e) => setThickness(e.target.value)}
/>
</div>
</div>
);
};
export default DrawingCanvas;

13
src/index.css Normal file
View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

11
src/index.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);