Nel corso delle lezioni abbiamo assistito al graduale arricchimento del progetto guida; in questo articolo ne esploreremo alcune possibili evoluzioni attraverso l’utilizzo delle API HTML5 apprese finora.
Il canvas nella dashboard
È possibile fare in modo che dalla dashboard (dashboard.html) si possa visualizzare il contenuto, attuale e salvato, del canvas di una data fiveboard con un meccanismo simile a quello utilizzato per la memorizzazione su localStorage
. Per ottenere questo risultato possiamo sostituire il blocco legato al comando ‘richiedi_testo
’ nel file application.js con:
case 'richiedi_testo':
evento.ports[0].postMessage('testo_corrente:' +
document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value);
evento.ports[0].postMessage('testo_memorizzato:' +
localStorage.getItem("fb_" + titolo_fiveboard));
evento.ports[0].postMessage('canvas_corrente:' +
ctx.canvas.toDataURL('image/png'));
evento.ports[0].postMessage('canvas_memorizzato:'+
localStorage.getItem("canvas_fb_" + titolo_fiveboard));
break;
A questo punto dobbiamo far sì che la dashboard sappia ricevere ed interpretare correttamente questi 4 distinti messaggi, modifichiamo quindi il blocco che fa seguito al comando attendi_testo
in dashboard.html:
case 'attendi_testo':
evento.ports[0].onmessage = function(e){
nome_comando = e.data.split(":")[0]
valore_comando = e.data.substr(nome_comando.length + 1);
elemento = document.getElementById(nome_comando);
switch (elemento.tagName){
case 'CANVAS':
elemento.width = elemento.width;
if(valore_comando == 'null') return
var context = elemento.getContext('2d');
var immagine = new Image();
immagine.src = valore_comando; context.drawImage(immagine,0,0);
break;
case 'TEXTAREA':
if(valore_comando == 'null')
valore_comando = '';
elemento.value = valore_comando;
break;
}
}
break;
Nel frammento di codice appena esposto facciamo uso dell’attributo tagName
per determinare il tipo di elemento che deve contenere le informazioni appena ricevute. In questo modo il comando ‘testo_memorizzato:contenuto_del_testo
’ inviato dalla fiveboard si traduce nella valorizzazione dell’elemento ‘testo_memorizzato
’ a ‘contenuto_del_testo’ nella dashboard.
Completiamo questa evoluzione inserendo gli elementi di markup all’interno di dashboard.html:
</ol>
<section>
<h1>In Osservazione:</h1>
<textarea id="testo_corrente" placeholder="testo_corrente" readonly></textarea>
<textarea id="testo_memorizzato" placeholder="testo_memorizzato" readonly></textarea>
<canvas id="canvas_corrente">Canvas Corrente</canvas>
<canvas id="canvas_memorizzato">Canvas Memorizzato</canvas>
</section>
Ecco uno screenshot dell’implementazione funzionante (figura 1):
Com’è possibile notare, ora alla pressione sul link ‘più informazioni’ la dashboard è in grado di mostrare sia testo che canvas, attuali e memorizzati, della fiveboard richiesta.
Esportazione in SVG
Potrebbe essere interessante, in questo contesto, offrire ai nostri utenti una esportazione in SVG del contenuto del canvas. In un prossimo futuro il task in questione potrà essere risolto in modo efficace utilizzando la funzione canvas.toDataUrl
con parametro ‘image/svg+xml
’ ; purtroppo ad oggi i browser supportano soltanto un'esportazione in formato png e quindi dovremo costruire qualcosa di più articolato per soddisfare questo comportamento.
Iniziamo con l’aggiungere questa funzione in canvas.js:
EsportaInSVG = function(){
var bb = new BlobBuilder();
bb.append( "" +
"<?xml version='1.0' standalone='no'?>" +
"<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" +
" 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" +
"<svg width='300px' height='300px' viewbox='0 0 300 300'" +
" xmlns='http://www.w3.org/2000/svg' version='1.1'>" +
" <title>SVG: " + titolo_fiveboard + "</title>" +
" <desc>Un esportazione in SVG del canvas disegnato</desc>" +
" <path d='" + svg_path + " z' fill='transparent'" +
" stroke='black' stroke-width='1'/>" +
"</svg>"
);
window.open(window.createObjectURL(bb.getBlob('image/svg+xml')));
}
L’oggetto BlobBuilder
fa parte delle File API: Writer, un set di funzionalità che gravitano attorno alle specifiche HTML5 e consentono di costruire veri e propri file da far scaricare all’utente o ai quali far puntare il browser. Al momento questi metodi sono ancora abbastanza sperimentali, nonostante già supportati in Chrome, Firefox e WebKit. In questo esempio la variabile bb
viene valorizzata con un file SVG nel quale sono inseriti dinamicamente i contenuti delle variabili titolo_fiveboard
, il titolo della fiveboard, e svg_path
, della cui costruzione ci occuperemo a breve. Nell’ultima riga della funzione il browser apre una nuova finestra verso una URL univoca generata dal metodo createObjectURL
. Tale URL punta al contenuto della variabile bb
esposto con MIME image/svg+xml
.
Occupiamoci ora della generazione della variabile svg_path
che dovrà contenere le istruzioni per ricostruire il percorso generato dall’utente sul canvas. Modifichiamo il file canvas.js come segue:
var ctx = null;
var started = false;
var svg_path = "";
iniziaDisegno = function(evento){
ctx.beginPath();
ctx.moveTo(evento.offsetX,evento.offsetY);
svg_path += "M " + evento.offsetX + " " + evento.offsetY + " ";
started = true;
}
disegna = function(evento){
if(started){
ctx.lineTo(evento.offsetX,evento.offsetY);
svg_path += "L " + evento.offsetX + " " + evento.offsetY + " ";
ctx.stroke();
}
}
Il trucco sta nel far seguire all’istruzione che insiste sul canvas la corrispondente variazione da applicare al path svg. In questo modo una particella ‘M x y’ verrà concatenata a svg_path
per ogni istruzione moveTo
inviata al canvas, lo stesso accadrà per l’istruzione lineTo
, seguita da una concatenazione di ‘L x y’.
L’ultimo passo prima del completamento di questa evoluzione consiste nell’aggiungere il pulsante preposto a far scatenare la funzione EsportaInSVG
:
</button>
<button type="button" onclick="EsportaInSVG();">
Esporta il canvas corrente in SVG
</button>
</menu>
Per testare questa modifica ricorriamo a Chrome (figura 2):
Ed ecco il risultato! Mentre la barchetta nella finestra in sfondo risiede su di un canvas quella in primo piano è creata a partire da un file SVG; notate inoltra il curioso URL di questa finestra, generato in tempo reale dalla funzione di cui abbiamo parlato poco fa.
Non ci resta che rimandare alla demo per un test. Il codice è disponibile per il download.
Un passo avanti
Con quest’ultima evoluzione ci apprestiamo a congedare il progetto guida, utile assistente durante le passate lezioni. Per chi fosse interessato a mantenere questo applicativo come base per proprie sperimentazioni ecco alcuni possibili e sfidanti implementazioni effettuabili:
- Fare in modo che il viewer.html possa seguire in tempo reale anche l’atto del disegno sul canvas della fiveboard che sta osservando, così come oggi avviene per il testo. Questa modifica coinvolge la definizione di un protocollo snello per la trasmissione di dati di questo tipo attraverso un WebSocket.
- Refactoring! Perché non provare a fare in modo che i comandi che giungono alle pagine web nel formato scelto per convenzione come ‘
nome_comando:valore_comando
’ siano trasformati in eventi custom e propagati attraverso il DOM ? La sintassi potrebbe essere questa:
worker.port.onmessage = function(evento){
nome_comando = evento.data.split(":")[0]
valore_comando = evento.data.substr(nome_comando.length + 1);
var evento_custom = document.createEvent("CustomEvent");
evento_custom.initCustomEvent(nome_comando, true, true, valore_comando);
document.dispatchEvent(evento_custom);
}
In questo modo si potrebbe ottenere una struttura molto più elegante, nella quale ogni aspetto dell’applicazione si sottoscrive autonomamente agli eventi dei quali necessita.
Conclusioni
Con questa lezione si conclude il ciclo dedicato all’esplorazione delle nuove API rese disponibili dall’HTML5. Prima della conclusione di questa guida trova spazio un'ultima lezione, la prossima, incentrata su tutte le tematiche che legano le specifiche al mondo reale. Verranno quindi trattati temi come il feature detection ed elencate librerie che possono, di caso in caso, sopperire alla mancanza di particolari funzionalità. Proprio a questo tema ricordiamo che il progetto guida è nato e cresciuto con finalità didattiche e di sperimentazione e per questa ragione è molto carente in termini di compatibilità tra browser e graceful degradation; a ben pensare anche perseguire queste tematiche potrebbe essere un ottimo esercizio da svolgere in autonomia dopo aver letto la prossima lezione!