JavaScript Canvas API
Scopri come disegnare grafica con l'elemento canvas HTML e JavaScript: contesto 2D, forme, linee, trasformazioni e animazioni con requestAnimationFrame.
Introduzione a HTML Canvas con JavaScript
L'elemento HTML <canvas> è una superficie di disegno che controlli con JavaScript. A differenza delle immagini o di SVG, canvas funziona in modalità immediata: non esistono oggetti forma da tenere traccia. Si emettono comandi di disegno e i pixel vengono dipinti immediatamente. Una volta dipinta, una forma è solo pixel colorati — il canvas non ha memoria di essa, ed è per questo che il ridisegno è il concetto fondamentale alla base di ogni animazione.
Questo capitolo illustra come ottenere un contesto 2D, disegnare forme e linee, trasformare il sistema di coordinate e animare con requestAnimationFrame.
Questa pagina tratta il contesto di rendering 2D (getContext('2d')). Canvas espone anche un contesto webgl per grafica 3D con accelerazione hardware, che è un'API separata.
Comprensione dell'elemento 'canvas'
L'elemento <canvas> crea una superficie di disegno a dimensione fissa che renderizza grafica al volo. Eccelle in applicazioni ad alta intensità grafica — giochi, grafici, manipolazione di immagini, effetti particellari — dove è necessario un controllo a livello di pixel e frame rate elevati. Di per sé il tag non disegna nulla; è un contenitore vuoto, e JavaScript esegue tutta la pittura.
Un'insidia da imparare subito: gli attributi width e height impostano la dimensione del buffer di disegno, mentre i valori CSS width/height impostano la dimensione visualizzata. Se non corrispondono, il canvas viene allungato e i disegni appaiono sfocati o distorti. Imposta la dimensione con gli attributi (o in JS tramite canvas.width/canvas.height), non con CSS, a meno che tu non voglia scalare intenzionalmente.
Configurazione base di Canvas
Per iniziare a disegnare, inserisci un tag <canvas> nel tuo HTML, poi ottieni il suo contesto di disegno 2D in JavaScript con getContext('2d'). L'oggetto contesto è dove risiedono tutti i metodi di disegno.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Example</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
</script>
</body>
</html>Qui definiamo un canvas 200×200, gli diamo un bordo in modo da poterne vedere i limiti, e otteniamo il contesto con getContext('2d'). Il bordo è puramente visivo; la superficie del canvas stessa inizia completamente trasparente.
Nota il sistema di coordinate: l'origine (0, 0) si trova nell'angolo in alto a sinistra. L'asse x cresce verso destra e l'asse y cresce verso il basso, quindi (0, 0) è il bordo superiore e valori y più grandi si spostano verso il basso. Questo sorprende i principianti che si aspettano assi in stile matematico.
Disegnare Forme
Il modo più rapido per mettere qualcosa sullo schermo è fillRect(x, y, width, height), che disegna un rettangolo riempito con una singola chiamata. Imposta prima fillStyle per scegliere il colore (qualsiasi stringa colore CSS funziona). Esiste anche strokeRect per un rettangolo solo con contorno e clearRect per cancellare una regione rendendola trasparente.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rectangle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="rectangleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('rectangleCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#FF0000'; // Set the fill color to red
ctx.fillRect(20, 20, 150, 100); // Draw the rectangle
</script>
</body>
</html>Creare Linee e Percorsi
Linee, curve e forme personalizzate vengono tutte costruite a partire dai percorsi. Lo schema è sempre lo stesso:
beginPath()— inizia un percorso nuovo (se lo ometti, la nuova linea si unisce a quella precedente).moveTo(x, y)— solleva la penna e posizionala senza disegnare.lineTo(x, y)— disegna un segmento di linea verso un nuovo punto (chiamalo ripetutamente per concatenare segmenti).stroke()per disegnare il contorno, oppurefill()per riempire l'area racchiusa.
Controlla l'aspetto della linea con lineWidth, strokeStyle e lineCap prima di chiamare stroke().
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line Drawing Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="lineCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('lineCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath(); // Start the path
ctx.moveTo(50, 50); // Move the pen to (50, 50)
ctx.lineTo(150, 50); // Draw a line to (150, 50)
ctx.lineTo(150, 150); // Continue to (150, 150)
ctx.lineWidth = 4; // Thicker line
ctx.strokeStyle = 'navy'; // Line color
ctx.stroke(); // Render the path visible
</script>
</body>
</html>Disegnare Cerchi e Archi
I cerchi vengono disegnati con arc(x, y, radius, startAngle, endAngle), dove gli angoli sono espressi in radianti (un cerchio completo è 2 * Math.PI). Racchiudilo in un percorso e poi usa fill() o stroke():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Circle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="circleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('circleCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
// Center (100, 100), radius 60, full circle
ctx.arc(100, 100, 60, 0, 2 * Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.stroke(); // Add an outline on top of the fill
</script>
</body>
</html>Operazioni Canvas Avanzate
Trasformazioni Canvas
translate, rotate e scale modificano il sistema di coordinate del canvas anziché le singole forme. È fondamentale capire che sono cumulative — ciascuna si costruisce sulla precedente, e una rotazione ruota attorno all'origine corrente, non al centro della forma. Per ruotare attorno a un punto, occorre prima usare translate per spostarsi su quel punto, poi applicare rotate.
Poiché le trasformazioni si accumulano, racchiudile in ctx.save() / ctx.restore() in modo da poter tornare allo stato precedente invece di annullare manualmente ogni trasformazione:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Transformation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="transformCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('transformCanvas');
const ctx = canvas.getContext('2d');
ctx.save(); // Remember the untransformed state
ctx.translate(50, 50); // Move the canvas origin to (50, 50)
ctx.rotate((Math.PI / 180) * 25); // Rotate 25 degrees (converted to radians)
ctx.fillStyle = 'blue'; // Set fill color to blue
ctx.fillRect(0, 0, 100, 50); // Draw at the new, rotated origin
ctx.restore(); // Back to the original coordinate system
</script>
</body>
</html>Animazioni con Canvas
Canvas non ha animazioni integrate; si crea il movimento cancellando e ridisegnando ripetutamente l'intera superficie. Il metodo requestAnimationFrame del browser pianifica l'esecuzione della funzione di disegno prima del prossimo repaint — tipicamente 60 volte al secondo — e si mette in pausa automaticamente quando la scheda è nascosta, motivo per cui è preferito a setInterval.
Il ciclo segue sempre tre passi: cancella il canvas (clearRect), aggiorna lo stato (qui, la posizione x), poi disegna il nuovo frame. Dimenticare il clearRect produrrà una scia di tutti i frame passati invece di una forma in movimento.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Animation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="animationCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let x = 0; // Starting position
function drawFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas for the new frame
ctx.fillStyle = 'green'; // Set the fill color to green
ctx.fillRect(x, 20, 50, 50); // Draw a moving rectangle
x++; // Increment the horizontal position
if (x > canvas.width) {
x = 0;
}
requestAnimationFrame(drawFrame); // Continue the animation
}
drawFrame();
</script>
</body>
</html>Errori Comuni
- Output sfocato: dimensione del buffer e dimensione CSS non corrispondenti. Imposta le dimensioni con gli attributi
width/height, non con CSS, a meno che tu non voglia scalare intenzionalmente. - Forme unite tra loro: dimenticare
beginPath()prima di un nuovo percorso lo fa continuare su quello precedente. - Le linee scompaiono: hai chiamato
stroke()ofill()ma non hai mai costruito un percorso, oppure hai disegnato al di fuori dei limiti del canvas. - L'animazione lascia scie: manca il
clearRectall'inizio di ogni frame. - Nulla viene renderizzato: lo script è stato eseguito prima che il
<canvas>esistesse nel DOM. Posiziona lo script dopo l'elemento, oppure eseguilo suDOMContentLoaded.
Conclusione
L'elemento <canvas> offre una superficie a basso livello, precisa al pixel per la grafica che HTML e CSS non possono produrre: grafici, editing di immagini, sistemi di particelle e giochi completi. L'API è ridotta — ottieni un contesto, imposta uno stile, emetti un comando di disegno — e le animazioni sono semplicemente quel ciclo eseguito molte volte al secondo.
Per approfondire, consulta il tag HTML canvas e le basi del disegno su canvas per la parte di markup, e le animazioni JavaScript e l'API delle animazioni basata su requestAnimationFrame per movimenti più fluidi. Per selezionare il tuo canvas in modo affidabile, consulta getElement e querySelector.