Skip to content

Commit 4833427

Browse files
Merge pull request #174 from chandra-011220/canvas
added canavs
2 parents 5f55ef1 + eb5c48c commit 4833427

File tree

3 files changed

+310
-14
lines changed

3 files changed

+310
-14
lines changed

projects/drawing-canvas/index.html

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!doctype html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
@@ -10,9 +10,31 @@
1010
<header><h1>Drawing Canvas</h1></header>
1111
<main id="app">
1212
<!-- TODO: <canvas> area with drawing via mouse/touch -->
13+
<div class="canvas-container">
14+
<canvas id="drawingCanvas"></canvas>
15+
</div>
16+
1317
<!-- TODO: Controls: color picker, brush size, clear, save as PNG -->
18+
<div class="toolbar" role="toolbar" aria-label="Drawing tools">
19+
<div class="tool-group">
20+
<label for="colorPicker">Color:</label>
21+
<input type="color" id="colorPicker" value="#000000" aria-label="Brush color">
22+
</div>
23+
24+
<div class="tool-group">
25+
<label for="brushSize">Brush Size:</label>
26+
<input type="range" id="brushSize" min="1" max="50" value="5" aria-label="Brush size">
27+
<span id="brushSizeValue">5</span>
28+
</div>
29+
30+
<button class="button clear" id="clearButton" aria-label="Clear canvas">Clear</button>
31+
<button class="button save" id="saveButton" aria-label="Save drawing as PNG">Save as PNG</button>
32+
</div>
33+
34+
<p class="warning">Note: Resizing the window will clear your drawing.</p>
35+
1436
<!-- TODO: Optional: eraser mode, background grid, undo/redo -->
1537
</main>
1638
<script defer src="main.js"></script>
1739
</body>
18-
</html>
40+
</html>

projects/drawing-canvas/main.js

Lines changed: 158 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,160 @@
1-
/**
2-
* TODO: Drawing Canvas
3-
* - Initialize <canvas>, handle mouse/touch drawing
4-
* - Color picker, brush size, clear canvas
5-
* - Save canvas as image (toDataURL)
6-
* Optional: Eraser mode, undo/redo, pressure sensitivity (Pointer Events)
7-
*/
8-
9-
document.addEventListener('DOMContentLoaded', () => {
1+
document.addEventListener('DOMContentLoaded', () => {
102
console.log('Drawing Canvas ready');
3+
114
// TODO: Canvas setup and draw handlers
12-
});
5+
const canvas = document.getElementById('drawingCanvas');
6+
const ctx = canvas.getContext('2d');
7+
const colorPicker = document.getElementById('colorPicker');
8+
const brushSize = document.getElementById('brushSize');
9+
const brushSizeValue = document.getElementById('brushSizeValue');
10+
const clearButton = document.getElementById('clearButton');
11+
const saveButton = document.getElementById('saveButton');
12+
13+
let isDrawing = false;
14+
let lastX = 0;
15+
let lastY = 0;
16+
let dpr = window.devicePixelRatio || 1;
17+
18+
// Initialize canvas
19+
function initCanvas() {
20+
const container = canvas.parentElement;
21+
const width = container.clientWidth;
22+
const height = container.clientHeight;
23+
24+
// Set canvas size with DPR scaling
25+
canvas.width = width * dpr;
26+
canvas.height = height * dpr;
27+
canvas.style.width = `${width}px`;
28+
canvas.style.height = `${height}px`;
29+
30+
// Scale context for high DPI displays
31+
ctx.scale(dpr, dpr);
32+
33+
// Set initial drawing styles
34+
ctx.strokeStyle = colorPicker.value;
35+
ctx.lineWidth = brushSize.value;
36+
ctx.lineCap = 'round';
37+
ctx.lineJoin = 'round';
38+
39+
// Clear canvas with white background
40+
ctx.fillStyle = '#ffffff';
41+
ctx.fillRect(0, 0, width, height);
42+
}
43+
44+
// Drawing functions
45+
function startDrawing(e) {
46+
isDrawing = true;
47+
const pos = getPointerPos(e);
48+
[lastX, lastY] = [pos.x, pos.y];
49+
}
50+
51+
function draw(e) {
52+
if (!isDrawing) return;
53+
54+
e.preventDefault();
55+
const pos = getPointerPos(e);
56+
57+
ctx.beginPath();
58+
ctx.moveTo(lastX, lastY);
59+
ctx.lineTo(pos.x, pos.y);
60+
ctx.stroke();
61+
62+
[lastX, lastY] = [pos.x, pos.y];
63+
}
64+
65+
function stopDrawing() {
66+
isDrawing = false;
67+
}
68+
69+
function getPointerPos(e) {
70+
const rect = canvas.getBoundingClientRect();
71+
let clientX, clientY;
72+
73+
if (e.type.includes('touch')) {
74+
clientX = e.touches[0].clientX;
75+
clientY = e.touches[0].clientY;
76+
} else {
77+
clientX = e.clientX;
78+
clientY = e.clientY;
79+
}
80+
81+
return {
82+
x: (clientX - rect.left) * (canvas.width / dpr / rect.width),
83+
y: (clientY - rect.top) * (canvas.height / dpr / rect.height)
84+
};
85+
}
86+
87+
// Event handlers for controls
88+
colorPicker.addEventListener('input', (e) => {
89+
ctx.strokeStyle = e.target.value;
90+
});
91+
92+
brushSize.addEventListener('input', (e) => {
93+
const size = e.target.value;
94+
ctx.lineWidth = size;
95+
brushSizeValue.textContent = size;
96+
});
97+
98+
clearButton.addEventListener('click', () => {
99+
if (confirm('Are you sure you want to clear the canvas?')) {
100+
const width = canvas.width / dpr;
101+
const height = canvas.height / dpr;
102+
ctx.fillStyle = '#ffffff';
103+
ctx.fillRect(0, 0, width, height);
104+
}
105+
});
106+
107+
saveButton.addEventListener('click', () => {
108+
// Create a temporary canvas for saving at original resolution
109+
const tempCanvas = document.createElement('canvas');
110+
const tempCtx = tempCanvas.getContext('2d');
111+
112+
tempCanvas.width = canvas.width;
113+
tempCanvas.height = canvas.height;
114+
115+
// Draw the high-resolution content to temp canvas
116+
tempCtx.drawImage(canvas, 0, 0);
117+
118+
// Create download link
119+
const link = document.createElement('a');
120+
link.download = `drawing-${new Date().getTime()}.png`;
121+
link.href = tempCanvas.toDataURL('image/png');
122+
link.click();
123+
});
124+
125+
// Pointer event handlers for drawing
126+
canvas.addEventListener('pointerdown', startDrawing);
127+
canvas.addEventListener('pointermove', draw);
128+
canvas.addEventListener('pointerup', stopDrawing);
129+
canvas.addEventListener('pointerout', stopDrawing);
130+
131+
// Touch event handlers for mobile devices
132+
canvas.addEventListener('touchstart', (e) => {
133+
e.preventDefault();
134+
startDrawing(e);
135+
});
136+
137+
canvas.addEventListener('touchmove', (e) => {
138+
e.preventDefault();
139+
draw(e);
140+
});
141+
142+
canvas.addEventListener('touchend', stopDrawing);
143+
144+
// Handle window resize
145+
let resizeTimeout;
146+
window.addEventListener('resize', () => {
147+
clearTimeout(resizeTimeout);
148+
resizeTimeout = setTimeout(() => {
149+
if (confirm('Resizing will clear your current drawing. Continue?')) {
150+
initCanvas();
151+
}
152+
}, 250);
153+
});
154+
155+
// Prevent context menu on canvas
156+
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
157+
158+
// Initialize the canvas
159+
initCanvas();
160+
});

projects/drawing-canvas/styles.css

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
/* TODO: Style the drawing canvas and toolbar */
1+
/* TODO: Style the drawing canvas and toolbar */
22
:root {
33
--bg: #0b1220;
44
--fg: #f8fafc;
55
--accent: #f472b6;
6+
--toolbar-bg: #1e293b;
7+
--toolbar-border: #334155;
8+
--canvas-bg: #ffffff;
69
}
710
* { box-sizing: border-box; }
811
body {
@@ -13,5 +16,128 @@ body {
1316
min-height: 100vh;
1417
display: grid;
1518
place-items: center;
19+
padding: 1rem;
1620
}
17-
#app { width: min(94vw, 960px); }
21+
#app {
22+
width: min(94vw, 960px);
23+
display: flex;
24+
flex-direction: column;
25+
gap: 1rem;
26+
}
27+
28+
header h1 {
29+
text-align: center;
30+
margin: 0 0 1rem 0;
31+
color: var(--accent);
32+
}
33+
34+
.canvas-container {
35+
position: relative;
36+
width: 100%;
37+
height: 60vh;
38+
border: 2px solid var(--toolbar-border);
39+
border-radius: 8px;
40+
overflow: hidden;
41+
background: var(--canvas-bg);
42+
}
43+
44+
#drawingCanvas {
45+
display: block;
46+
width: 100%;
47+
height: 100%;
48+
cursor: crosshair;
49+
touch-action: none;
50+
}
51+
52+
.toolbar {
53+
display: flex;
54+
flex-wrap: wrap;
55+
gap: 1rem;
56+
padding: 1rem;
57+
background: var(--toolbar-bg);
58+
border: 1px solid var(--toolbar-border);
59+
border-radius: 8px;
60+
align-items: center;
61+
justify-content: center;
62+
}
63+
64+
.tool-group {
65+
display: flex;
66+
align-items: center;
67+
gap: 0.5rem;
68+
}
69+
70+
.tool-group label {
71+
font-size: 0.9rem;
72+
font-weight: 500;
73+
}
74+
75+
#colorPicker {
76+
width: 50px;
77+
height: 40px;
78+
border: 2px solid var(--toolbar-border);
79+
border-radius: 4px;
80+
cursor: pointer;
81+
background: #000000;
82+
}
83+
84+
#brushSize {
85+
width: 120px;
86+
}
87+
88+
#brushSizeValue {
89+
min-width: 30px;
90+
text-align: center;
91+
font-size: 0.9rem;
92+
}
93+
94+
.button {
95+
padding: 0.75rem 1.5rem;
96+
background: var(--accent);
97+
color: white;
98+
border: none;
99+
border-radius: 4px;
100+
cursor: pointer;
101+
font-size: 0.9rem;
102+
font-weight: 500;
103+
transition: opacity 0.2s;
104+
}
105+
106+
.button:hover {
107+
opacity: 0.9;
108+
}
109+
110+
.button:active {
111+
transform: translateY(1px);
112+
}
113+
114+
.button.clear {
115+
background: #ef4444;
116+
}
117+
118+
.button.save {
119+
background: #10b981;
120+
}
121+
122+
.warning {
123+
color: #f59e0b;
124+
font-size: 0.8rem;
125+
text-align: center;
126+
margin-top: 0.5rem;
127+
}
128+
129+
/* Responsive design */
130+
@media (max-width: 768px) {
131+
.toolbar {
132+
flex-direction: column;
133+
align-items: stretch;
134+
}
135+
136+
.tool-group {
137+
justify-content: space-between;
138+
}
139+
140+
.canvas-container {
141+
height: 50vh;
142+
}
143+
}

0 commit comments

Comments
 (0)