Docs
19.7-Canvas-WebGL-Basics
19.7 Canvas and WebGL Basics
Overview
The HTML Canvas API provides a way to draw graphics using JavaScript. The 2D context offers simple drawing operations, while WebGL enables hardware-accelerated 3D graphics.
Learning Objectives
- •Understand Canvas 2D drawing fundamentals
- •Draw shapes, paths, and text
- •Work with images and transformations
- •Introduction to WebGL concepts
- •Basic animation techniques
Canvas Setup
<canvas id="canvas" width="800" height="600"></canvas>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size (accounting for device pixel ratio)
function setupCanvas(canvas) {
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
return ctx;
}
Basic Shapes
Rectangles
// Filled rectangle
ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 100, 50);
// Stroked rectangle
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(10, 70, 100, 50);
// Clear rectangle
ctx.clearRect(20, 20, 80, 30);
Paths
// Triangle
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.closePath();
ctx.fill();
// Line
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(200, 100);
ctx.stroke();
Circles and Arcs
// Full circle
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2);
ctx.fill();
// Semi-circle
ctx.beginPath();
ctx.arc(200, 75, 50, 0, Math.PI);
ctx.stroke();
// Arc segment
ctx.beginPath();
ctx.arc(300, 75, 50, 0, Math.PI / 2);
ctx.lineTo(300, 75);
ctx.closePath();
ctx.fill();
Curves
Quadratic Curves
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.quadraticCurveTo(200, 100, 350, 200); // (cpX, cpY, endX, endY)
ctx.stroke();
Bezier Curves
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.bezierCurveTo(100, 100, 300, 100, 350, 200); // (cp1X, cp1Y, cp2X, cp2Y, endX, endY)
ctx.stroke();
Styling
Colors and Gradients
// Solid color
ctx.fillStyle = '#FF5733';
ctx.fillStyle = 'rgba(255, 87, 51, 0.5)';
// Linear gradient
const linearGradient = ctx.createLinearGradient(0, 0, 200, 0);
linearGradient.addColorStop(0, 'red');
linearGradient.addColorStop(0.5, 'yellow');
linearGradient.addColorStop(1, 'blue');
ctx.fillStyle = linearGradient;
ctx.fillRect(0, 0, 200, 100);
// Radial gradient
const radialGradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 75);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(1, 'black');
ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, 150, 150);
Line Styles
ctx.lineWidth = 5;
ctx.lineCap = 'round'; // 'butt', 'round', 'square'
ctx.lineJoin = 'round'; // 'round', 'bevel', 'miter'
ctx.setLineDash([5, 10]); // Dashed line pattern
Text
// Set font
ctx.font = '48px serif';
ctx.textAlign = 'center'; // 'left', 'right', 'center'
ctx.textBaseline = 'middle'; // 'top', 'bottom', 'middle'
// Fill text
ctx.fillStyle = 'black';
ctx.fillText('Hello Canvas', 200, 100);
// Stroke text
ctx.strokeStyle = 'blue';
ctx.strokeText('Hello Canvas', 200, 150);
// Measure text
const metrics = ctx.measureText('Hello');
console.log(metrics.width);
Images
// Draw image
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0); // Original size
ctx.drawImage(img, 0, 0, 200, 100); // Scaled
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh); // Crop and scale
};
img.src = 'image.png';
// Draw from video
const video = document.querySelector('video');
ctx.drawImage(video, 0, 0);
Transformations
// Save current state
ctx.save();
// Translate (move origin)
ctx.translate(100, 100);
// Rotate (in radians)
ctx.rotate(Math.PI / 4); // 45 degrees
// Scale
ctx.scale(2, 2);
// Reset or restore
ctx.restore();
// Combined transformations
ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset
ctx.transform(a, b, c, d, e, f); // Multiply current matrix
Animation
function animate() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update positions
x += velocityX;
y += velocityY;
// Draw
ctx.fillRect(x, y, 50, 50);
// Request next frame
requestAnimationFrame(animate);
}
animate();
Compositing and Clipping
// Global alpha
ctx.globalAlpha = 0.5;
// Composite operations
ctx.globalCompositeOperation = 'source-over'; // Default
// Other: 'multiply', 'screen', 'overlay', 'difference', etc.
// Clipping
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.clip();
// All subsequent drawing is clipped to this circle
WebGL Basics
const gl = canvas.getContext('webgl');
// Check support
if (!gl) {
console.error('WebGL not supported');
}
// Set clear color
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Vertex shader source
const vertexShaderSource = `
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
`;
// Fragment shader source
const fragmentShaderSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
`;
// Create shader
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Create program
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
return program;
}
Best Practices
- •Use requestAnimationFrame - For smooth animations
- •Minimize state changes - Batch similar operations
- •Use offscreen canvas - For complex pre-rendering
- •Handle high DPI - Scale for device pixel ratio
- •Clean up resources - Remove event listeners, cancel animations
Summary
| Feature | 2D Canvas | WebGL |
|---|---|---|
| Complexity | Simple | Complex |
| Performance | Good | Excellent |
| Use Case | 2D graphics, UI | 3D graphics, games |
| GPU Usage | Minimal | Heavy |