Lavorare con l’elemento canvas – Introduzione
Come ho già avuto modo di scrivere, nel corso degli anni ho sviluppato una malsana ossessione per i quaderni ad anelli (rigorosamente a quadretti) e le penne cancellabili. Ne possiedo decine, tutti contraddistinti da copertine pacchiane e pieni zeppi di appunti sui linguaggi di programmazione che uso per lavoro o per hobby. Visto che lo spazio di casa – ormai fagocitato da romanzi e fumetti – ormai scarseggia, ho deciso di provare a disintossicarmi dalla mia dipendenza sfruttando il blog.
Per cominciare, voglio provare a trasporre qui i miei appunti sulle pagine web dinamiche, a cominciare dai tag più interessanti dell’HTML5 per poi continuare con createJS, questa volta affrontato da coder puro (quindi senza nessun tool di supporto).
L’elemento Canvas
Partiamo da presupposto che mi legge sa come è fatto un documento HTML5. Per farla breve, l’HTML5 è il presente e il futuro del web, ha soppiantato tutti i plugin e imposto un nuovo standard imprescindibile per la navigazione online. Se siete completamente digiuno vi consiglio di fare una capatina qui o qui . Siccome il settore che più di tutti mi interessa è quello legato alle pagine web interattive, la prima cosa che studiai quando qualche anno fa l’HTML5 mosse i primi passi, furono le funzionalità mirate allo sviluppo di giochi e animazioni.
Il tag (o elemento) canvas sfrutta al massimo le peculiarità grafiche dell’HTML5. Con delle semplici istruzioni JavaScript “al volo” è possibile disegnare elementi raster di base senza plugin. Se invece si vuole realizzare qualcosa di più complesso, sono disponibili numerose librerie JavaScript pensate per lavorare con gli elementi canvas: strumenti capaci di creare elementi visivi statici e animati di grande impatto senza dover scrivere da zero listati chilometrici. Il mio preferito è createJS, ma c’e solo l’imbarazzo della scelta.
Prima di entrare nel vivo, faccio alcune precisazioni. Fin dagli albori della tecnologia alla base delle pagine web, il DOM, acronimo di Document Object Model, ci fornisce un modello rappresentativo di tutti gli oggetti in una pagina HTML. Attraverso delle istruzioni JavaScrpt ad hoc possiamo individuare – e quindi manipolare – i vari elementi di una pagina HTML. Il discorso vale anche per l’elemento canavas (che resta pur sempre un tag), tuttavia è importante ricordare che non possiamo ricorrere alle regole del DOM per identificare i singoli elementi visivi creati all’interno di un elemento canvas. Del resto, le figure create con quel tipo di approccio, non sono equiparabili a dei tag figli. Si tratta di una nozione scontata, ma io stesso ebbi qualche difficoltà ad afferrare all’epoca. Riepilogando: si usa JavaScript, ma seguendo le nuove regole previste per l’HTML5.
La sintassi di base
In soldoni, che diavolo è sto canvas? Risposta: un banalissimo tag HTML5 pensato per creare un’aera rettangolare all’interno della quale creare grafica con JavaScript. La sintassi di un elemento canvas è la seguente:
<canvas id="id_canvas" width="550" height="400"> Se stai leggendo questa frase il tuo browser non legge i tag canvas, quindi ti trovi su Marte. </canvas>
Se osserviamo il codice, abbiamo un tag di apertura, uno di chiusura e tre attributi. Per la precisione un id che chi permette di identificare il tag, e due dimensioni relative alla larghezza e altezza. Questo tipo di tag non è equiparabile ad un banale img, quindi non prevede attributi “diretti” come alt o src. Con esso ci limitiamo a creare un rettangolo che posizioneremo da qualche parte nella pagina web. Tutto quello che viene inserito tra il tag di apertura e chiusura non verrà mostrato dal browser. Infatti, agli albori dell’HTML5, questa porzione di codice veniva usata per scrivere delle frasi o caricare delle immagini alternative, che apparissero solo nei browser che non supportavano i canvas. Chiaramente, oggi quasi tutti i browser del pianeta riconoscono il tag in questione. Quindi è davvero difficile incappare in un terrestre non adeguatamente attrezzato.
Volendo fare una media tra i vari modelli, i vari browser web in circolazione supportano il tag in questione da circa dieci anni. Per la precisione, ecco una tabella che riporta da quale versione i principali browser sono compatibili con i canvas.
Tag HTML5 supportato | Browser web | Anno di rilascio del browser |
<canvas> | Internet Explorer 9 | 2011 |
<canvas> | Mozilla Firefox 2 | 2006 |
<canvas> | Chrome 4.0 | 2010 |
<canvas> | Safari 3.1 | 2008 |
<canvas> | Opera 9.0 | 2006 |
Quindi per riscontrare problemi dovremmo incappare in un visitatore (alieno) che naviga con un browser molto datato.
Una volta definita la sintassi del tag passiamo al codice vero e proprio. Il tag canvas serve solo ad identificare il campo da gioco, ma tutto il lavoro lo fa JavaScript. Quindi è sempre necessario scrivere un codice aggiuntivo nel documento HTML5. Facciamo un esempio al volo aggiungendo in un ipotetico <head> il codice riferito al al canvas dell’esempio precedente:
<head> <script type="text/ecmascript"> var ctx; function action() { var id_canvas = document.getElementById('id_canvas'); if (id_canvas.getContext) { var ctx = id_canvas.getContext('2d'); } } </script> </head>
Prime di andare avanti voglio precisare che il codice in questione non svolge nessun compito. Serve solo ad illustrare la sintassi generale corretta. Presupponendo di aver scritto questo codice nell’head di una pagina HTML5, abbiamo creato una variabile ctx alla quale passare, tramite il metodo getContext() il tipo di API usata per disegnare. Attualmente le uniche supportate in maniera unanime da tutti browser sono le API 2d. Quindi con questa assegnazione ci limitiamo a stabilire le “regole di ingaggio”. Osservando il codice, si può notare che l’assegnazione avviene all’interno di un’istruzione condizionale. Si tratta di quella che in teoria dovrebbe essere la sintassi canonica da usare tutte le volte. Analogamente a quanto detto in precedenza per il tag canvas, quando l’HTML5 muoveva i primi passi, non tutti i browser ne supportavano adeguatamente le funzionalità. Questo if() verifica il caso in cui l’oggetto id_canvas, che fa riferimento ad un tag tramite i metodi del DOM, restituisce true. In altre parole, verifica se supporta le API. Si tratta della versione al volo di un’altra tecnica molto usata fino a qualche tempo fa, e che si basava sull’uso della libreria modernizr. In ogni caso, si tratta di una sintassi – per quanto corretta – oggi ridondante. Quindi, anche se potreste incappare sul web in dei tutorial che mostrano la versione completa, lo stesso codice si può scrivere in modo molto più snello, senza ricorrere a librerie o istruzioni aggiuntive. Facciamo un breve esempio:
<!DOCTYPE html> <html lang="it"> <head> <script type="text/ecmascript"> var ctx; function action() { ctx= id_canvas.getContext("2d"); // disegna o anima qualcosa } </script> </head> <body onload="action()"> <canvas id="id_canvas" width="550" height="400"> Il tuo browser non supporta l'HTML5. </canvas> </body> </html>
Abbiamo un tag che identifica l’area, uno script nell’intestazione, e un evento onload che lo innesca nel corpo del documento. Tutto lavoro operativo viene quindi svolto da Javascript tramite la API canvas 2d. Lo schema generale è illustrato nella figura in alto. Tornando al listato, il codice usato nell’esempio può essere “cucinato” in molti modi. Ad esempio mettendo la porzione di JavaScript in un file .js da chiamare esternamente, oppure usando window.addEventListener(load,funzione) al posto dell’evento onload e così via. Insomma, si può variare la ricetta in base alle preferenze del cuoco-programmatore.
Qualche esempio concreto
Una volta chiariti i concetti essenziali entriamo nel vivo. Supponiamo di voler scrivere l’immancabile “Ciao Mondo!” che tutti programmatori usano quando testano per la prima volta un linguaggio ambiente di sviluppo, magari inserito in un rettangolo colorato. A questo indirizzo trovate l’esempio online e di seguito il codice:
<!DOCTYPE html>
<html lang="it">
<head>
<title>
Creare pagine interattive con l'elemento canvas - Esempio 1
</title>
<script type="text/ecmascript">
var ctx;
function action() {
ctx= id_canvas.getContext("2d");
//sfondo
ctx.fillStyle = "#ff9900";
ctx.fillRect(0, 0, 550, 400);
//testo
ctx.fillStyle = "#ffffff";
ctx.font = "20px Sans-Serif";
ctx.textBaseline = "top";
ctx.fillText ("Ciao Mondo!", 218, 180 );
//cornice
ctx.strokeStyle = "#eeeeee";
ctx.strokeRect(2, 2, 546, 396);
}
</script>
</head>
<body onload="action()">
<canvas id="id_canvas" width="550" height="400">
Il tuo browser non supporta l'HTML5.
</canvas>
</body>
</html>
Come si può osservare, sono state inserite delle istruzioni aggiuntive nella corpo della funzione action(). Abbiamo tre blocchi di codice: il primo crea un rettangolo colorato, il secondo un testo e il terzo una cornice. Attraverso le api canvas 2d, possiamo creare del testo grafico, disegnare figure geometriche o anche caricare immagini. Molte proprietà usate per controllare i vari elementi creati sono condivise: ad esempio fillStyle definisce il colore sia del rettangolo che del testo. Tutte le istruzioni fanno riferimento all’oggetto ctx tramite la solita sintassi del punto. Nel primo blocco di codice, tramite i metodi fillStyle() e fillRect() viene creato il rettangolo che useremo come sfondo. Il primo metodo specifica il colore e il secondo le coordinate e e le dimensioni del rettangolo. Il metodo fillRect() prevede quattro parametri: la coordinata x, la coordinata y, la larghezza e l’altezza.
Nella fattispecie abbiamo un rettangolo il cui vertice in alto a sinistra è posizionato in corrispondenza delle coordinate 0,0 (il vertice dell’area del browser), largo 550 pixel e alto 400.
Il blocco di codice successivo di consente di creare una scritta bianca con un font Sans Serif. La frase è collocata tramite la proprietà top nella parte alta della linea del testo. Infine, tramite i tre parametri del metodo fillText(), viene specificato cosa scrivere e quali sono le coordinate x e y. Il terzo e ultimo blocco di codice viene creta la cornicee. Il metodo strokeRect() funziona allo stesso modo del metodo fillRect(), solo che al posto di una campitura piena, viene creato il perimetro di un rettangolo. Tutte le istruzioni illustrate sono molto intuitive. Trovate un elenco dei metodi utili per creare grafica nel tag canvas sul sito w3school.
Analogamente a quanto avviene nei programmi di grafica visuale, quando creiamo grafica con i canvas, i vari elementi che compongono il tutto sono sovrapponibili uno sopra l’altro. Non avendo dei livelli nei quali disegnare usando un tool, vale l’ordine di lettura del codice da parte del browser. Quindi, tutti i vari “pezzi” sono sovrapposti dal basso verso l’altro grazie alla sequenza di istruzioni JavaScript. Nel nostro semplicissimo esempio, prima disegniamo un rettangolo colorato, poi un testo grafico ed infine una cornice vuota. Se avessimo invertito l’ordine delle istruzioni, il testo sarebbe stato coperto (e quindi nascosto) dal rettangolo.
Non abbiamo creato niente di particolare: una banale scritta collocata in’un’area colorata. La piccola grande rivoluzione introdotta dai canvas consiste nel fatto che quella in questione, è a tutti gli effetti un’immagine. Il testo non solo non è selezionabile, ma se dopo aver aperto il file HTML5 clicchiamo su di esso con il tasto destro del mouse, nel menu contestuale ci apparirà l’opzione per salvare il tutto come un immagine bitmap. Per la precisione, otterremo un’immagine png che comprende tutto il blocco presente nell’area definita dal canvas.
Facciamo un esempio al volo per aggiungere un’immagine alla nostra frase. Trovate l’esempio a questo indirizzo. Siccome la parte HTMl resta invariata, di seguito viene riportato solo il codice JavaScript:
<script type="text/ecmascript"> var ctx; var miaImg; function action() { ctx= id_canvas.getContext("2d"); //sfondo ctx.fillStyle = "#ff9900"; ctx.fillRect(0, 0, 550, 400); //testo ctx.fillStyle = "#ffffff"; ctx.font = "20px Sans-Serif"; ctx.textBaseline = "top"; ctx.fillText ("Ciao Mondo!", 218, 80 ); //immagine miaImg = new Image(); miaImg.onload = function(){ ctx.drawImage(miaImg, 147, 110); } miaImg.src = "h5.png"; //cornice ctx.strokeStyle = "#eeeeee"; ctx.strokeRect(2, 2, 546, 396); } </script>
Per ottenere questo risultato, ci siamo limitati ad aggiungere qualche riga di codice al listato iniziale. Nella fattispecie, all’inizio del codice viene dichiarata una variabile miaImg che rappresenta l’oggetto tramite il quale viene caricata un file esterno. Si potrebbe dichiarare anche nel corpo della funzione, ma per una mia abitudine tendo a mettere fuori dalle funzioni tutti gli oggetti che potrei voler controllare a prescindere dall’area di validità. Inoltre è stato aggiunto un nuovo blocco di codice (colorato diversamente nel listato) la cui funzione è quella di caricare un’immagine png esterna tramite il metodo drawImg() che ha come primo parametro l’oggetto da caricare e a seguire le coordinate x ed y. Il metodo drawImg() è collocato in una funzione onLoad , la quale ha il compito di posizionare l’oggetto esterno al caricamento. Infine la proprietà src specifica il percorso del file da caricare.
Creare effetti interattivi con i canvas
Quello che rende l’elemento canvas uno dei più importanti strumenti messi a disposizione dall’HTML5 è la possibilità di generare effetti animati e interattivi. Nel web odierno, che sia un banner o un istant game, e a prescindere dall’eventuale libreria JavaScript usata, alla base del progetto ci sarà quasi sempre un tag canvas. Facciamo un piccolo esempio al volo a partire dal codice precedente. Supponiamo di voler applicare un effetto di dissolvenza in entrata all’immagine esterna caricata tramite il metodo drawImg(). Cliccando su questo link trovate l’effetto finale. Di seguito vediamo come cambia il codice:
<script type="text/ecmascript"> var ctx; var miaImg; var alfa=0; var miaVar; function action() { ctx= id_canvas.getContext("2d"); //sfondo ctx.fillStyle = "#ff9900"; ctx.fillRect(0, 0, 550, 400); //testo ctx.fillStyle = "#ffffff"; ctx.font = "20px Sans-Serif"; ctx.textBaseline = "top"; ctx.fillText ("Ciao Mondo!", 218, 80 ); //immagine miaImg = new Image(); miaImg.onload = function(){ miaVar=setInterval(tr,100); } miaImg.src = "h5.png"; //cornice ctx.strokeStyle = "#eeeeee"; ctx.strokeRect(2, 2, 546, 396); } function tr(){ if(alfa<1){ alfa+=.01; ctx.globalAlpha = alfa; ctx.drawImage(miaImg, 147, 110); } else if(alfa>=1){ clearInterval(miaVar); } } </script>
Prima di entrare nello specifico, vediamo come funziona la trasparenza nei canvas. Per poter manipolare questo aspetto entra in gioco una proprietà denominata globalAlpha i cui valori variano da 0 (massima trasparenza) a 1 (massima opacità). Anche in questo caso si tratta di una proprietà che è possibile impostare per tutti gli elementi del nostro disegno usando come riferimento l’oggetto ctx. Per cui se usiamo una sintassi del tipo ctx.globalAlpha =0.5 impostiamo una trasparenza del 50%.
Come si può osservare dal listato, nella parte iniziale del codice, abbiamo dichiarato due nuove variabili: alfa e miaVar. La prima ci serve per variare in modo interattivo la trasparenza dell’immagine e la seconda per attivare un setInterval().
Piccolo inciso: siccome nei miei articoli mi rivolgo anche a chi non ha nessuna cognizione di programmazione (una vecchia abitudine che mi porto dietro da quando insegnavo informatica qualche annetto fa) non do nulla per scontato. Per cui, se non avete mai avuto modo di usare la funzione setInterval() ecco qualche link per una lettura veloce.
Al verificarsi dell’evento onLoad con il quale carichiamo l’immagine esterna, questa volta viene chiamato un metodo setInterval() che a sua volta chiama in modo ricorsivo una nuova funzione chiamata tr().
Cosa fa la funzione tr()? Niente di complicato: usa la variabile alfa per incrementare ogni 200 millisecondi la trasparenza dell’immagine. Se alfa è minore di 1 viene incrementata di 0,1 e passata a globalAlpha. Se è maggiore o uguale a 1, l’incremento viene interrotto tramite clearInterval().
Chiaramente, questa tecnica non vale solo per la trasparenza, ma per tutti gli aspetti di un elemento visivo collocato in un’area canvas. Con poche istruzioni possiamo muoverlo o modificarlo a nostro piacimento. Aspetto che intendo approfondire nei prossimi articoli.