XML Driven Pie Chart
Description: Data is grabbed from an XML file and displayed as a pie chart with a data table
Author: John Bezanis
Added: December 10th 2008
Version: Flash 8
This script grabs data from an XML file and displays it as a pie chart with a data table. This entire project is actionscript driven.
Before working on the flash file, we need to set up the xml file containing the data. The main node is chartdata and each entry is a child named entry. Each enty has a label and a count. Optionally, the chart can have a title, which is given using a title node instead of an entry node. This file is saved as chartdata.xml, but you can specify any url by adding ?xmlfile=yourdatafile.xml after both occurences of .swf in the html. XML file example:
<?xml version="1.0"?>
<chartdata>
<title>Favorite Video Games Amongst The Staff</title>
<entry><label>Halo 3</label><count>20</count></entry>
<entry><label>Final Fantasy VII</label><count>15</count></entry>
<entry><label>Call of Duty 4</label><count>10</count></entry>
<entry><label>World of Warcraft</label><count>8</count></entry>
<entry><label>Gran Turismo 3: A-Spec</label><count>4</count></entry>
<entry><label>Nintendogs</label><count>1</count></entry>
</chartdata>
This first bit of code imports two classes, sets a few variables, and receives the xml file.
- //These two classes are used for changing the alpha values of a slice of the pie when moused over
- import flash.geom.ColorTransform;
- import flash.geom.Transform;
- //Radius of the pie chart. Also effects the x position of the table
- pieradius = 100;
- //Amount to scale pie slices when moused over
- mouseoverscale = 110;
- //X position of the center of the pie chart
- xpos = 150;
- //Y position of the center of the pie chart
- ypos = Stage.height/2;
- //Initialize hte chart's title to blank
- var charttitle:String = "";
- //Create an array to store the pie data
- var piearray:Array = new Array();
- //Grab pie chart data from an xml file
- //if the get parameter "xmlfile" is not set, change the xml file to load the data to a default name
- if (_root.xmlfile == undefined || _root.xmlfile == "") {
- //default name for the xml feed
- _root.xmlfile = "chartdata.xml";
- }
- //Create an XML object to hold the xml data
- var myXml:XML = new XML();
- //Ignore white space
- myXml.ignoreWhite = true;
- //Load the xml file
- myXml.load(_root.xmlfile);
- //run when the xml file has loaded
- myXml.onLoad = function() {
- //parse the xml file and load in the chart data
- loadChartData();
- };
Parse the data from the xml file. Store the title into a variable and the entries into an array.
- //this function loads the chart data from the xml file into an array
- function loadChartData() {
- //loop through child nodes of <chartdata> within the xml file
- for (chartIndex=0; chartIndex<myXml.childNodes[0].childNodes.length; chartIndex++) {
- //if the node is an entry, add it to the pie slices
- if(myXml.childNodes[0].childNodes[chartIndex].nodeName=="entry"){
- //add the pie slice info into the array
- piearray.push([myXml.childNodes[0].childNodes[chartIndex].childNodes[0].childNodes[0].nodeValue,
- Number(myXml.childNodes[0].childNodes[chartIndex].childNodes[1].childNodes[0].nodeValue)]);
- //else if the current node is title, set the title of the pie chart
- }else if(myXml.childNodes[0].childNodes[chartIndex].nodeName=="title"){
- //update the charttitle variable, which will be displayed above the table
- charttitle=myXml.childNodes[0].childNodes[chartIndex].childNodes[0].nodeValue;
- }
- }
- //draw the pie chart and data table
- drawChart();
- }
This function creates a movieclip for each slice and draws it into the data table.
- //This function draws the pie chart and the data table
- function drawChart() {
- //this is a sum of all of the slices combined, initialized to 0
- totalcount = 0;
- //loop through all of the data in the array, adding the count of each slice to the total
- for (curpos=0; curpos<piearray.length; curpos++) {
- //add to the total
- totalcount += piearray[curpos][1];
- }
- //now we loop through all of the slices, and update the count as we progress.
- //this is used to determine how much to rotate each slice
- curcount = 0;
- //keep track of the width of the widest text entry. Used to draw the right side of the data table
- widesttext = 0;
- //Create a movieclip to hold the data table graphic
- this.createEmptyMovieClip('datatable', this.getNextHighestDepth());
- //loop through all of the data, drawing each piece of the pie and adding it to the table
- for (curpos=0; curpos<piearray.length; curpos++) {
- //attach a movie clip to hold an individual slice of the pie
- this.createEmptyMovieClip("piece"+curpos, this.getNextHighestDepth());
- //calculate the percentage of the pie the piece takes up
- eval("piece"+curpos).piecepercent = piearray[curpos][1]/totalcount;
- //calculate the rotation in percentage
- eval("piece"+curpos).startpercent = curcount/totalcount;
- //set the x position according to xpos set at the top of this script
- eval("piece"+curpos)._x = xpos;
- //set the y position according to ypos set at the top of this script
- eval("piece"+curpos)._y = ypos;
- //the info for the slice has been set, so now we draw the piece into its movieclip
- drawPiece(eval("piece"+curpos));
- //the piece has been drawn, now add the text showing the percentage of the pie it takes up
- labelangle = (eval("piece"+curpos).startpercent+eval("piece"+curpos).piecepercent/2);
- //hold the piece number, used for coloring in the box in the data table
- eval("piece"+curpos).piecenumber = curpos;
- //create the textfield to hold the percentage
- this.createTextField("piecetext"+curpos, this.getNextHighestDepth(), xpos+Math.cos(labelangle*Math.PI*2-(Math.PI/2))*(pieradius*mouseoverscale/100+12)-20,
- ypos+Math.sin(labelangle*Math.PI*2-(Math.PI/2))*(pieradius*mouseoverscale/100+12)-10, 40, 20);
- //set this text to not selectable
- eval("piecetext"+curpos).selectable = false;
- //set the actual text
- eval("piecetext"+curpos).text = Math.round(eval("piece"+curpos).piecepercent*1000)/10+"%";
- //create a new text formatting to center the text
- var textfmt:TextFormat = new TextFormat();
- //set the text formatting to centered
- textfmt.align = 'center';
- //apply the text format
- eval("piecetext"+curpos).setTextFormat(textfmt);
- //add to total amount that has already been drawn
- curcount += piearray[curpos][1];
- //Create a new movieclip in the data table to hold the piece's text info
- datatable.createEmptyMovieClip('box'+curpos, datatable.getNextHighestDepth());
- //Draw a box to the left of the piece's text. This line sets the style for the border
- eval("datatable.box"+curpos).lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
- //set the color and opacity of the box fill
- eval("datatable.box"+curpos).beginFill(eval('piece'+curpos).randomcolor, 80);
- //draw a line to 10,0 from 0,0
- eval("datatable.box"+curpos).lineTo(10, 0);
- //draw a line to 10,10 from 10,0
- eval("datatable.box"+curpos).lineTo(10, 10);
- //draw a line to 0,10 from 10,10
- eval("datatable.box"+curpos).lineTo(0, 10);
- //draw a line to 0,0 from 0,10
- eval("datatable.box"+curpos).lineTo(0, 0);
- //set the x position a bit left of the text
- eval("datatable.box"+curpos)._x = -12;
- //move the y position according to its position in the pie and the total number of pieces
- eval("datatable.box"+curpos)._y = curpos*20-(piearray.length*20)/2+4;
- //create a text field that holds the text for a slice in the data table. It shows the name and the count
- datatable.createTextField("piecedata"+curpos, this.getNextHighestDepth(), 0, curpos*20-(piearray.length*20)/2, 200, 20);
- //set the text with the name of the current slice and its count
- eval("datatable.piecedata"+curpos).text = piearray[curpos][0]+' ('+piearray[curpos][1]+')';
- //make this text field not selectable
- eval("datatable.piecedata"+curpos).selectable = false;
- //adjust the textfield's width according to the text length.
- //This is used to determine the width of the pie data box
- eval("datatable.piecedata"+curpos).autoSize = "left";
- //if this is the longest text field, update the width. Used when the border box is drawn
- if (widesttext<eval("datatable.piecedata"+curpos)._width) {
- //update the width to this width
- widesttext = eval("datatable.piecedata"+curpos)._width;
- }
- }
- //All of the pieces and the data table has been drawn, now add a border around the data table
- //set the linestyle of the data table's border
- datatable.lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
- //y position changes according to how many pieces are in the pie
- datatable.moveTo(-20, -(piearray.length*20)/2-4);
- //move a bit right of the widest text field
- datatable.lineTo(widesttext+8, -(piearray.length*20)/2-4);
- //move to the bottom right corner of the data table, with some spacing
- datatable.lineTo(widesttext+8, (piearray.length*20)/2+4);
- //move to the bottom left corner of the data table, with some spacing
- datatable.lineTo(-20, (piearray.length*20)/2+4);
- //move back to the top left corner of the data table
- datatable.lineTo(-20, -(piearray.length*20)/2-4);
- //create a text field above the data table to display the title of the pie chart
- datatable.createTextField("charttitletext", datatable.getNextHighestDepth(), -20, -(piearray.length*20)/2-28, 300, 20);
- //set the text according to the variable grabbed in the xml file.
- //if none is set in the xml file, the title is blank
- datatable.charttitletext.text = charttitle;
- //make the title not selectable
- datatable.charttitletext.selectable = false;
- //move the data table to the right of the pie chart with some spacing
- datatable._x=xpos+pieradius+70;
- //move the datatable's y position according to ypos set at the top of this script
- datatable._y=ypos;
- }
This function draws out an individual piece of the pie.
- //this function draws a piece of the pie to the screen.
- //it also adjusts its appearance when moused over/out and underlines a piece in the data table when moused over
- function drawPiece(piece) {
- //The curveto function uses a Quadratic Bezier curve, so we can only draw one eighth of a circle at a time without distortion.
- //Determine how much of the piece is left over after cutting out every full 1/8th of a circle from the piece
- lastsection = (2*Math.PI*piece.piecepercent)%(Math.PI/4);
- //generate a random color for the piece's fill
- piece.randomcolor = (Math.floor(Math.random()*255)+Math.floor(Math.random()*255)*255+Math.floor(Math.random()*255)*255*255);
- //give the piece a black border
- piece.lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
- //apply the fill color to the piece
- piece.beginFill(piece.randomcolor, 80);
- //determine the number of eigths of a circle that fit within the piece
- extraeighths = Math.floor((2*Math.PI*piece.piecepercent)/(Math.PI/4));
- //if the piece is larger than 1/8th of the pie, draw out each of the 1/8ths until less than an eighth is left
- switch (extraeighths) {
- //full pie. move to the center-right of the circle and draw a curve 1/8th of the way clockwise
- case 8 :
- piece.moveTo(pieradius, 0);
- //draw a curved line 1/8th of the circle. The third and fourth parameters are the end x and y positions.
- //the first two parameters tell the line how much to curve
- piece.curveTo(pieradius, Math.tan(Math.PI/8)*pieradius, Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
- //piece is at least 7/8ths of the pie. move 45 degrees clockwise from three o'clock and curve to the six o clock position
- case 7 :
- piece.lineTo(Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
- piece.curveTo(Math.tan(Math.PI/8)*pieradius, pieradius, 0, pieradius);
- //piece is at least 6/8ths of the pie. move to the six o'clock position and curve to 45 degrees past the six o clock position
- case 6 :
- piece.lineTo(0, pieradius);
- piece.curveTo(-Math.tan(Math.PI/8)*pieradius, pieradius, -Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
- //piece is at least 5/8ths of the pie. move 45 degrees clockwise from six o'clock and curve to the nine o clock position
- case 5 :
- piece.lineTo(-Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
- piece.curveTo(-pieradius, Math.tan(Math.PI/8)*pieradius, -pieradius, 0);
- //piece is at least 4/8ths of the pie. move to 9 o'clock position and curve to 45 degrees past the nine o'clock position
- case 4 :
- piece.lineTo(-pieradius, 0);
- piece.curveTo(-pieradius, -Math.tan(Math.PI/8)*pieradius, -Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
- //piece is at least 3/8ths of the pie. move 45 degrees clockwise past nine o'clock and curve to the 12 o clock position
- case 3 :
- piece.lineTo(-Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
- piece.curveTo(-Math.tan(Math.PI/8)*pieradius, -pieradius, 0, -pieradius);
- //piece is at least 2/8ths of the pie. move to 12 o'clock position and curve to 45 degrees past the 12 o'clock position
- case 2 :
- piece.lineTo(0, -pieradius);
- piece.curveTo(Math.tan(Math.PI/8)*pieradius, -pieradius, Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
- //piece is at least 1/8th of the pie. move 45 degrees clockwise past 12 o'clock and curve to the 3 o'clock position
- case 1 :
- piece.lineTo(Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
- piece.curveTo(pieradius, -Math.tan(Math.PI/8)*pieradius, pieradius, 0);
- }
- //draw a line to the three o'clock position. If the slice is under 1/8th of the pie, a line is drawn from 0,0. Otherwise, there is 0 length to the line
- piece.lineTo(pieradius, 0);
- //curve from the three o'clock position to the remainder after all full 1/8th slices have been removed
- piece.curveTo(pieradius, Math.tan(lastsection/2)*pieradius, Math.cos(lastsection)*pieradius, Math.sin(lastsection)*pieradius);
- //draw a final line back to 0,0
- piece.lineTo(0, 0);
- //rotate the piece according to the percentage of the pie the slice takes up
- piece._rotation = extraeighths*45+piece.startpercent*360-90;
- //store a variable holding the current coloroffset
- //this variable changes when the mouse moves over or off of the slice.
- piece.coloroffset = 0;
- //do not show the hand cursor when moused over the slice
- piece.useHandCursor = false;
- //when the user mouses over the slice, underline the text in the data table that corresponds to the slice
- //also, start growing the piece and incxreasing its alpha value
- piece.onRollOver = function() {
- //create a text formatting
- var piecetextformat:TextFormat = new TextFormat();
- //set the formatting to underline
- piecetextformat.underline = true;
- //apply the text formatting
- eval("datatable.piecedata"+piece.piecenumber).setTextFormat(piecetextformat);
- //create an interval to start growing the piece and increasing its alpha value
- fadeininterval(this);
- };
- //when the user mouses out from the slice, remove the underline from the text in the data table that corresponds to the slice
- //start shrinking the piece back to its original size and decrease the alpha
- piece.onRollOut = function() {
- //create a new text formatting
- var piecetextformat:TextFormat = new TextFormat();
- //set underline to false
- piecetextformat.underline = false;
- //apply the formatting to the piece
- eval("datatable.piecedata"+piece.piecenumber).setTextFormat(piecetextformat);
- //create an interval to restore the shape to its original shape and opacity
- fadeoutinterval(this);
- };
- }
The next two functions are triggered when the user moves over a piece. A function is called every 10 milliseconds until the piece grows to a certain scale and hits 100% opacity
- //This function sets up an interval triggered every 10 milliseconds that grows a moused over piece and increases its opacity
- function fadeininterval(piece) {
- //if there is already an interval with the selected piece, clear it so we can set up a new one
- if (piece.intervalid) {
- //clear the current interval
- clearInterval(piece.intervalid);
- }
- //create an interval with piece that calls the fadeinstep function every 10 milliseconds
- piece.intervalid = setInterval(this, "fadeinstep", 10, piece);
- }
- //this function increases the scale and opacity of the selected piece
- function fadeinstep(piece):Void {
- //add 10 to the coloroffset variable, with a max of 255. coloroffset is going to specify the alpha offset
- piece.coloroffset = Math.min(255, piece.coloroffset+10);
- //create a color transform object
- var colorTrans:ColorTransform = new ColorTransform();
- //set the alpha offset to the coloroffset set in the piece
- colorTrans.alphaOffset = piece.coloroffset;
- //associate the color transform with the piece
- var trans:Transform = new Transform(piece);
- //apply the color transform to the transform object
- trans.colorTransform = colorTrans;
- //increase the x and y scale by .5, with a max of mouseoverscale (110)
- piece._xscale = Math.min(mouseoverscale, piece._xscale+.5);
- piece._yscale = Math.min(mouseoverscale, piece._xscale+.5);
- //if the piece is fully opaque, remove the interval
- if (piece.coloroffset == 255) {
- //piece is fully opaque, stop calling this function
- clearInterval(piece.intervalid);
- }
- }
The next two functions are triggered when the user moves off of a piece. A function is called every 10 milliseconds until the piece returns to its original size and opacity
- //this function creates an interval to fade a piece back to its original scale and opacity
- function fadeoutinterval(piece) {
- //if there is already an interval established with this piece, remove it
- if (piece.intervalid) {
- //remove the interval associated with this piece
- clearInterval(piece.intervalid);
- }
- //create an interval fired every 10 milliseconds that calls fadeoutstep, which returns a piece to its original scale and opacity
- piece.intervalid = setInterval(this, "fadeoutstep", 10, piece);
- }
- //this function slightly returns a piece to its original size and opacity each time it is called
- function fadeoutstep(piece):Void {
- //decrease the coloroffset variable by 10, with a minimum of 0
- piece.coloroffset = Math.max(0, piece.coloroffset-10);
- //create a new colortransform
- var colorTrans:ColorTransform = new ColorTransform();
- //set the alpha offset to the piece's coloroffset
- colorTrans.alphaOffset = piece.coloroffset;
- //associate the colortransform with the piece
- var trans:Transform = new Transform(piece);
- //apply the colortransform
- trans.colorTransform = colorTrans;
- //decrease the x and y scale by .5, with a minimum of 100
- piece._xscale = Math.max(100, piece._xscale-.5);
- piece._yscale = Math.max(100, piece._xscale-.5);
- //if the piece's coloroffset is back to 0, remove this interval
- if (piece.coloroffset == 0) {
- //remove the interval associated with this piece
- clearInterval(piece.intervalid);
- }
- }
Example html for this project:
<object data="/swfspot/resources/51-piechart.swf?xmlfile=http://www.bezzmedia.com/swfspot/resources/chartdata.xml"
type="application/x-shockwave-flash" width="600" height="300" >
<param name="movie" value="/swfspot/resources/51-piechart.swf?xmlfile=http://www.bezzmedia.com/swfspot/resources/chartdata.xml" />
</object>The source file is available below for download:
Download Source File
Download Demo SWF
Comments Currently Disabled


