Canvas and SVG: Deep Comparison and Application of HTML5 Graphics Drawing Technologies â
Two Paths of Graphics Drawing â
HTML5 provides Web developers with two powerful graphics drawing technologies: Canvas and SVG. Each has its own characteristics and is suitable for different application scenarios.
Core Differences â
Canvas:
- Pixel-based bitmap drawing
- Draws pixel by pixel using JavaScript
- Content cannot be directly modified after drawing (requires redrawing)
- Excellent performance, suitable for dynamic, complex graphics
SVG (Scalable Vector Graphics):
- XML-based vector graphics
- Describes graphics using tags
- Each graphic element is a DOM node that can be manipulated independently
- Lossless scaling, suitable for graphics requiring interaction
You can think of it this way: Canvas is like a canvas where you paint with a brush; SVG is like building graphics with blocks, where each block can be moved and modified individually.
Canvas Basics â
Creating a Canvas â
html
<canvas id="myCanvas" width="800" height="600">
Your browser does not support Canvas.
</canvas>Note:
- Use
widthandheightattributes to set the canvas size (not CSS). - Setting dimensions via CSS will cause the graphics to stretch and distort.
Getting the Drawing Context â
javascript
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d"); // Get 2D drawing context
// Check if browser supports Canvas
if (!ctx) {
console.error("Browser does not support Canvas");
}Basic Shape Drawing â
1. Rectangle â
javascript
const ctx = canvas.getContext("2d");
// Fill rectangle
ctx.fillStyle = "#3498db"; // Set fill color
ctx.fillRect(50, 50, 200, 100); // fillRect(x, y, width, height)
// Stroke rectangle
ctx.strokeStyle = "#e74c3c"; // Set border color
ctx.lineWidth = 5; // Set line width
ctx.strokeRect(300, 50, 200, 100);
// Clear rectangle area
ctx.clearRect(350, 75, 100, 50);2. Path Drawing â
Canvas uses paths to draw complex shapes:
javascript
// Draw triangle
ctx.beginPath(); // Start path
ctx.moveTo(100, 200); // Move to start point
ctx.lineTo(200, 200); // Draw line to second point
ctx.lineTo(150, 120); // Draw line to third point
ctx.closePath(); // Close path (return to start point)
ctx.fillStyle = "#2ecc71";
ctx.fill(); // Fill
ctx.strokeStyle = "#27ae60";
ctx.stroke(); // Stroke3. Circles and Arcs â
javascript
// Draw circle
ctx.beginPath();
ctx.arc(400, 300, 80, 0, 2 * Math.PI); // arc(x, y, radius, startAngle, endAngle)
ctx.fillStyle = "#9b59b6";
ctx.fill();
// Draw semi-circle
ctx.beginPath();
ctx.arc(600, 300, 80, 0, Math.PI); // 0 to Ī is a semi-circle
ctx.strokeStyle = "#8e44ad";
ctx.lineWidth = 3;
ctx.stroke();
// Draw sector
ctx.beginPath();
ctx.moveTo(400, 500); // Center
ctx.arc(400, 500, 60, 0, Math.PI / 2); // 90 degree sector
ctx.lineTo(400, 500); // Return to center
ctx.closePath();
ctx.fillStyle = "#f39c12";
ctx.fill();4. Bezier Curves â
javascript
// Quadratic Bezier Curve
ctx.beginPath();
ctx.moveTo(100, 400);
ctx.quadraticCurveTo(200, 300, 300, 400); // Control point and end point
ctx.strokeStyle = "#e67e22";
ctx.lineWidth = 3;
ctx.stroke();
// Cubic Bezier Curve
ctx.beginPath();
ctx.moveTo(100, 500);
ctx.bezierCurveTo(150, 450, 250, 550, 300, 500); // Two control points and end point
ctx.strokeStyle = "#d35400";
ctx.lineWidth = 3;
ctx.stroke();Text Drawing â
javascript
// Fill text
ctx.font = "48px Arial"; // Set font
ctx.fillStyle = "#34495e";
ctx.fillText("Hello Canvas!", 100, 100); // fillText(text, x, y)
// Stroke text
ctx.font = "bold 36px Georgia";
ctx.strokeStyle = "#2c3e50";
ctx.lineWidth = 2;
ctx.strokeText("Outlined Text", 100, 200);
// Set text alignment
ctx.textAlign = "center"; // left, right, center, start, end
ctx.textBaseline = "middle"; // top, bottom, middle, alphabetic, hanging
ctx.fillText("Centered Text", canvas.width / 2, canvas.height / 2);
// Measure text width
const text = "Measure Me";
const metrics = ctx.measureText(text);
console.log(`Text width: ${metrics.width}px`);Image Processing â
javascript
// Load and draw image
const img = new Image();
img.onload = function () {
// Draw original image
ctx.drawImage(img, 0, 0);
// Draw and scale
ctx.drawImage(img, 200, 0, 150, 100); // drawImage(img, x, y, width, height)
// Slice drawing
ctx.drawImage(
img,
50,
50,
100,
100, // Source image slice position and size
400,
0,
200,
200 // Target canvas position and size
);
};
img.src = "photo.jpg";Gradients and Patterns â
javascript
// Linear Gradient
const linearGradient = ctx.createLinearGradient(0, 0, 200, 0);
linearGradient.addColorStop(0, "#e74c3c");
linearGradient.addColorStop(0.5, "#f39c12");
linearGradient.addColorStop(1, "#f1c40f");
ctx.fillStyle = linearGradient;
ctx.fillRect(50, 50, 200, 100);
// Radial Gradient
const radialGradient = ctx.createRadialGradient(400, 300, 20, 400, 300, 100);
radialGradient.addColorStop(0, "#3498db");
radialGradient.addColorStop(1, "#2c3e50");
ctx.fillStyle = radialGradient;
ctx.beginPath();
ctx.arc(400, 300, 100, 0, 2 * Math.PI);
ctx.fill();
// Pattern Fill
const patternImg = new Image();
patternImg.onload = function () {
const pattern = ctx.createPattern(patternImg, "repeat"); // repeat, repeat-x, repeat-y, no-repeat
ctx.fillStyle = pattern;
ctx.fillRect(0, 400, 300, 150);
};
patternImg.src = "pattern.png";Canvas Animation â
javascript
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
let x = 0;
let y = 100;
let dx = 2; // x velocity
let dy = 1; // y velocity
const radius = 20;
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw ball
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = "#3498db";
ctx.fill();
ctx.closePath();
// Boundary detection
if (x + dx > canvas.width - radius || x + dx < radius) {
dx = -dx; // Reverse
}
if (y + dy > canvas.height - radius || y + dy < radius) {
dy = -dy;
}
// Update position
x += dx;
y += dy;
// Loop animation
requestAnimationFrame(draw);
}
draw();SVG Basics â
Creating SVG â
SVG can be embedded directly in HTML:
html
<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<!-- SVG Content -->
</svg>Or referenced as an independent file:
html
<img src="graphic.svg" alt="SVG Graphic" />
<object data="graphic.svg" type="image/svg+xml"></object>Basic Shapes â
1. Rectangle â
html
<svg width="400" height="300">
<!-- Rectangle -->
<rect
x="50"
y="50"
width="200"
height="100"
fill="#3498db"
stroke="#2980b9"
stroke-width="3"
/>
<!-- Rounded Rectangle -->
<rect
x="300"
y="50"
width="150"
height="100"
rx="15"
ry="15"
fill="#e74c3c"
/>
</svg>2. Circle and Ellipse â
html
<svg width="400" height="300">
<!-- Circle -->
<circle cx="100" cy="100" r="50" fill="#2ecc71" />
<!-- Ellipse -->
<ellipse cx="300" cy="100" rx="80" ry="50" fill="#9b59b6" />
</svg>3. Line and Polyline â
html
<svg width="400" height="300">
<!-- Line -->
<line x1="50" y1="50" x2="200" y2="150" stroke="#34495e" stroke-width="3" />
<!-- Polyline -->
<polyline
points="50,200 100,150 150,180 200,120 250,160"
fill="none"
stroke="#e67e22"
stroke-width="2"
/>
<!-- Polygon -->
<polygon
points="300,50 350,100 325,150 275,150 250,100"
fill="#f39c12"
stroke="#d35400"
stroke-width="2"
/>
</svg>4. Path â
Path is the most powerful element in SVG:
html
<svg width="400" height="300">
<!-- M = moveto, L = lineto, Z = closepath -->
<path
d="M 50 50 L 150 50 L 100 120 Z"
fill="#3498db"
stroke="#2980b9"
stroke-width="2"
/>
<!-- Curve: Q = Quadratic Bezier, C = Cubic Bezier -->
<path
d="M 200 50 Q 250 20 300 50"
fill="none"
stroke="#e74c3c"
stroke-width="3"
/>
<!-- Arc: A rx ry x-axis-rotation large-arc-flag sweep-flag x y -->
<path
d="M 50 200 A 50 50 0 0 1 150 200"
fill="none"
stroke="#2ecc71"
stroke-width="3"
/>
</svg>SVG Text â
html
<svg width="400" height="200">
<!-- Basic Text -->
<text x="50" y="50" font-family="Arial" font-size="24" fill="#34495e">
Hello SVG!
</text>
<!-- Text along Path -->
<defs>
<path id="textPath" d="M 50 100 Q 200 50 350 100" />
</defs>
<text font-size="18" fill="#e74c3c">
<textPath href="#textPath">This is text along a path</textPath>
</text>
<!-- Multi-line Text -->
<text x="50" y="150" font-size="16">
<tspan x="50" dy="0">First Line</tspan>
<tspan x="50" dy="20">Second Line</tspan>
<tspan x="50" dy="20">Third Line</tspan>
</text>
</svg>SVG Gradients and Filters â
html
<svg width="400" height="300">
<defs>
<!-- Linear Gradient -->
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#e74c3c;stop-opacity:1" />
<stop offset="100%" style="stop-color:#f39c12;stop-opacity:1" />
</linearGradient>
<!-- Radial Gradient -->
<radialGradient id="grad2">
<stop offset="0%" style="stop-color:#3498db;stop-opacity:1" />
<stop offset="100%" style="stop-color:#2c3e50;stop-opacity:1" />
</radialGradient>
<!-- Shadow Filter -->
<filter id="shadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="3" />
<feOffset dx="2" dy="2" result="offsetblur" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<rect x="50" y="50" width="150" height="100" fill="url(#grad1)" />
<circle cx="300" cy="100" r="50" fill="url(#grad2)" />
<text x="50" y="200" font-size="36" fill="#34495e" filter="url(#shadow)">
Text with Shadow
</text>
</svg>SVG Animation â
html
<svg width="400" height="300">
<!-- Move Animation -->
<circle cx="50" cy="100" r="20" fill="#3498db">
<animate
attributeName="cx"
from="50"
to="350"
dur="3s"
repeatCount="indefinite"
/>
</circle>
<!-- Color Change -->
<rect x="50" y="150" width="100" height="60" fill="#e74c3c">
<animate
attributeName="fill"
values="#e74c3c;#f39c12;#e74c3c"
dur="2s"
repeatCount="indefinite"
/>
</rect>
<!-- Path Animation -->
<circle r="10" fill="#2ecc71">
<animateMotion dur="4s" repeatCount="indefinite">
<mpath href="#motionPath" />
</animateMotion>
</circle>
<path
id="motionPath"
d="M 50 250 Q 200 200 350 250"
fill="none"
stroke="#ccc"
stroke-width="2"
/>
</svg>Manipulating SVG with JavaScript â
html
<svg id="mySvg" width="400" height="300">
<circle id="myCircle" cx="100" cy="100" r="50" fill="#3498db" />
</svg>
<button id="changeColor">Change Color</button>
<button id="moveCircle">Move Circle</button>
<script>
const svg = document.getElementById("mySvg");
const circle = document.getElementById("myCircle");
// Change Color
document.getElementById("changeColor").addEventListener("click", () => {
const colors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12"];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
circle.setAttribute("fill", randomColor);
});
// Move Circle
document.getElementById("moveCircle").addEventListener("click", () => {
const newX = Math.random() * (400 - 100) + 50;
const newY = Math.random() * (300 - 100) + 50;
circle.setAttribute("cx", newX);
circle.setAttribute("cy", newY);
});
// Dynamically Create SVG Element
function addRectangle() {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", Math.random() * 300);
rect.setAttribute("y", Math.random() * 200);
rect.setAttribute("width", 80);
rect.setAttribute("height", 50);
rect.setAttribute("fill", "#9b59b6");
svg.appendChild(rect);
}
addRectangle();
</script>Canvas vs SVG: How to Choose? â
Performance Comparison â
| Feature | Canvas | SVG |
|---|---|---|
| Rendering | Pixel-based (Bitmap) | Object-based (Vector) |
| DOM Manipulation | No DOM nodes, high performance | Each element is a DOM node |
| Element Count | Suitable for large number of elements (Games) | Too many elements affect performance |
| Scaling | Distorts (Pixelated) | Lossless scaling |
| Event Handling | Requires manual coordinate calculation | Elements support native events |
| Memory Usage | Fixed (depends on canvas size) | Increases with element count |
Applicable Scenarios â
Choose Canvas:
â
Game development (requires high frame rate animation)
â
Real-time data visualization (large number of data points)
â
Image processing (filters, pixel manipulation)
â
Video frame processing
â
Complex particle systemsChoose SVG:
â
Icons, Logos (require scaling)
â
Charts (require interaction)
â
Map applications
â
Graphics content requiring SEO
â
High-quality graphics for printing
â
Few but interaction-rich elementsPractical Example Comparison â
Canvas Implementation of Bar Chart:
javascript
const canvas = document.getElementById("chart");
const ctx = canvas.getContext("2d");
const data = [30, 80, 45, 60, 95];
const barWidth = 50;
const barGap = 20;
data.forEach((value, index) => {
const x = index * (barWidth + barGap) + 50;
const y = canvas.height - value - 50;
const height = value;
ctx.fillStyle = "#3498db";
ctx.fillRect(x, y, barWidth, height);
// Draw value
ctx.fillStyle = "#000";
ctx.font = "14px Arial";
ctx.textAlign = "center";
ctx.fillText(value, x + barWidth / 2, y - 5);
});SVG Implementation of Bar Chart:
html
<svg width="400" height="300">
<g id="chart"></g>
</svg>
<script>
const svg = document.getElementById("chart");
const data = [30, 80, 45, 60, 95];
const barWidth = 50;
const barGap = 20;
data.forEach((value, index) => {
const x = index * (barWidth + barGap) + 50;
const y = 250 - value;
// Create rectangle
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("width", barWidth);
rect.setAttribute("height", value);
rect.setAttribute("fill", "#3498db");
// Add interaction
rect.addEventListener("mouseover", function () {
this.setAttribute("fill", "#e74c3c");
});
rect.addEventListener("mouseout", function () {
this.setAttribute("fill", "#3498db");
});
svg.appendChild(rect);
// Add text
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", x + barWidth / 2);
text.setAttribute("y", y - 5);
text.setAttribute("text-anchor", "middle");
text.textContent = value;
svg.appendChild(text);
});
</script>Best Practices â
Canvas Optimization â
javascript
// 1. Avoid unnecessary state changes
ctx.fillStyle = "#3498db";
for (let i = 0; i < 1000; i++) {
ctx.fillRect(x[i], y[i], 10, 10); // Batch draw similar graphics
}
// 2. Use Offscreen Canvas
const offscreenCanvas = document.createElement("canvas");
const offscreenCtx = offscreenCanvas.getContext("2d");
// Draw complex graphics on offscreen canvas
offscreenCtx.drawComplexGraphic();
// Draw to main canvas at once
ctx.drawImage(offscreenCanvas, 0, 0);
// 3. Reduce redraw area
// Only clear the area that needs updating
ctx.clearRect(x, y, width, height);SVG Optimization â
html
<!-- 1. Reuse elements -->
<svg>
<defs>
<circle id="dot" r="5" fill="#3498db" />
</defs>
<use href="#dot" x="50" y="50" />
<use href="#dot" x="100" y="100" />
<use href="#dot" x="150" y="150" />
</svg>
<!-- 2. Use CSS Animation instead of SMIL -->
<style>
.animated-circle {
animation: move 3s infinite;
}
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(300px);
}
}
</style>
<svg>
<circle class="animated-circle" cx="50" cy="100" r="20" fill="#3498db" />
</svg>Summary â
Canvas and SVG are two powerful graphics technologies provided by HTML5:
Canvas:
- High performance, suitable for dynamic, complex graphics.
- Pixel-level control, suitable for games and image processing.
- Requires more JavaScript code.
SVG:
- Vector graphics, lossless scaling.
- DOM elements, easy to interact with and manipulate.
- Suitable for graphics that are few in number but require scaling or interaction.