Canvas
Cosa è il Canvas
L’oggetto canvas (rappresentato in html col tag <canvas>) è uno strumento che permette di creare un’area all’interno della pagina web in cui è possibile disegnare programmaticamente linee, forme, figure. E’ quindi un’area di grafica 2D.
I canvas sono molto usati nelle pagine html per questi scopi:
– arte visuale: ad esempio scritte artistiche, scritte a scorrimento, ecc. in particolare nel coding creativo[1]
– animazioni (in alternativa a SVG[2])
– grafici (es. a istogramma, barra, torta, ecc.)
– videogiochi
– è alla base della libreria WebGL per la grafica 3D
Vediamo come funziona.
Creazione ed utilizzo
L’area (rettangolare) in html la si inizializza con questo codice:
<canvas id=”canvas” width=”500″ height=”500″></canvas>
(larghezza ed altezza sono modificabili anche via CSS che JS).
Il resto dell’attività si svolge in Javascript.
Per usare il canvas bisogna eseguire prima di tutto queste due istruzioni:
const canvas = document.getElementById(“canvas”);
const ctx = canvas.getContext(“2d”);
L’oggetto ctx (Context) sarà l’oggetto che ci permette di disegnare nel canvas.
Il canvas è internamente una griglia dove ogni punto ha coordinate (x,y) con la posizione (0,0) in alto a sinistra.

L’attività di disegno consiste quindi nel disegnare forme all’interno della griglia, dando le coordinate e le dimensioni.
Rettangoli
Comando | Esempio | Note |
ctx.fillRect(x, y, width, height); | ctx.fillRect(25, 25, 100, 100); | Crea un rettangolo (pieno) dalle coordinate x,y e dimensioni width ed height. |
ctx.strokeRect(x, y, width, height); | ctx.strokeRect(45, 45, 60, 60); | Crea un rettangolo (vuoto) dalle coordinate x,y e dimensioni width ed height. |
ctx.clearRect(x, y, width, height); | ctx.clearRect(50, 50, 50, 50); |
Se si eseguono le seguenti 3 istruzioni:
ctx.fillRect(25, 25, 100, 100);
ctx.strokeRect(45, 45, 60, 60);
ctx.clearRect(50, 50, 50, 50);
Spiegazione:
– fill crea un rettangolo pieno
– stroke un rettangolo pieno
– clear cancella tutto quanto c’è nel rettangolo
Cosa si ottiene?

Notare che clearRect può essere usato per cancellare tutto il canvas.
Disegnare forme con path
E’ possibile disegnare forme complesse utilizzando il path.
Vediamo un esempio: se voglio disegnare una linea che va da 100, 100 a 300, 300 uso queste istruzioni:
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(300,300);
ctx.moveTo(100,300);
ctx.lineTo(300, 300);
ctx.moveTo(100,100);
ctx.lineTo(100, 300);
ctx.lineWidth = 2;
ctx.stroke();
Si dovrebbe ottenere un triangolo rettangolo.
Riassumendo:
– beginPath da inizio al disegno;
– moveTo sposta la “penna” su una coordinata;
– lineTo disegna una linea dalla coordinata attuale alla nuova coordinata;
– lineWidth da la dimensione della linea (istruzione non obbligatoria);
– stroke esegue la “commit”, ovvero rende il disegno effettivo.
Archi, cerchi e circonferenze
Vediamo ora come si disegna una circonferenza:
ctx.beginPath();
ctx.arc(250, 250, 200, 0, 2 * Math.PI);
ctx.lineWidth = 1;
ctx.stroke();
Come si può vedere, arc riceve 5 parametri:
- le coordinate x ed y;
- la dimensione del raggio;
- gli ultimi due parametri sono l’angolo di inizio e di fine dell’arco: nel caso del cerchio sono 0 e 2π.
Se invece vogliamo un cerchio pieno usiamo fill.
ctx.beginPath();
ctx.arc(250, 250, 50, 0, 2 * Math.PI);
ctx.lineWidth = 1;
ctx.fill();
Riassumendo:
– stroke indica un disegno solo del contorno;
– fill indica un disegno della forma piena.
Se vogliamo disegnare più forme diverse con strutture diverse è sufficiente che replichiamo la struttura:
ctx.beginPath();
...
ctx.stroke() o ctx.fill();
I colori
Si può disegnare a colori con queste istruzioni:
ctx.fillStyle = "#ff0000";
ctx.strokeStyle = "#00cc22";
dove il codice altro non è che la terna RGB esadecimale.
Esempio: movimento di una pallina 2D
In questo esempio si proverà a simulare il comportamento di una pallina in 2D. Qui uno screenshot di come si può vedere la “pallina” (che altro non è che un cerchio).

Vediamo nello specifico il funzionamento di questo algoritmo.
1) La pallina viene disegnata da una funzione render
Questa riceve come parametro una coppia di coordinate (x,y) e che prima cancella l’intero canvas (clearRect sull’intera dimensione del canvas) e poi ridisegna la pallina nella posizione richiesta. La pallina come detto viene simulata disegnando un cerchio di piccole dimensioni (5-10 pixel).
2) Il refresh
L’effetto visivo dello spostamento della pallina si ottiene mediante aggiornamento della posizione (x,y) della pallina stessa. Dobbiamo quindi scrivere una funzione refresh che calcola le nuove coordinate in cui si dovrà trovare la pallina in base alla sua velocità e la sua direzione. Questa funzione dovrà essere eseguita periodicamente tramite setInterval con una frequenza abbastanza veloce da rendere il movimento fluido per l’occhio umano. E’ in questo modo che funzionano tutti i videogiochi.
Riassumendo: ad ogni esecuzione di setInterval verrà eseguita:
– refresh(): funzione che ricalcola le nuove coordinate (x,y)
– render(): ridisegna la pallina
3) Come calcolare le nuove coordinate.
La pallina ha una direzione (angolo ) e una velocità. La nuova posizione va aggiornata ogni t millisecondi, e corrisponderà ad uno spostamento in diagonale, ovvero sia orizzontale (verso destra o sinistra) che verticale (verso l’alto o verso il basso). Qui un esempio di spostamento con algolo di π/6 (30 gradi):

La traslazione della pallina ha una componente orizzontale, che corrisponde al coseno di ed una componente verticale pari al seno di
, entrambi moltiplicati per la velocità della pallina stessa.
Quindi una pallina che all’istante t è nella posizione (x,y) nell’istante t’ (ad esempio dopo 20 millisecondi) si troverà nella posizione (x’,y’):

Quindi se ad esempio all’istante t la pallina si trova nella posizione (100,100) ed ha velocità di 1 pixel/ms e angolo 30° all’istante t’ (=t+20ms) si troverà:

Ricordare sempre che nei canvas il punto in alto a sinistra è (0,0). Quello che si deve fare nell’esercizio è quindi semplicemente calcolare le nuove posizioni con la formula sopra riportata.
4) Urti con le pareti.
In caso di urto con le pareti supponiamo che sia elastico, quindi la velocità rimane la stessa, ciò che cambia è l’angolo di uscita, che per effetto del rimbalzo cambia secondo la seguente regola:
– se l’urto è verso le pareti verticali (dx o sx) il nuovo angolo è:

se l’urto è verso le pareti orizzontali (sopra o sotto) il nuovo angolo è:

A livello di codice, dopo ogni traslazione bisogna verificare se la pallina sta uscendo dall’area del canvas. Se si vede che sta uscendo bisogna ricalcolare il nuovo angolo, così alla traslazione successiva la pallina avrà cambiato direzione, per effetto appunto del rimbalzo.
Qui un esempio di codice:
const refresh = (ball) => {
let newx = ball.x + ball.speed * Math.cos(ball.angle);
let newy = ball.y + ball.speed * Math.sin(ball.angle);
if (newx < size || newx > width-size) {
ball.angle = Math.PI - ball.angle;
} else if (newy < size || newy > height-size) {
ball.angle = 2 * Math.PI - ball.angle;
} else {
ball.x = newx;
ball.y = newy;
}
};
Dove size è la dimensione in pixel della pallina, ball è un dizionario che contiene x, y, speed e angle della pallina.
Si lascia allo studente il resto dell’elaborazione come semplice esercizio.
Eventi da tastiera
Per intercettare un evento da tastiera in una pagina web è sufficiente eseguire il seguente codice:
document.onkeydown = function(e) {
console.log(String.fromCharCode(e.keyCode)+" --> "+e.keyCode);
};
Il valore di keyCode indica il codice numerico del tasto premuto.
Qui un elenco di codici.
Keyboard key Pressed | JavaScript Key Code value |
backspace | 8 |
tab | 9 |
enter | 13 |
shift | 16 |
ctrl | 17 |
alt | 18 |
pause/break | 19 |
caps lock | 20 |
escape | 27 |
page up | 33 |
Space | 32 |
page down | 34 |
end | 35 |
home | 36 |
arrow left | 37 |
arrow up | 38 |
arrow right | 39 |
arrow down | 40 |
print screen | 44 |
insert | 45 |
delete | 46 |
0 | 48 |
1 | 49 |
2 | 50 |
3 | 51 |
4 | 52 |
5 | 53 |
6 | 54 |
7 | 55 |
8 | 56 |
9 | 57 |
a | 65 |
b | 66 |
c | 67 |
d | 68 |
e | 69 |
f | 70 |
g | 71 |
h | 72 |
i | 73 |
j | 74 |
k | 75 |
l | 76 |
m | 77 |
n | 78 |
o | 79 |
p | 80 |
q | 81 |
r | 82 |
s | 83 |
t | 84 |
u | 85 |
v | 86 |
w | 87 |
x | 88 |
y | 89 |
z | 90 |
left window key | 91 |
right window key | 92 |
select key | 93 |
numpad 0 | 96 |
numpad 1 | 97 |
numpad 2 | 98 |
numpad 3 | 99 |
numpad 4 | 100 |
numpad 5 | 101 |
numpad 6 | 102 |
numpad 7 | 103 |
numpad 8 | 104 |
numpad 9 | 105 |
multiply | 106 |
add | 107 |
subtract | 109 |
decimal point | 110 |
divide | 111 |
f1 | 112 |
f2 | 113 |
f3 | 114 |
f4 | 115 |
f5 | 116 |
f6 | 117 |
f7 | 118 |
f8 | 119 |
f9 | 120 |
f10 | 121 |
f11 | 122 |
f12 | 123 |
num lock | 144 |
scroll lock | 145 |
semi-colon | 186 |
equal sign | 187 |
comma | 188 |
dash | 189 |
period | 190 |
forward slash | 191 |
open bracket | 219 |
back slash | 220 |
close braket | 221 |
single quote | 222 |
[1] Il “coding creativo” è l’uso della programmazione per produrre applicazioni visuali, che mostrano disegni ed animazioni: possono essere sfondi, salvaschermi, scritte in movimento, ecc. tutte prodotte da algoritmi generativi.
[2] il formato SVG permette di disegnare oggetti vettoriali tramite una descrizione testuale in XML