Scalable Vector Graphics multipiattaforma con svgweb

// <![CDATA[ //convert table to json //@param t is the id of the table (string) //returns a json object with an array containing an object for each row function tableToJSON(t){ var table = document.getElementById(t), header_row = table.rows[0], n_headers = header_row.cells.length, n_rows = table.rows.length, keys = [], n_keys = 0, json_data = [], json = {};

L’articolo prosegue sotto

//rip through every row to create the JSON data objects //assume the first row contains the headers for(var i=0; i < n_rows; i++){ if(i===0){ for(var j=0; j < n_headers; j++){ keys.push(header_row.cells[j].firstChild.nodeValue.toLowerCase()); } } else{ var this_row = table.rows[i]; var data = {}; n_keys = keys.length; for( var j=0; j < n_keys; j++){ data[keys[j]] = this_row.cells[j].firstChild.nodeValue; } json_data.push(data); } } //hide the table table.style.display = 'none'; json[t] = json_data; return(json); } function init(){ //get the svg map via the 'contentDocument' property var map_svg = document.getElementById('state-map').contentDocument; //tableToJSON will return a JSON object with our data var map_json = tableToJSON('results-data'); //grab the actual array from the json object var map_data = map_json['results-data']; //get the array length so you don't calculate it for every loop iteration var n_map_data = map_data.length; //loop through the map data and assign properties to each state on the map for(var i=0; i < n_map_data; i++){ //get the state on the map based on the ID in the data array //in this case, the ID is the state abbreviation var map_state = map_svg.getElementById(map_data[i].state); if(map_data[i].change === '-2 or more' ){ map_state.style.fill = '#B26600'; } else if (map_data[i].change === '-1'){ map_state.style.fill = '#FF9200'; } else if (map_data[i].change === '+1'){ map_state.style.fill = '#19ABFF'; } else if (map_data[i].change === '+2 or more'){ map_state.style.fill = '#0071B2'; } //assign the state a title, in this case the name of the state and the change in seats map_state.title = map_data[i].state + ' will have a change of ' + map_data[i].change + ' seats'; //give each state an event //for simplicity, the event is a simple alert that displays the title param we just assigned //yes, addEventListener even works in IE, thanks to svgweb map_state.addEventListener('mouseup', function(e){ alert(e.target.title); }, false); } } window.onsvgload = init; // ]]>

Povera Scalable Vector Graphics! E' uno standard ufficiale fin da prima del rilascio di IE6 ma nonostante ciò non ha mai trovato molto pubblico sul web, sicuramente non quello che si merita. E proprio quando SVG stava cominciando ad essere supportato dai browser, ecco che arriva il tag canvas a rubargli l'idea delle immagini generate dinamicamente lato client.

Nonostante tutta l'attenzione che si dedica a canvas, c'è ancora un po' di posto per il buon vecchio SVG, particolarmente per gli sviluppatori che stanno cercando di rimpiazzare i plugin come Flash per la visualizzazione dei dati.

Canvas e SVG#section1

Una delle ragioni per cui Flash è stato scelto dal grande pubblico per la visualizzazione dei dati è la rich graphics API che è stata introdotta alcune versioni fa. Questa API grafica, nativa del linguaggio di programmazione di Flash ActionScript, rende possibile disegnare in maniera dinamica diagrammi, grafici e mappe in maniera programmatica, basandosi sui dati o sull'input dell'utente, senza dover armeggiare con del middleware server-side, ma solo ed esclusivamente nel browser. Questa fu una rivelazione quando venne introdotta e apportò molta innovazione nella grafica informativa interattiva.

Qualche anno fa, Apple introdusse il tag canvas per permettere agli sviluppatori che creavano le widget per la Dashboard di utilizzare un meccanismo per disegnare forme arbitrarie usando solo HTML. Quella proposta, come sicuramente saprete, è ora una parte ufficiale della specifica di HTML5. I metodi per disegnare che canvas ha introdotto sono effettivamente piuttosto simili a quelli che gli sviluppatori ActionScript stavano usando da un po'. Infatti, scommetterei che chiunque abbia mai fatto dei disegni dinamici in Flash potrà spostarsi su canvas piuttosto facilmente. Raddoppierò quella scommessa e vi dirò che le similitudini tra il disegnare con ActionScript e con canvas siano almeno in parte responsabili del successo di canvas.

Ci sono anche librerie per il disegno basate su JavaScript, come Raphaël, in effetti, probabilmente avrete già letto l'eccellente articolo di Brian Suda sull'utilizzo della libreria JavaScript Raphaël per rendere SVG dinamicamente nel browser. Raphaël è grandioso per rimuovere molta della complessità di SVG e per lavorare cross-browser, su su fino a IE6: ma una cosa che non fa è lasciarvi caricare dei files SVG esterni, similmente a come carichereste un'immagine usando il tag img.

Cosa c'entra la possibilità di caricare un file SVG? Beh, da una parte, significa che potete usare dei tool di terze parti come Illustrator o l'eccellente (e gratuito) Inkscape per disegnare le vostre immagini SVG, che possono poi essere caricate e manipolate nel browser usando JavaScript. La differenza riguarda solo come approcciare il problema: ActionScript, canvas e Raphaël vi permettono di disegnare immagini in maniera programmatica con il codice, mentre SVG vi permette di disegnare le immagini in un software di disegno, caricare quell'immagine in un browser e aggiungere lo script.

Per far sì che tutto questo funzioni, useremo una libreria JavaScript open-source chiamata svgweb per caricare il file SVG, aggiungere gli eventi e gestire i problemi di compatibilità cross-browser. Il modo in cui svgweb gestisce i browser più vecchi, come IE, è alquanto geniale: piuttosto che cercare di mappare SVG sulla API di disegno proprietaria di IE, che è quello che fa Raphaël, si sono creati il proprio renderer SVG in Flash. Questo renderer SVG basato su Flash carica i files SVG esterni e gestisce anche gli eventi JavaScript che arrivano dal browser, in maniera completamente trasparente.

La libreria svgweb scopre se il browser ha il supporto nativo per SVG e, se non ce l'ha, fa l'embed del livello Flash che traduce SVG nei comandi di disegno nativi di Flash. (Questo è simile a come alcuni player audio e video in HTML5 usano Flash come meccanismo di fallback per far vedere i video codificati in H.264 nei browser che non supportano il tag video o i video codificati in H.264). Gli eventi in stile DOM sono gestiti e passati avanti e indietro tra il livello Flash ed il browser così che voi dobbiate solo scrivere una sola volta la logica dell'evento, in JavaScript, invece di dover scrivere anche gli handler ActionScript. Ciò significa che svgweb rende disponibile SVG a qualcosa come il 95% dei browser, inclusi i dispositivi mobili come iPhone e iPad che gestiscono SVG nativamente. (I dispositivi Android al momento non supportano nativamente il rendering SVG, ma ci stanno lavorando).

Prima di proseguire, voglio solo sottolineare che non sto dicendo che un metodo sia migliore di un altro: penso che ci sia posto per canvas, Raphaël e SVG. C'è probabilmente addirittura un po' di sovrapposizione, che è una buona notizia per gli sviluppatori perché ci permette di scegliere l'approccio che fa più al caso nostro. Ho scoperto che il canvas sembra particolarmente adatto per il rendering immediato e client side di grafica raster: i grafici semplici sono un ottimo uso per il canvas e l'eccellente libreria Flot rende estremamente semplice la creazione di grafici con JavaScript. SVG è particolarmente indicato per mostrare forme complesse, come una mappa, che potreste voler disegnare in un'applicazione come Illustrator prima e poi utilizzare JavaScript per linkarci i dati dinamici.

Una mappa dati con SVG#section2

Ecco una richiesta che il mio team di designer e sviluppatori alla msnbc.com si sente fare molto spesso: creare una mappa per illustrare un articolo di giornale. Le mappe sono comode e noi abbiamo sviluppato un insieme di template per pubblicarle rapidamente, alcuni dei quali sono basati su servizi esistenti come Google o Bing e altri sono applicazioni Flash personalizzate. Si è già detto molto sul potere di sviluppare qualcosa sfruttando Google maps o perfino creare il vostro motore di mappe tile-based. Tuttavia, per la grafica informativa, le mappe tile-based possono essere eccessive e perfino distrarre: spesso è meglio avere un disegno custom.

Un classico esempio potrebbe essere una mappa di una nazione che mostra dei dati per stato, come le mappe rosse e blu della notte delle elezioni americane. Per questo esempio, useremo i dati del più recente censimento degli Stati Uniti per mostrare quali stati hanno guadagnato o perso dei rappresentanti.

Prima di cominciare, dobbiamo configurare alcune cose. Siccome alcuni browers hanno delle difficoltà a caricare lo script ed il file Flash necessari direttamente dal file system del vostro computer, è meglio farglieli arrivare da un web server. Potreste caricare tutto sul vostro host web ma probabilmente avete un webserver proprio nel computer su cui state sviluppando. Su Mac, create una nuova cartella chiamata svg-infonella cartella Sites nella home directory, poi andare su System Preferences > Sharing ed abilitare Web Sharing. Digitate nella barra indirizzo del browser http://localhost/~your-username/svg-info (sostituendo your-username con lo username che state usando) e vedrete la cartella svg-info nel browser.

Una volta fatto ciò, vi servirà una mappa, in SVG. Potete trovarne una da diverse fonti: il grafico che lavora con voi potrebbe crearne una in Illustrator o usare un tool come ArcGIS per generarne una partendo da uno shapefile. Oppure potete prenderne una da Wikipedia, che ha un'intera collezione di mappe SVG gratuite, inclusa una mappa vuota degli Stati Uniti con ciascuno stato con la sua forma etichettata. Assegnare un'etichetta correttamente a ciascuna forma è importante quando viene il momento di creare un link tra i dati e la mappa: fortunatamente, i files SVG hanno un DOM simile a quello dei files HTML, con ciascuno stato contenuto all'interno del proprio nodo etichettato. Questo è sicuramente qualcosa da tenere a mente se state generando le vostre mappe o forme.

Questa mappa è piuttosto grande per la maggior parte delle pagine web, quindi la prima cosa da fare è rimpicciolirla di un po', usando Illustrator o Inkscape, entrambe apriranno ed editeranno i files SVG nativamente. Questo mostra già uno dei vantaggi dell'uso di SVG: un grafico può aprire ed editare file usando i tool che utilizza tutti i giorni. Ho ridotto la mappa ad una larghezza di 500 pixel così che entri in una pagina. Assicuratevi di ridurre anche il documento che la contiene. (In Illustrator, andate su File > Document Setup > Edit Artboards e poi impostate “Fit Artboard to Artwork bounds” dai presets.)

Poi, vi servono dei dati, che scriveremo nella pagina come una tabella, per renderli il più accessibili possibile. La tabella sarà simile a questa:

 
<table id="results-data">
 <tr>
  <th>State</th>
  <th>Change</th>
 </tr>
 <tr>
  <td>AL</td>
  <td>0</td>
 </tr>
 <!-- snip! -->
 <tr>
  <td>WI</td>
  <td>0</td>
 </tr>
</table>
 

Ora, questa tabella non riflette esattamente quelle complessità del mondo reale in cui incappate con dei dati reali, ma dovrebbe andare bene per i nostri scopi.

Vi starete chiedendo perché dobbiamo scrivere una tabella se lo scopo è quello di mostrare una grafica e la risposta sta tutta nel rendere i dati accessibili. Gli spider dei motori di ricerca e gli screen reader, anche quelli JavaScript-aware, probabilmente non interpretano i dati sulla vostra mappa in alcun modo che abbia senso, quindi questa tabella li aiuterà ad indicizzare i vostri dati. SVG è, in teoria, più accessibile della grafica raster come JPEG, GIF e PNG (innanzitutto, SVG è scalabile quindi i lettori ipovedenti potranno ingrandirla senza perdere dettagli) perché l'immagine stessa è descritta come XML. La realtà, tuttavia, è che il supporto per l'accessibilità in SVG è ancora abbastanza agli albori quindi non potete contare sul fatto che gli screen reader saranno in grado di accedere all'informazione presente nello SVG. Un'umile tabella funzionerà qui a meraviglia.

Con questi dati sulla pagina, cominciamo ora a scrivere del JavaScript per farci qualcosa di più interessante. Per prima cosa, scaricate l'ultima versione dei files svgweb (l'ultima release ufficiale è dell'Agosto scorso ma una nuova release è prevista a breve) e unzippateli.

Aprite la cartella svgweb che avete unzippato e poi aprite la cartella src e copiate svg.js, svg.swf e svg.htc in una cartella sul vostro webserver. Usare una sotto-cartella come js va bene, assicuratevi solamente di tenere tutti e tre i files nella stessa cartella. Ora, includete svg.js come primo tag script nel head del vostro file HTML.

<script src="js/svg.js"></script>

Ora, scriviamo una funzione che prenda i dati dalla tabella e li converta in un oggetto JSON. Mettete tutto ciò in un tag script dopo il sorgente dello script per includere la libreria svgweb (gli a-capo sono segnati da » —Ed.):

 
//convert table to json
//@param t is the id of the table (string)
//returns a json object with an array containing an object for each row
function tableToJSON(t){
    var table = document.getElementById(t),
        header_row = table.rows[0],
        n_headers = header_row.cells.length,
        n_rows = table.rows.length,
        keys = [],
        n_keys = 0,
        json_data = [],
        json = {};
 
    //rip through every row to create the JSON data objects
    //assume the first row contains the headers
    for(var i=0; i < n_rows; i++){
        if(i===0){
            for(var j=0; j < n_headers; j++){
               keys.push(header_row.cells[j].firstChild»
.nodeValue.toLowerCase());
            }
        } else{
            var this_row = table.rows[i]
            var data = {};
            n_keys = keys.length;
 
            for( var j=0; j < n_keys; j++){
                data[keys[j]] = this_row.cells[j].firstChild.nodeValue;
            }
 
            json_data.push(data);
        }
 
    }
 
    json[t] = json_data
    return(json);
}
   

Non c'è molto da dire su questa funzione: prende un singolo parametro (il nome della tabella) e poi fa un loop in tutte le righe per assemblarle e ritornare un oggetto JSON. Assume che la prima colonna della tabella siano label per ciascuna colonna, che vengono poi usati per ciascuna coppia nome-valore. Data la tabella di cui sopra, il nostro oggetto JSON sarà così:

 
{ "results-data": [
    {"state": "AL", "change": 0},
     //... snip! ...
    {"state": "WI", "change": 0}
  ]
}
 

(Breve nota di produzione: questo è un tentativo di mantenere il codice accessibile senza approfondire i dettagli delle implementazioni backend. Le vostre esigenze, ad esempio, potrebbero richiedere un feed JSON separato dal vostro database).

I dati sono a posto, aggiungiamo la mappa. Useremo un tag object per caricare la mappa SVG come una risorsa esterna, con un commento condizionale per gestire IE. L'object specifico per IE usa alcuni parametri differenti, ossia src invece di data e classid al posto di type. Notate inoltre che dei files SVG che caricate, la maggior parte arriva dalla stessa sorgente (il vostro webserver) come lo stesso file HTML: non sono permessi cross-site scripting.

 
<div id="results-map">
   <!--[if IE]-->
       <object src="states.svg" classid="image/svg+xml"
           width="500" height="375"
           id="state-map" alt="Interactive map graphic showing»
electoral gains and loses per state as of the 2010 census">
   <!--[endif]-->
   <!--[if !IE]-->
       <object data="states.svg" type="image/svg" 
           width="500" height="375"
           id="state-map" alt="Interactive map graphic showing»
electoral gains and loses per state as of the 2010 census">
   <!--![endif]-->
       </object>
</div>
 

Tutto ciò dovrebbe essere abbastanza semplice. L'attributo src/data (a seconda del browser) punta al file SVG: la larghezza e l'altezza dovrebbero essere impostate almeno alla dimensione a cui avete salvato prima il file SVG. Dovreste inoltre dare a ciascun oggetto lo stesso id, per riferimenti futuri. Notate che impostare la larghezza o l'altezza nel tag object non influisce sulla dimensione del file embedded, ma solo sull'oggetto che lo contiene. Se la larghezza/altezza dell'oggetto sono più piccole, vedrete le barre di scorrimento, se sono maggiori, ci saranno degli spazi bianchi. Gli attributi alt forniranno delle guide ai bot e agli screen reader.

Se caricate questa pagina nel vostro browser, vedrete una mappa grigia degli Stati Uniti d'America. E se usate la funzione zoom nativa del browser, vedrete che la grafica è, in effetti, scalabile.

Adesso è il momento di collegare i dati alla mappa. Così come capita spesso con la programmazione JavaScript, dovrete assicurarvi che la mappa sia stata caricata prima di provare ad accedervi con lo script. Fortunatamente, svgweb fornisce un evento onsvgload su cui potete fare listen perché si attiva una volta che SVG è stato caricato. Gli faremo chiamare una funzione init che gestirà il processing dai dati alla mappa:

 
function init(){
 
}
 
window.onsvgload = init;
 

All'interno della funzione init, abbiamo bisogno per prima cosa di prendere la mappa, usando lo standard getElementById con una proprietà extra, contentDocument, che ci permette di prendere proprio lo SVG.

var map_svg = document.getElementById('state-map').contentDocument;

Una volta presa la mappa SVG, si comporta in maniera simile ad un oggetto DOM ordinario.

Dopo di che, chiamerete la funzione tableToJSON per ottenere i dati.

 
//tableToJSON will return a JSON object with our data
var map_json = tableToJSON('results-data');
//grab the actual array from the json object
var map_data = map_json['results-data'];
//get the array length so you don't calculate it»
for every loop iteration
var n_map_data = map_data.length;

Ora, dobbiamo solo fare un loop tra i risultati ed applicare i dati alla nostra mappa.

 
//loop through the map data and assign properties»
to each state on the map
for(var i=0; i < n_map_data; i++){
    //get the state on the map based on the ID in the data array
    //in this case, the ID is the state abbreviation
    var map_state = map_svg.getElementById(map_data[i].state);
 
    if(map_data[i].change === '-2 or more' ){
        map_state.style.fill = '#B26600';
    } else if (map_data[i].change === '-1'){
        map_state.style.fill = '#FF9200';
    } else if (map_data[i].change === '+1'){
        map_state.style.fill = '#19ABFF';
    } else if (map_data[i].change === '+2 or more'){
        map_state.style.fill = '#0071B2';
    }
 
    //assign the state a title, in this case the name of the state»
and the change in seats
    map_state.title = map_data[i].state + '»
will have a change of ' + map_data[i].change + ' seats';
 
    //give each state an event
    //for simplicity, the event is a simple alert that displays»
 		the title param we just assigned
    //yes, addEventListener even works in IE, thanks to svgweb
    map_state.addEventListener('mouseup', function(e){
        alert(e.target.title);
    }, false);
}

Quello che fa questo loop è di cercare prima qualunque nodo SVG con un ID che si abbini allo stato presente nel nostro array di dati: entrambe sono abbreviazioni come “AL” per l'Alabama. Quando corrispondono lo stato sulla mappa e lo stato nel nostro array di dati, lo stato viene colorato in base al cambiamento, da arancio scuro a blu scuro.

Infine, assegniamo a ciascun nodo un tag titolo. Questo ci permetterà di accedere a questa informazione quando assegneremo un handler per il click del mouse a ciascuno stato. Avrete notato che usiamo addEventListener, uno standard DOM non supportato da IE per gli eventi JavaScript. Fortunatamente, le menti brillanti dietro a svgweb hanno gestito questo problema al posto nostro e hanno astratto l'event handler per usare il modello standard. Questo è un evento molto semplice che fa apparire un alert JavaScript con il titolo che gli abbiamo appena assegnato.

Come se fosse il 1999#section3

SVG ha mantenuto molte promesse non realizzate per più di un decennio, con il supporto nativo che sta finalmente diventando mainstream (perfino IE9 includera il rendering nativo SVG quando verrà lanciato più avanti quest'anno). A differenza di canvas o di altri approcci script-only, SVG può essere facilmente diviso tra elementi di grafica e di codice, con giusto un po' di codice per aggiungere l'interattività. Funziona persino su dispositivi come iPad e iPhone. Ed ora, grazie a svgweb e ad un uso oculato di Flash, funziona sulle piattaforme più vecchie su cui nessuno mai immaginerebbe il supporto per SVG.

E' ora di aggiungere SVG al nostro arsenale per costruire uno strabiliante web.

Illustrazioni: {carlok}

Sull’autore

Jim Ray

Jim Ray è editorial development lead presso msnbc.com, dove gestisce un team di giornalisti, grafici e sviluppatori che sono tutti più brillanti di quanto non sia lui. Da dieci anni ormai lavora al far confluire media e tecnologia. Occasionalmente scrive di cibo e fa lo spiritoso su Twitter.

Nessun commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Altro da ALA

Webwaste

In questo estratto da World Wide Waste, Gerry McGovern esamina l'impatto ambientale di siti web pieni zeppi di asset inutili. Digital is physical. Sembra economico e gratis ma non lo è: ci costa la Terra.
Industry