Buon giorno, riprendendo l'articolo pubblicato dal Dr. Carlo Daniele sul numero 184 di Io Programmo, ho approfondito un pò lo studio dell'oggetto canvas, per estendere le funzionalità della creazione dei grafici.
Il tip che propongo mira alla creazione di un grafico a torta animato (Pie Chart).
Il concetto alla base è abbastanza semplice, e sfrutta la funzione
arc() dell'oggetto canvas.
senza dilungarmi nelle spiegazioni (anche perchè il codice è abbastanza semplice), spendo due parole solo sula funzione arc() dell'oggetto canvas e sulle funzioni che andrò ad implementare per creare il grafico.
la funzione arc() crea un arco di circonferenza, utilizzando i seguenti parametri:
- centerX coordinata di centro X della circonferenza
- centerY coordinata di centro Y della circonferenza
- radius raggio della circonferenza
- initialAngle porzione di angolo iniziale dal quale tracciare l'arco
- finalAngle porzione di angolo finale dal quale tracciare l'arco
- anitClockWise valore booleano che indica se il disegno dell'arco deve seguire un senso antiorario
Bene, a questo punto inseriamo nella pagina html i due oggetti che utilizzeremo
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<div id="legendSpace" style="width:400"></div>
e partiamo subito con lo script
definiamo per prima cosa alcune variabili di utilizzo globale
<script type="text/javascript">
var dataPie = [10.00, 5.00, 40.73, 4.27, 40.00]; //dati percentuali del grafico
var dataPieColor = ["red", "yellow", "green", "blue", "gray"]; //colori dei settori del grafico
var dataPieLegend = ["Legenda1", "Legenda2", "Legenda3", "Legenda4", "Legenda5"]; //testo della legenda dei colori
var dataPieData = ["Dati aggiuntivi 1", "Dati aggiuntivi 2", "Dati aggiuntivi 3", "Dati aggiuntivi 4", "Dati aggiuntivi 5"]; // dati aggiuntivi
var canvas = document.getElementById("canvas"); // riferimento all'oggetto canvas
var ctx; //Canvas context
var lastangle = 0; // porzione di angolo finale dell'ultimo arco disegnato
var centerX = canvas.width / 2; //centro della circonferenza (X)
var centerY = canvas.height / 2; //centro della circonferenza (Y)
var radius = 75; // raggio
var speed = 10; // Velocità animazione (ms)
var currentloop = 0; // frame corrente dell'animazione
var totalloops = 100; // frame totali dell'animazione
var currentIndex = 0; //indice dei dati visualizzati e disegnati
var color = dataPieColor[currentIndex]; //colore dell'arco corrente da disegnare
var countPercentage = dataPie[currentIndex]; // dato percentuale corrente da calcolare
a questo punto implementiamo la prima funzione, quella che richiameremo per avviare l'animazione
function dataPieInit() {
if (dataPie && dataPieColor && dataPieData && dataPieLegend) {
if (canvas && canvas.getContext) {
ctx = canvas.getContext("2d");
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
plot();
createLegend();
}
}
la funzione dataPieInit() inizializza gli oggetti e richiama la funzione plot() che disegna il gafico e la funzione createLegend() che crea la tabella con i simboli di legenda
ecco la funzione plot()
function plot() {
if (dataPie) {
if (currentloop < totalloops) { // controllo che il passo corrente sia minore del totale dei frame
ctx.fillStyle = color; //imposto il colore
ctx.beginPath();
ctx.moveTo(centerX, centerY); //mi sposto al centro
ctx.arc(centerX, centerY, radius, lastangle, lastangle + ((Math.PI * 2 * 1 / totalloops) + 0.02), false); //traccio un arco
ctx.lineTo(centerX, centerY); //tiro le linee dagli estremi dell'arco al centro (creo in questo modo un settore circolare)
ctx.fill(); // riempio il settore circolare con il colore corrente
lastangle += Math.PI * 2 * 1 / totalloops; //aggiorno l'angolo in modo che il successivo arco parta dalla fine del precedente.
currentloop++; //incremento il passo
updateData(); // aggiorno i dati
setTimeout("plot()", 10); // ripeto la funzione plot() dopo 10ms.
}
else { // se sono arrivato alla fine dei frame, la circonferenza è stata tracciata, ora è il momento di disegnare i valori percentuali nei posti giusti.
var initAngle = 0; //imposto l'angolo iniziale
for (var i = 0; i < dataPie.length; i++) {
//calcolo l'angolo finale
var finalAngle = initAngle + Math.PI * 2 * (dataPie[i] / 100); // calcolo l'angolo finale in base al dato percentuale contenuto nel vettore
var middleAnglePos = finalAngle - ((finalAngle - initAngle) / 2); // posizione mediana dell'angolo
initAngle = finalAngle;
var textPercentage = parseFloat(dataPie[i]).toFixed(2).toString() + "%"; //valore percentuale in stringa
//calcolo la coordinata x del testo, a partire dal centro
var xpos = (Math.cos(middleAnglePos) * (radius)) + (Math.cos(middleAnglePos) * (4 * textPercentage.length)) + centerX ;
//e poi la y...
var ypos = (Math.sin(middleAnglePos) * (radius)) + (Math.sin(middleAnglePos) * (4 * textPercentage.length)) + centerY ;
ctx.fillStyle = "black"; //testo nero
ctx.fillText(textPercentage, xpos, ypos); //scrivo il testo
}
}
}
}
la funzione plot(), in pratica controlla se il frame corrente sia minore del numero di frame dell'animazione, quindi traccia un settore di circonferenza, a partire dall'angolo alla fine dell'ultimo settore di circonferenza creato con un'ampiezza calcolata in base all'incremento dei frame/il numero totale dei frame (in questo caso 1/100).
se il frame corrente arriva al totale, la funzione disegna sul grafico i valori percentuali dei dati.
definiamo la funzione updateData() richiamata all'interno di plot(), che controlla se aggiornare l'indice dei vettori contenenti i dati del grafico, in base alla percentuale correntemente disegnata.
function updateData() {
if (dataPie && countPercentage) {
if (currentloop >= Math.round(countPercentage)) // la funzione è calcolata con un arrotondamento.
{
currentIndex++;
if (currentIndex < dataPie.length) {
countPercentage += dataPie[currentIndex]; //aggiorno il contatore globale della percentuale, in modo che quando il frame corrente SUPERA questo valore, vuol dire che è il momento di aggiornare l'indice dei dati.
color = dataPieColor[currentIndex];
}
}
}
}
Infine definiamo la funzione createLegend, che crea la tabella con la legenda dei colori del grafico:
function createLegend() {
var columnsXrows = 3; // quante colonne per riga vogliamo
var currentColumn = 1;
var legendLayer = document.getElementById("legendSpace");
var table = document.createElement("table");
//attributi di stile, si possono manipolare per ottenere stili diversi
table.setAttribute("border", "0");
table.setAttribute("cellpadding", "2px");
table.setAttribute("cellspacing", "0");
table.setAttribute("style", "width:100%");
var row = document.createElement("tr");
legendLayer.appendChild(table);
for (var i = 0; i < dataPie.length; i++) {
if ((currentColumn % (columnsXrows + 1)) == 0) {
currentColumn = 0;
table.appendChild(row);
row = document.createElement("tr");
}
//creo 2 "td" , uno per il quadrato colorato e l'altro per la descrizione e i dati aggiuntivi (inseriti come tooltip)
var td = document.createElement("td");
//create colored square
td.setAttribute("style", "cursor:pointer;width:20px;height:20px;background-color:" + dataPieColor[i]);
td.innerHTML = " "
row.appendChild(td);
//Create text
td = document.createElement("td");
td.innerHTML = dataPieLegend[i];
td.setAttribute("style", "cursor:pointer;font-size:8pt;font-family:Arial");
td.setAttribute("title", dataPieData[i]);
row.appendChild(td);
currentColumn++;
}
table.appendChild(row);
}
questa funzione crea dinamicamente una tabella contenente delle coppie di colonne COLORE/LEGENDA, con un TOOLTIP che è contenuto nel vettore dei dati aggiuntivi dataPieData[]
A questo punto, chiamiamo la funzione dataPieInit() e vediamo il risultato.
dataPieInit();
</script>
</body>
A presto!!
