🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Build a complete drawing pad application
- Integrate Canvas API with touch and mouse events
- Implement multiple drawing tools and brushes
- Add color picker and brush size controls
- Save and load drawings
- Create a responsive drawing interface
📚 Project Overview
We'll build a comprehensive drawing pad that includes: - Multiple drawing tools (pen, brush, eraser) - Color picker and brush size controls - Undo/redo functionality - Save and load drawings - Touch and mouse support - Responsive design
🏗️ Project Structure
drawing-pad/
├── index.html
├── style.css
└── script.js
📝 HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drawing Pad</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>Drawing Pad</h1>
<div class="toolbar">
<div class="tool-group">
<button id="penTool" class="tool-btn active">Pen</button>
<button id="brushTool" class="tool-btn">Brush</button>
<button id="eraserTool" class="tool-btn">Eraser</button>
</div>
<div class="tool-group">
<input type="color" id="colorPicker" value="#000000">
<input type="range" id="brushSize" min="1" max="50" value="2">
<span id="brushSizeValue">2px</span>
</div>
<div class="tool-group">
<button id="undoBtn">Undo</button>
<button id="redoBtn">Redo</button>
<button id="clearBtn">Clear</button>
</div>
<div class="tool-group">
<button id="saveBtn">Save</button>
<button id="loadBtn">Load</button>
<input type="file" id="fileInput" accept=".json" style="display: none;">
</div>
</div>
</header>
<main>
<canvas id="drawingCanvas"></canvas>
</main>
</div>
<script src="script.js"></script>
</body>
</html>
💻 JavaScript Implementation
class DrawingPad {
constructor() {
this.canvas = document.getElementById('drawingCanvas');
this.ctx = this.canvas.getContext('2d');
this.isDrawing = false;
this.currentTool = 'pen';
this.currentColor = '#000000';
this.currentSize = 2;
this.history = [];
this.historyIndex = -1;
this.setupCanvas();
this.setupEventListeners();
this.setupTools();
this.saveState();
}
setupCanvas() {
const rect = this.canvas.getBoundingClientRect();
this.canvas.width = rect.width * window.devicePixelRatio;
this.canvas.height = rect.height * window.devicePixelRatio;
this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
this.canvas.style.width = rect.width + 'px';
this.canvas.style.height = rect.height + 'px';
this.ctx.lineCap = 'round';
this.ctx.lineJoin = 'round';
}
setupEventListeners() {
// Mouse events
this.canvas.addEventListener('mousedown', (e) => this.startDrawing(e));
this.canvas.addEventListener('mousemove', (e) => this.draw(e));
this.canvas.addEventListener('mouseup', () => this.stopDrawing());
this.canvas.addEventListener('mouseout', () => this.stopDrawing());
// Touch events
this.canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
this.startDrawing(e.touches[0]);
});
this.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
this.draw(e.touches[0]);
});
this.canvas.addEventListener('touchend', (e) => {
e.preventDefault();
this.stopDrawing();
});
// Window resize
window.addEventListener('resize', () => this.setupCanvas());
}
setupTools() {
// Tool buttons
document.getElementById('penTool').addEventListener('click', () => this.setTool('pen'));
document.getElementById('brushTool').addEventListener('click', () => this.setTool('brush'));
document.getElementById('eraserTool').addEventListener('click', () => this.setTool('eraser'));
// Color picker
document.getElementById('colorPicker').addEventListener('change', (e) => {
this.currentColor = e.target.value;
});
// Brush size
const brushSizeSlider = document.getElementById('brushSize');
const brushSizeValue = document.getElementById('brushSizeValue');
brushSizeSlider.addEventListener('input', (e) => {
this.currentSize = parseInt(e.target.value);
brushSizeValue.textContent = e.target.value + 'px';
});
// Action buttons
document.getElementById('undoBtn').addEventListener('click', () => this.undo());
document.getElementById('redoBtn').addEventListener('click', () => this.redo());
document.getElementById('clearBtn').addEventListener('click', () => this.clear());
document.getElementById('saveBtn').addEventListener('click', () => this.save());
document.getElementById('loadBtn').addEventListener('click', () => this.load());
// File input
document.getElementById('fileInput').addEventListener('change', (e) => {
this.loadFromFile(e.target.files[0]);
});
}
setTool(tool) {
this.currentTool = tool;
// Update active tool button
document.querySelectorAll('.tool-btn').forEach(btn => {
btn.classList.remove('active');
});
document.getElementById(tool + 'Tool').classList.add('active');
}
startDrawing(e) {
this.isDrawing = true;
const rect = this.canvas.getBoundingClientRect();
this.lastX = e.clientX - rect.left;
this.lastY = e.clientY - rect.top;
this.ctx.beginPath();
this.ctx.moveTo(this.lastX, this.lastY);
}
draw(e) {
if (!this.isDrawing) return;
const rect = this.canvas.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
this.ctx.lineWidth = this.currentSize;
switch(this.currentTool) {
case 'pen':
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.strokeStyle = this.currentColor;
break;
case 'brush':
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.strokeStyle = this.currentColor;
this.ctx.lineWidth = this.currentSize * 2;
break;
case 'eraser':
this.ctx.globalCompositeOperation = 'destination-out';
this.ctx.lineWidth = this.currentSize * 3;
break;
}
this.ctx.lineTo(currentX, currentY);
this.ctx.stroke();
this.lastX = currentX;
this.lastY = currentY;
}
stopDrawing() {
if (this.isDrawing) {
this.isDrawing = false;
this.saveState();
}
}
saveState() {
this.historyIndex++;
if (this.historyIndex < this.history.length) {
this.history.length = this.historyIndex;
}
this.history.push(this.canvas.toDataURL());
if (this.history.length > 20) {
this.history.shift();
this.historyIndex--;
}
}
undo() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.restoreState();
}
}
redo() {
if (this.historyIndex < this.history.length - 1) {
this.historyIndex++;
this.restoreState();
}
}
restoreState() {
const img = new Image();
img.onload = () => {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(img, 0, 0);
};
img.src = this.history[this.historyIndex];
}
clear() {
if (confirm('Are you sure you want to clear the canvas?')) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.saveState();
}
}
save() {
const dataURL = this.canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'drawing.png';
link.href = dataURL;
link.click();
}
load() {
document.getElementById('fileInput').click();
}
loadFromFile(file) {
if (file && file.type === 'application/json') {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
this.loadDrawing(data);
} catch (error) {
console.error('Error loading file:', error);
}
};
reader.readAsText(file);
}
}
loadDrawing(data) {
const img = new Image();
img.onload = () => {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(img, 0, 0);
this.saveState();
};
img.src = data.imageData;
}
}
// Initialize the drawing pad
const drawingPad = new DrawingPad();
🎯 Key Features Implemented
- Multiple Drawing Tools - Pen, brush, and eraser
- Color and Size Controls - Color picker and brush size slider
- Undo/Redo - History management with 20 levels
- Save/Load - Export as PNG and load JSON files
- Touch Support - Works on mobile devices
- Responsive Design - Adapts to different screen sizes
🎯 Key Takeaways
- Canvas API enables powerful drawing applications
- Event handling for mouse and touch interactions
- State management for undo/redo functionality
- File operations for saving and loading drawings
- Responsive design ensures cross-device compatibility
🚀 Next Steps
Congratulations! You've built a complete drawing pad application. In the next lesson, we'll create:
- Voice Note App - Speech recognition and text-to-speech
- Voice input - Converting speech to text
- Audio playback - Text-to-speech functionality
- Note management - Save and organize voice notes
Next Lesson: Lesson 10: Mini Project - Voice Note App