Alzi la mano chi non si è trovato nella situazione di dover creare dei grafici a torta (mezzi o interi) su una pagina web, magari mostrando in tempo reale le variazioni che i dati di origine subiscono.

La soluzione di solito adottata prevedeva nella maggior parte di casi l'utilizzo di soluzioni Flash – e dunque qualcosa che a noi amanti di HTML5 non piace troppo, vista l'esigenza di avere un plugin apposito e, tra le altre cose, l'impossibilità di mostrare la nostra creazione su device quali iPhone e iPad.

Partiamo dunque dal caso concreto di cui mi sono occupato e vediamo come arrivare a questo risultato finale:

e cioè una mezza torta che, nel mio caso, mi mostri in tempo reale il tempo occupato e il tempo disponibile di una risorsa in un determinato orizzonte temporale (la prossima settimana, il prossimo mese, etc.)

Per prima cosa creiamo il nostro canvas, così:

<canvas id="occupazione" width="250" height="100"></canvas>

dando le dimensioni che noi preferiamo.

Dopodichè chiamiamo la nostra funzione, così (i metodi per eseguire una funzione al caricamento di una pagina sono tanti, chi mastica un briciolo di jQuery lo sa, qui usiamo il metodo più veloce anche se meno "elegante"):

<script><br />        drawOccupazione(<?php echo (1 + $occupation) ?>);<br />    </script>

Come vedete passiamo (con PHP) la percentuale di occupazione (nella forma "0.75" per il 75% di occupazione, ad esempio): chiaramente questa è la modalità che serviva a me, la cosa importante è che possiamo passare ala funzione drawOccupazione un valore decimale compreso tra 0 e 1 e questa funzione ne farà una rappresentazione come in figura.

Passiamo alla funzione vera e propria:

function drawOccupazione(occupato){<br />        var canvas = document.getElementById('occupazione');<br />        if (canvas.getContext){<br />            var ctx = canvas.getContext('2d');<br />            ctx.beginPath();<br />            ctx.arc(125, 100, 100, Math.PI * 1, Math.PI * (1 + parseFloat(occupato)), false);<br />            ctx.lineTo(125, 100);<br />            ctx.lineTo(0, 100);<br />            ctx.fillStyle = "red";<br />            ctx.fill();<br />            ctx.closePath();<br />            ctx.beginPath();<br />            ctx.arc(125, 100, 100, Math.PI * 2, Math.PI * (1 + parseFloat(occupato)), true);<br />            ctx.lineTo(125, 100);<br />            ctx.lineTo(250, 100);<br />            ctx.closePath();<br />            ctx.fillStyle = "green";<br />            ctx.fill();<br />        }<br />    }

Per prima cosa, "prendiamo" il "contesto 2d" del canvas (che è in sostanza il "foglio", la "tela" letteralmente su cui disegnare). In sostanza, prendiamo l'elemento a cui applicare tutte le funzioni di disegno:

    var canvas = document.getElementById('occupazione');<br />        if (canvas.getContext){<br />            var ctx = canvas.getContext('2d');

A questo punto abbiamo dunque il nostro "foglio" nell'oggetto

ctx

pronto per essere usato.

Una volta fatto questo iniziamo il disegno vero è proprio. E' necessario ricordare che le funzioni di disegno di canvas (arc(), line(), rect(), …) non disegnano nulla, ma tracciano dei "percorsi" sul foglio: per avere poi il disegno vero è proprio è necessario chiamare funzioni quali stroke() o fill() per "tracciare" o "riempire" i percorsi che si sono creati. 

Avviamo dunque il nostro "percorso" con:

ctx.beginPath();

e poi disegnamo il nostro primo arco (quello per il tempo occupato) con

ctx.arc(125, 100, 100, Math.PI * 1, Math.PI * (1 + parseFloat(occupato)), false);

Ricordando che la funzione arc() accetta i seguenti parametri:

  • posizione sulle x del centro del cerchio
  • posizione sulle y del centro del cerchio
  • raggio del cerchio
  • inizio dell'arco (in radianti, con: 0 gradi = 0 * Pi greco (oppure 2 * Pi greco), 90 gradi = 1.5 * PI Greco, 180 gradi = 1 * PI Greco, 270 gradi = 0.5 * PI Greco)
  • fine dell'arco
  • senso antiorario (se true)

questa funzione, in sostanza, dice alla matita di tracciare un arco (cioè un pezzo di cerchio) che abbia come centro il punto 125,100 (quindi a metà della larghezza e al'inizio del nostro foglio – ricordate che noi abbiamo creato il canvas di dimensioni 250 pixel di larghezza e 100 di altezza), che abbia un raggio di 100 (quindi occupi tutto il nostro canvas) e che parta da 180 gradi (con il parametro "1") e arrivi esattamente al punto che rappresenta, in radianti, la percentuale di occupazione che noi abbiamo passato alla funzione.

Siamo arrivati nel nostro percorso alla fine dell'arco: dobbiamo completare il percorso con le linee che chiudono la "fetta di torta". Contando che la "matita" non si alza dal foglio fino a che non lo decidiamo noi, le seguenti due righe

ctx.lineTo(125, 100);<br />    ctx.lineTo(0, 100);

dicono: "dal punto in cui sei arrivato (cioè la fine dell'arco) traccia una linea che arrivi al centro dell'arco (125,100) e da lì arrivi poi all'angolo in basso a sinistra del canvas". Dopodichè possiamo dire

ctx.fillStyle = "red";<br />    ctx.fill();<br />    ctx.closePath();

e cioè: "usa come stile di riempimento il rosso, riempi il percorso che abbiamo fatto fino ad adesso e chiudilo (per poter poi iniziare un nuovo percorso)".

La "fetta rossa" l'abbiamo disegnata. Per disegnare la fetta verde non dobbiamo far altro che partire dall'altro angolo e andare in senso antiorario, cioè:

ctx.arc(125, 100, 100, Math.PI * 0, Math.PI * (1 + parseFloat(occupato)), true);

dove diciamo di creare un arco che abbia sempre il centro basso sul nostro canvas ma questa volta parta da zero gradi, arrivi dov'è arrivata prima (dove è finita la fetta rossa) ma questa volta viaggi in senso antiorario.

E questo è quanto! Il vantaggio d questa soluzione è che è possibile passare al volo dei valori alla funzione, che aggiornerà dunque il grafico in maniera "live". Chiaramente, poi, noi abbiamo presentato una versione graficamente molto scarna e semplice per rendere l'idea del tutto: è possibile aggiungere ombre ed effetti per rendere il tutto più accattivante e bello.

 

Ricapitoliamo qui per comodità tutto il codice:

function drawOccupazione(occupato){<br />        var canvas = document.getElementById('occupazione');<br />        if (canvas.getContext){<br />            var ctx = canvas.getContext('2d');<br />            ctx.beginPath();<br />            ctx.arc(125, 100, 100, Math.PI * 1, Math.PI * (1 + parseFloat(occupato)), false);<br />            ctx.lineTo(125, 100);<br />            ctx.lineTo(0, 100);<br />            ctx.fillStyle = "red";<br />            ctx.fill();<br />            ctx.closePath();<br />            ctx.beginPath();<br />            ctx.arc(125, 100, 100, Math.PI * 2, Math.PI * (1 + parseFloat(occupato)), true);<br />            ctx.lineTo(125, 100);<br />            ctx.lineTo(250, 100);<br />            ctx.closePath();<br />            ctx.fillStyle = "green";<br />            ctx.fill();<br />        }<br />    }

The following two tabs change content below.
Silvio Porcellana
Silvio Porcellana è il fondatore di mob.is.it, il tool che centinaia di agenzie e professionisti di tutto il mondo utilizzano per creare con semplicità siti mobili e applicazioni native per i loro clienti. Tiene anche un podcast dove racconta ogni venerdì le sue avventure imprenditoriali, senza veli o segreti: Opus Digitalis