Canvas vs SVG: Understanding the Difference
FeatureCanvasSVGTypeRaster (bitmap)VectorDOMSingle elementDOM-basedScalabilityFixed resolutionInfinitely scalableInteractivityManual event handlingBuilt-in DOM eventsPerformanceBetter for complex scenesBetter for simple graphicsSEO/AccessibilityRequires extra workNaturally accessible
HTML5 Canvas Fundamentals
Canvas provides a drawing surface for creating graphics programmatically using JavaScript.
Basic Canvas Setup
html
<canvas id="myCanvas" width="400" height="300">
Your browser doesn't support Canvas.
</canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Basic shapes
ctx.fillStyle = '#3498db';
ctx.fillRect(50, 50, 100, 75);
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 3;
ctx.strokeRect(200, 50, 100, 75);
</script>
Drawing Shapes and Paths
html
<canvas id="shapesCanvas" width="500" height="300"></canvas>
<script>
const canvas = document.getElementById('shapesCanvas');
const ctx = canvas.getContext('2d');
// Circle
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 2 * Math.PI);
ctx.fillStyle = '#9b59b6';
ctx.fill();
// Triangle
ctx.beginPath();
ctx.moveTo(250, 50);
ctx.lineTo(200, 150);
ctx.lineTo(300, 150);
ctx.closePath();
ctx.fillStyle = '#f39c12';
ctx.fill();
// Complex path
ctx.beginPath();
ctx.moveTo(400, 50);
ctx.quadraticCurveTo(450, 25, 400, 100);
ctx.lineTo(450, 100);
ctx.strokeStyle = '#27ae60';
ctx.lineWidth = 4;
ctx.stroke();
</script>
Working with Images and Text
html
<canvas id="contentCanvas" width="600" height="400"></canvas>
<script>
const canvas = document.getElementById('contentCanvas');
const ctx = canvas.getContext('2d');
// Text
ctx.font = '30px Arial';
ctx.fillStyle = '#2c3e50';
ctx.fillText('Canvas Text', 50, 50);
ctx.font = 'bold 24px Georgia';
ctx.strokeStyle = '#c0392b';
ctx.lineWidth = 1;
ctx.strokeText('Outlined Text', 50, 100);
// Image (when loaded)
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 300, 50, 200, 150);
// Apply filter effect
const imageData = ctx.getImageData(300, 50, 200, 150);
const data = imageData.data;
// Grayscale filter
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // Red
data[i + 1] = avg; // Green
data[i + 2] = avg; // Blue
}
ctx.putImageData(imageData, 300, 220);
};
img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgZmlsbD0iIzM0OThkYiIvPjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIyMCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj5TYW1wbGUgSW1hZ2U8L3RleHQ+PC9zdmc+';
</script>
Canvas Animation
html
<canvas id="animationCanvas" width="500" height="300"></canvas>
<button onclick="toggleAnimation()">Start/Stop Animation</button>
<script>
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let animationId;
let isAnimating = false;
class Ball {
constructor(x, y, radius, color, velocityX, velocityY) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocityX = velocityX;
this.velocityY = velocityY;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
}
update() {
// Bounce off walls
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
this.velocityX = -this.velocityX;
}
if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
this.velocityY = -this.velocityY;
}
this.x += this.velocityX;
this.y += this.velocityY;
}
}
const balls = [
new Ball(100, 100, 20, '#e74c3c', 2, 1.5),
new Ball(200, 150, 15, '#3498db', -1.5, 2.5),
new Ball(300, 100, 25, '#2ecc71', 1, -2)
];
function animate() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update and draw balls
balls.forEach(ball => {
ball.update();
ball.draw();
});
animationId = requestAnimationFrame(animate);
}
function toggleAnimation() {
if (isAnimating) {
cancelAnimationFrame(animationId);
isAnimating = false;
} else {
animate();
isAnimating = true;
}
}
</script>
SVG Fundamentals
SVG (Scalable Vector Graphics) creates vector-based graphics using XML markup.
Basic SVG Structure
html
<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<!-- Rectangle -->
<rect x="50" y="50" width="100" height="75"
fill="#3498db" stroke="#2980b9" stroke-width="2"/>
<!-- Circle -->
<circle cx="250" cy="100" r="40"
fill="#e74c3c" opacity="0.8"/>
<!-- Line -->
<line x1="50" y1="200" x2="350" y2="200"
stroke="#34495e" stroke-width="3"/>
<!-- Polyline -->
<polyline points="100,250 150,220 200,250 250,220 300,250"
fill="none" stroke="#9b59b6" stroke-width="2"/>
</svg>
Complex SVG Shapes and Paths
html
<svg width="500" height="400" xmlns="http://www.w3.org/2000/svg">
<!-- Path with curves -->
<path d="M 100 200 Q 150 100 200 200 T 300 200"
fill="none" stroke="#f39c12" stroke-width="3"/>
<!-- Star shape -->
<polygon points="250,50 270,90 310,90 280,120 290,160 250,140 210,160 220,120 190,90 230,90"
fill="#e67e22"/>
<!-- Ellipse -->
<ellipse cx="400" cy="300" rx="60" ry="40"
fill="#1abc9c" transform="rotate(45 400 300)"/>
<!-- Text along path -->
<defs>
<path id="textPath" d="M 50 350 Q 250 300 450 350"/>
</defs>
<text font-family="Arial" font-size="16" fill="#2c3e50">
<textPath href="#textPath">SVG text along a curved path!</textPath>
</text>
</svg>
SVG Gradients and Filters
html
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Linear Gradient -->
<linearGradient id="linearGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#3498db;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#9b59b6;stop-opacity:1"/>
</linearGradient>
<!-- Radial Gradient -->
<radialGradient id="radialGrad" cx="50%" cy="50%" r="50%">
<stop offset="0%" style="stop-color:#f39c12;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#e74c3c;stop-opacity:1"/>
</radialGradient>
<!-- Drop Shadow Filter -->
<filter id="dropshadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="3" dy="3" stdDeviation="2" flood-color="#000000" flood-opacity="0.3"/>
</filter>
</defs>
<!-- Shapes using gradients and filters -->
<rect x="50" y="50" width="150" height="100" fill="url(#linearGrad)" filter="url(#dropshadow)"/>
<circle cx="350" cy="100" r="50" fill="url(#radialGrad)" filter="url(#dropshadow)"/>
</svg>
SVG Animations with CSS and SMIL
html
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<!-- CSS Animation -->
<style>
.rotating-rect {
animation: rotate 3s linear infinite;
transform-origin: 125px 100px;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.pulsing-circle {
animation: pulse 2s ease-in-out infinite alternate;
}
@keyframes pulse {
from { r: 30px; }
to { r: 50px; }
}
</style>
<rect class="rotating-rect" x="50" y="50" width="150" height="100"
fill="#3498db" stroke="#2980b9" stroke-width="2"/>
<circle class="pulsing-circle" cx="350" cy="100" r="30" fill="#e74c3c"/>
<!-- SMIL Animation -->
<circle cx="100" cy="200" r="20" fill="#2ecc71">
<animateMotion dur="4s" repeatCount="indefinite">
<path d="M 0 0 Q 150 -50 300 0 T 600 0"/>
</animateMotion>
</circle>
<!-- Path animation -->
<path d="M 50 250 Q 250 200 450 250" fill="none" stroke="#9b59b6"
stroke-width="3" stroke-dasharray="10,5">
<animate attributeName="stroke-dashoffset" values="0;15" dur="1s" repeatCount="indefinite"/>
</path>
</svg>
Interactive SVG with JavaScript
html
<svg id="interactive-svg" width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<rect class="draggable" x="50" y="50" width="80" height="60"
fill="#3498db" cursor="move"/>
<circle class="clickable" cx="250" cy="100" r="40"
fill="#e74c3c" cursor="pointer"/>
<text x="200" y="200" font-family="Arial" font-size="16"
text-anchor="middle" id="status-text">Click circle or drag rectangle</text>
</svg>
<script>
const svg = document.getElementById('interactive-svg');
const statusText = document.getElementById('status-text');
// Click event on circle
svg.querySelector('.clickable').addEventListener('click', function(e) {
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6'];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
this.setAttribute('fill', randomColor);
statusText.textContent = `Circle clicked! New color: ${randomColor}`;
});
// Drag functionality for rectangle
let isDragging = false;
let startX, startY, elementX, elementY;
const draggableRect = svg.querySelector('.draggable');
draggableRect.addEventListener('mousedown', function(e) {
isDragging = true;
const rect = this.getBoundingClientRect();
const svgRect = svg.getBoundingClientRect();
startX = e.clientX - svgRect.left;
startY = e.clientY - svgRect.top;
elementX = parseFloat(this.getAttribute('x'));
elementY = parseFloat(this.getAttribute('y'));
statusText.textContent = 'Dragging rectangle...';
});
svg.addEventListener('mousemove', function(e) {
if (!isDragging) return;
const rect = this.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
const newX = elementX + (currentX - startX);
const newY = elementY + (currentY - startY);
draggableRect.setAttribute('x', newX);
draggableRect.setAttribute('y', newY);
});
document.addEventListener('mouseup', function() {
if (isDragging) {
isDragging = false;
statusText.textContent = 'Rectangle moved!';
}
});
</script>
Canvas vs SVG: Practical Examples
When to Use Canvas
html
<!-- Particle System with Canvas -->
<canvas id="particles" width="600" height="400"></canvas>
<script>
const canvas = document.getElementById('particles');
const ctx = canvas.getContext('2d');
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (Math.random() - 0.5) * 2;
this.vy = (Math.random() - 0.5) * 2;
this.size = Math.random() * 3 + 1;
this.opacity = Math.random();
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
}
draw() {
ctx.save();
ctx.globalAlpha = this.opacity;
ctx.fillStyle = '#3498db';
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
const particles = Array.from({ length: 100 }, () => new Particle());
function animateParticles() {
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach(particle => {
particle.update();
particle.draw();
});
requestAnimationFrame(animateParticles);
}
animateParticles();
</script>
When to Use SVG
html
<!-- Interactive Data Visualization with SVG -->
<svg id="chart" width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<style>
.bar { cursor: pointer; transition: fill 0.3s ease; }
.bar:hover { fill: #e74c3c !important; }
.axis { stroke: #333; stroke-width: 2; }
.label { font-family: Arial; font-size: 12px; fill: #333; }
</style>
<!-- Axes -->
<line class="axis" x1="50" y1="250" x2="450" y2="250"/>
<line class="axis" x1="50" y1="50" x2="50" y2="250"/>
<!-- Data bars -->
<rect class="bar" x="70" y="200" width="40" height="50" fill="#3498db" data-value="25"/>
<rect class="bar" x="130" y="150" width="40" height="100" fill="#2ecc71" data-value="50"/>
<rect class="bar" x="190" y="100" width="40" height="150" fill="#f39c12" data-value="75"/>
<rect class="bar" x="250" y="120" width="40" height="130" fill="#9b59b6" data-value="65"/>
<rect class="bar" x="310" y="180" width="40" height="70" fill="#e67e22" data-value="35"/>
<!-- Labels -->
<text class="label" x="90" y="270" text-anchor="middle">Q1</text>
<text class="label" x="150" y="270" text-anchor="middle">Q2</text>
<text class="label" x="210" y="270" text-anchor="middle">Q3</text>
<text class="label" x="270" y="270" text-anchor="middle">Q4</text>
<text class="label" x="330" y="270" text-anchor="middle">Q5</text>
<text id="chart-info" class="label" x="250" y="30" text-anchor="middle"
font-size="14" font-weight="bold">Hover over bars for values</text>
</svg>
<script>
const bars = document.querySelectorAll('.bar');
const info = document.getElementById('chart-info');
bars.forEach(bar => {
bar.addEventListener('mouseenter', function() {
const value = this.getAttribute('data-value');
const quarter = this.nextElementSibling.textContent;
info.textContent = `${quarter}: ${value}%`;
});
bar.addEventListener('mouseleave', function() {
info.textContent = 'Hover over bars for values';
});
});
</script>
Performance Optimization Tips
Canvas Optimization
javascript
// Use requestAnimationFrame for smooth animations
function optimizedAnimation() {
// Clear only dirty regions instead of entire canvas
ctx.clearRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
// Use off-screen canvas for complex operations
const offscreenCanvas = document.createElement('canvas');
const offCtx = offscreenCanvas.getContext('2d');
// Batch operations
ctx.save();
// Multiple drawing operations
ctx.restore();
requestAnimationFrame(optimizedAnimation);
}
SVG Optimization
html
<!-- Minimize DOM manipulation -->
<svg>
<defs>
<!-- Reuse elements with <use> -->
<g id="reusable-shape">
<circle r="10" fill="#3498db"/>
</g>
</defs>
<use href="#reusable-shape" x="50" y="50"/>
<use href="#reusable-shape" x="100" y="100"/>
</svg>