Skip to content

Commit 8c8f3af

Browse files
authored
Create draw.html
1 parent 5faa15f commit 8c8f3af

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

apps/draw.html

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>4ndyDraw</title>
7+
<style>
8+
body {
9+
display: flex;
10+
flex-direction: column;
11+
align-items: center;
12+
background: #f0f0f0;
13+
font-family: Arial, sans-serif;
14+
}
15+
#toolbar {
16+
display: flex;
17+
gap: 10px;
18+
padding: 10px;
19+
background: #ddd;
20+
border-radius: 8px;
21+
margin: 10px;
22+
}
23+
button {
24+
padding: 8px;
25+
border: 2px solid #333;
26+
border-radius: 4px;
27+
cursor: pointer;
28+
background: white;
29+
}
30+
button.active {
31+
background: #666;
32+
color: white;
33+
}
34+
canvas {
35+
border: 2px solid #333;
36+
background: white;
37+
cursor: crosshair;
38+
}
39+
</style>
40+
</head>
41+
<body>
42+
<h1>4ndyDraw 🎨</h1>
43+
<div id="toolbar">
44+
<button class="active" data-tool="brush">Brush</button>
45+
<button data-tool="eraser">Eraser</button>
46+
<input type="color" id="color" value="#000000">
47+
<input type="range" id="size" min="1" max="100" value="5">
48+
<span id="sizeValue">5px</span>
49+
<button id="undo">Undo</button>
50+
<button id="redo">Redo</button>
51+
<button id="clear">Clear</button>
52+
<button id="save">Save As</button>
53+
</div>
54+
<canvas id="canvas" width="800" height="600"></canvas>
55+
56+
<script>
57+
const canvas = document.getElementById('canvas');
58+
const ctx = canvas.getContext('2d');
59+
let isDrawing = false;
60+
let currentTool = 'brush';
61+
let undoStack = [];
62+
let redoStack = [];
63+
64+
// Initialize canvas
65+
resetCanvas();
66+
67+
function resetCanvas() {
68+
ctx.fillStyle = '#ffffff';
69+
ctx.fillRect(0, 0, canvas.width, canvas.height);
70+
saveState();
71+
}
72+
73+
function saveState() {
74+
undoStack.push(canvas.toDataURL());
75+
redoStack = [];
76+
}
77+
78+
function restoreState() {
79+
if (undoStack.length > 1) {
80+
redoStack.push(undoStack.pop());
81+
redrawCanvas();
82+
}
83+
}
84+
85+
function redoState() {
86+
if (redoStack.length > 0) {
87+
undoStack.push(redoStack.pop());
88+
redrawCanvas();
89+
}
90+
}
91+
92+
function redrawCanvas() {
93+
const img = new Image();
94+
img.onload = () => {
95+
ctx.clearRect(0, 0, canvas.width, canvas.height);
96+
ctx.drawImage(img, 0, 0);
97+
};
98+
img.src = undoStack[undoStack.length - 1];
99+
}
100+
101+
// Drawing functions
102+
function startDrawing(e) {
103+
isDrawing = true;
104+
ctx.beginPath();
105+
ctx.moveTo(e.offsetX, e.offsetY);
106+
ctx.strokeStyle = currentTool === 'eraser' ? '#ffffff' : document.getElementById('color').value;
107+
ctx.lineWidth = document.getElementById('size').value;
108+
ctx.lineCap = 'round';
109+
ctx.lineJoin = 'round';
110+
}
111+
112+
function draw(e) {
113+
if (!isDrawing) return;
114+
ctx.lineTo(e.offsetX, e.offsetY);
115+
ctx.stroke();
116+
}
117+
118+
function endDrawing() {
119+
if (isDrawing) {
120+
isDrawing = false;
121+
saveState();
122+
ctx.closePath();
123+
}
124+
}
125+
126+
// Save As function
127+
function saveAs() {
128+
const link = document.createElement('a');
129+
link.download = '4ndyPaint.png';
130+
link.href = canvas.toDataURL();
131+
link.click();
132+
}
133+
134+
// Event listeners
135+
canvas.addEventListener('mousedown', startDrawing);
136+
canvas.addEventListener('mousemove', draw);
137+
canvas.addEventListener('mouseup', endDrawing);
138+
canvas.addEventListener('mouseout', endDrawing);
139+
140+
document.querySelectorAll('#toolbar button[data-tool]').forEach(btn => {
141+
btn.addEventListener('click', () => {
142+
document.querySelectorAll('#toolbar button').forEach(b =>
143+
b.classList.remove('active'));
144+
btn.classList.add('active');
145+
currentTool = btn.dataset.tool;
146+
});
147+
});
148+
149+
document.getElementById('size').addEventListener('input', (e) => {
150+
document.getElementById('sizeValue').textContent = `${e.target.value}px`;
151+
});
152+
153+
document.getElementById('undo').addEventListener('click', restoreState);
154+
document.getElementById('redo').addEventListener('click', redoState);
155+
document.getElementById('clear').addEventListener('click', resetCanvas);
156+
document.getElementById('save').addEventListener('click', saveAs);
157+
</script>
158+
</body>
159+
</html>

0 commit comments

Comments
 (0)