Newest Articles

MegaCombs
Flash Media Player
XML Driven Pie Chart
Base Defender
Hangman Game
8 Ball Pool


Popular Articles

True Fullscreen Flash Mode
Mp3 Player with XML Playlist
Catapult Game
Infinitely Zooming Image
Create Pong
Base Defender


Random Articles

Combs
Custom Right Click Menu
Glowing Orb
Space Shooter Game
onEnterFrame vs setInterval
Flash Media Player


Links

Shapes the Game
Reddit
Newgrounds
TWiT
Link to SwfSpot
Swf Spot

Contact me on Google+



rss feed

XML Driven Pie Chart

XML Driven Pie Chart
AddThis Social Bookmark Button
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.
  1. //These two classes are used for changing the alpha values of a slice of the pie when moused over
  2. import flash.geom.ColorTransform;
  3. import flash.geom.Transform;
  4. //Radius of the pie chart. Also effects the x position of the table
  5. pieradius = 100;
  6. //Amount to scale pie slices when moused over
  7. mouseoverscale = 110;
  8. //X position of the center of the pie chart
  9. xpos = 150;
  10. //Y position of the center of the pie chart
  11. ypos = Stage.height/2;
  12. //Initialize hte chart's title to blank
  13. var charttitle:String = "";
  14. //Create an array to store the pie data
  15. var piearray:Array = new Array();
  16.  
  17. //Grab pie chart data from an xml file
  18. //if the get parameter "xmlfile" is not set, change the xml file to load the data to a default name
  19. if (_root.xmlfile == undefined || _root.xmlfile == "") {
  20.   //default name for the xml feed
  21.   _root.xmlfile = "chartdata.xml";
  22. }
  23. //Create an XML object to hold the xml data
  24. var myXml:XML = new XML();
  25. //Ignore white space
  26. myXml.ignoreWhite = true;
  27. //Load the xml file
  28. myXml.load(_root.xmlfile);
  29. //run when the xml file has loaded
  30. myXml.onLoad = function() {
  31.   //parse the xml file and load in the chart data
  32.   loadChartData();
  33. };

Parse the data from the xml file. Store the title into a variable and the entries into an array.
  1. //this function loads the chart data from the xml file into an array
  2. function loadChartData() {
  3.   //loop through child nodes of <chartdata> within the xml file
  4.   for (chartIndex=0; chartIndex<myXml.childNodes[0].childNodes.length; chartIndex++) {
  5.     //if the node is an entry, add it to the pie slices
  6.     if(myXml.childNodes[0].childNodes[chartIndex].nodeName=="entry"){
  7.       //add the pie slice info into the array
  8.       piearray.push([myXml.childNodes[0].childNodes[chartIndex].childNodes[0].childNodes[0].nodeValue,
  9.           Number(myXml.childNodes[0].childNodes[chartIndex].childNodes[1].childNodes[0].nodeValue)]);
  10.     //else if the current node is title, set the title of the pie chart
  11.     }else if(myXml.childNodes[0].childNodes[chartIndex].nodeName=="title"){
  12.       //update the charttitle variable, which will be displayed above the table
  13.       charttitle=myXml.childNodes[0].childNodes[chartIndex].childNodes[0].nodeValue;
  14.     }
  15.   }
  16.   //draw the pie chart and data table
  17.   drawChart();
  18. }

This function creates a movieclip for each slice and draws it into the data table.
  1. //This function draws the pie chart and the data table
  2. function drawChart() {
  3.   //this is a sum of all of the slices combined, initialized to 0
  4.   totalcount = 0;
  5.   //loop through all of the data in the array, adding the count of each slice to the total
  6.   for (curpos=0; curpos<piearray.length; curpos++) {
  7.     //add to the total
  8.     totalcount += piearray[curpos][1];
  9.   }
  10.   //now we loop through all of the slices, and update the count as we progress.
  11.   //this is used to determine how much to rotate each slice
  12.   curcount = 0;
  13.   //keep track of the width of the widest text entry. Used to draw the right side of the data table
  14.   widesttext = 0;
  15.   //Create a movieclip to hold the data table graphic
  16.   this.createEmptyMovieClip('datatable', this.getNextHighestDepth());
  17.   //loop through all of the data, drawing each piece of the pie and adding it to the table
  18.   for (curpos=0; curpos<piearray.length; curpos++) {
  19.     //attach a movie clip to hold an individual slice of the pie
  20.     this.createEmptyMovieClip("piece"+curpos, this.getNextHighestDepth());
  21.     //calculate the percentage of the pie the piece takes up
  22.     eval("piece"+curpos).piecepercent = piearray[curpos][1]/totalcount;
  23.     //calculate the rotation in percentage
  24.     eval("piece"+curpos).startpercent = curcount/totalcount;
  25.     //set the x position according to xpos set at the top of this script
  26.     eval("piece"+curpos)._x = xpos;
  27.     //set the y position according to ypos set at the top of this script
  28.     eval("piece"+curpos)._y = ypos;
  29.     //the info for the slice has been set, so now we draw the piece into its movieclip
  30.     drawPiece(eval("piece"+curpos));
  31.     //the piece has been drawn, now add the text showing the percentage of the pie it takes up
  32.     labelangle = (eval("piece"+curpos).startpercent+eval("piece"+curpos).piecepercent/2);
  33.     //hold the piece number, used for coloring in the box in the data table
  34.     eval("piece"+curpos).piecenumber = curpos;
  35.     //create the textfield to hold the percentage
  36.     this.createTextField("piecetext"+curpos, this.getNextHighestDepth(), xpos+Math.cos(labelangle*Math.PI*2-(Math.PI/2))*(pieradius*mouseoverscale/100+12)-20,
  37.       ypos+Math.sin(labelangle*Math.PI*2-(Math.PI/2))*(pieradius*mouseoverscale/100+12)-10, 40, 20);
  38.     //set this text to not selectable
  39.     eval("piecetext"+curpos).selectable = false;
  40.     //set the actual text
  41.     eval("piecetext"+curpos).text = Math.round(eval("piece"+curpos).piecepercent*1000)/10+"%";
  42.     //create a new text formatting to center the text
  43.     var textfmt:TextFormat = new TextFormat();
  44.     //set the text formatting to centered
  45.     textfmt.align = 'center';
  46.     //apply the text format
  47.     eval("piecetext"+curpos).setTextFormat(textfmt);
  48.     //add to total amount that has already been drawn
  49.     curcount += piearray[curpos][1];
  50.     //Create a new movieclip in the data table to hold the piece's text info
  51.     datatable.createEmptyMovieClip('box'+curpos, datatable.getNextHighestDepth());
  52.     //Draw a box to the left of the piece's text. This line sets the style for the border
  53.     eval("datatable.box"+curpos).lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
  54.     //set the color and opacity of the box fill
  55.     eval("datatable.box"+curpos).beginFill(eval('piece'+curpos).randomcolor, 80);
  56.     //draw a line to 10,0 from 0,0
  57.     eval("datatable.box"+curpos).lineTo(10, 0);
  58.     //draw a line to 10,10 from 10,0
  59.     eval("datatable.box"+curpos).lineTo(10, 10);
  60.     //draw a line to 0,10 from 10,10
  61.     eval("datatable.box"+curpos).lineTo(0, 10);
  62.     //draw a line to 0,0 from 0,10
  63.     eval("datatable.box"+curpos).lineTo(0, 0);
  64.     //set the x position a bit left of the text
  65.     eval("datatable.box"+curpos)._x = -12;
  66.     //move the y position according to its position in the pie and the total number of pieces
  67.     eval("datatable.box"+curpos)._y = curpos*20-(piearray.length*20)/2+4;
  68.     //create a text field that holds the text for a slice in the data table. It shows the name and the count
  69.     datatable.createTextField("piecedata"+curpos, this.getNextHighestDepth(), 0, curpos*20-(piearray.length*20)/2, 200, 20);
  70.     //set the text with the name of the current slice and its count
  71.     eval("datatable.piecedata"+curpos).text = piearray[curpos][0]+' ('+piearray[curpos][1]+')';
  72.     //make this text field not selectable
  73.     eval("datatable.piecedata"+curpos).selectable = false;
  74.     //adjust the textfield's width according to the text length.
  75.     //This is used to determine the width of the pie data box
  76.     eval("datatable.piecedata"+curpos).autoSize = "left";
  77.     //if this is the longest text field, update the width. Used when the border box is drawn
  78.     if (widesttext<eval("datatable.piecedata"+curpos)._width) {
  79.       //update the width to this width
  80.       widesttext = eval("datatable.piecedata"+curpos)._width;
  81.     }
  82.   }
  83.   //All of the pieces and the data table has been drawn, now add a border around the data table
  84.   //set the linestyle of the data table's border
  85.   datatable.lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
  86.   //y position changes according to how many pieces are in the pie
  87.   datatable.moveTo(-20, -(piearray.length*20)/2-4);
  88.   //move a bit right of the widest text field
  89.   datatable.lineTo(widesttext+8, -(piearray.length*20)/2-4);
  90.   //move to the bottom right corner of the data table, with some spacing
  91.   datatable.lineTo(widesttext+8, (piearray.length*20)/2+4);
  92.   //move to the bottom left corner of the data table, with some spacing
  93.   datatable.lineTo(-20, (piearray.length*20)/2+4);
  94.   //move back to the top left corner of the data table
  95.   datatable.lineTo(-20, -(piearray.length*20)/2-4);
  96.   //create a text field above the data table to display the title of the pie chart
  97.   datatable.createTextField("charttitletext", datatable.getNextHighestDepth(), -20, -(piearray.length*20)/2-28, 300, 20);
  98.   //set the text according to the variable grabbed in the xml file.
  99.   //if none is set in the xml file, the title is blank
  100.   datatable.charttitletext.text = charttitle;
  101.   //make the title not selectable
  102.   datatable.charttitletext.selectable = false;
  103.   //move the data table to the right of the pie chart with some spacing
  104.   datatable._x=xpos+pieradius+70;
  105.   //move the datatable's y position according to ypos set at the top of this script
  106.   datatable._y=ypos;
  107. }

This function draws out an individual piece of the pie.
  1. //this function draws a piece of the pie to the screen.
  2. //it also adjusts its appearance when moused over/out and underlines a piece in the data table when moused over
  3. function drawPiece(piece) {
  4.   //The curveto function uses a Quadratic Bezier curve, so we can only draw one eighth of a circle at a time without distortion.
  5.   //Determine how much of the piece is left over after cutting out every full 1/8th of a circle from the piece
  6.   lastsection = (2*Math.PI*piece.piecepercent)%(Math.PI/4);
  7.   //generate a random color for the piece's fill
  8.   piece.randomcolor = (Math.floor(Math.random()*255)+Math.floor(Math.random()*255)*255+Math.floor(Math.random()*255)*255*255);
  9.   //give the piece a black border
  10.   piece.lineStyle(0, 0x000000, 100, false, "none", "round", "miter", 1);
  11.   //apply the fill color to the piece
  12.   piece.beginFill(piece.randomcolor, 80);
  13.   //determine the number of eigths of a circle that fit within the piece
  14.   extraeighths = Math.floor((2*Math.PI*piece.piecepercent)/(Math.PI/4));
  15.   //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
  16.   switch (extraeighths) {
  17.   //full pie. move to the center-right of the circle and draw a curve 1/8th of the way clockwise
  18.   case 8 :
  19.     piece.moveTo(pieradius, 0);
  20.     //draw a curved line 1/8th of the circle. The third and fourth parameters are the end x and y positions.
  21.     //the first two parameters tell the line how much to curve
  22.     piece.curveTo(pieradius, Math.tan(Math.PI/8)*pieradius, Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
  23.   //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
  24.   case 7 :
  25.     piece.lineTo(Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
  26.     piece.curveTo(Math.tan(Math.PI/8)*pieradius, pieradius, 0, pieradius);
  27.   //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
  28.   case 6 :
  29.     piece.lineTo(0, pieradius);
  30.     piece.curveTo(-Math.tan(Math.PI/8)*pieradius, pieradius, -Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
  31.   //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
  32.   case 5 :
  33.     piece.lineTo(-Math.sin(Math.PI/4)*pieradius, Math.sin(Math.PI/4)*pieradius);
  34.     piece.curveTo(-pieradius, Math.tan(Math.PI/8)*pieradius, -pieradius, 0);
  35.   //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
  36.   case 4 :
  37.     piece.lineTo(-pieradius, 0);
  38.     piece.curveTo(-pieradius, -Math.tan(Math.PI/8)*pieradius, -Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
  39.   //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
  40.   case 3 :
  41.     piece.lineTo(-Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
  42.     piece.curveTo(-Math.tan(Math.PI/8)*pieradius, -pieradius, 0, -pieradius);
  43.   //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
  44.   case 2 :
  45.     piece.lineTo(0, -pieradius);
  46.     piece.curveTo(Math.tan(Math.PI/8)*pieradius, -pieradius, Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
  47.   //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
  48.   case 1 :
  49.     piece.lineTo(Math.sin(Math.PI/4)*pieradius, -Math.sin(Math.PI/4)*pieradius);
  50.     piece.curveTo(pieradius, -Math.tan(Math.PI/8)*pieradius, pieradius, 0);
  51.   }
  52.   //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
  53.   piece.lineTo(pieradius, 0);
  54.   //curve from the three o'clock position to the remainder after all full 1/8th slices have been removed
  55.   piece.curveTo(pieradius, Math.tan(lastsection/2)*pieradius, Math.cos(lastsection)*pieradius, Math.sin(lastsection)*pieradius);
  56.   //draw a final line back to 0,0
  57.   piece.lineTo(0, 0);
  58.   //rotate the piece according to the percentage of the pie the slice takes up
  59.   piece._rotation = extraeighths*45+piece.startpercent*360-90;
  60.   //store a variable holding the current coloroffset
  61.   //this variable changes when the mouse moves over or off of the slice. 
  62.   piece.coloroffset = 0;
  63.   //do not show the hand cursor when moused over the slice
  64.   piece.useHandCursor = false;
  65.   //when the user mouses over the slice, underline the text in the data table that corresponds to the slice
  66.   //also, start growing the piece and incxreasing its alpha value
  67.   piece.onRollOver = function() {
  68.     //create a text formatting
  69.     var piecetextformat:TextFormat = new TextFormat();
  70.     //set the formatting to underline
  71.     piecetextformat.underline = true;
  72.     //apply the text formatting
  73.     eval("datatable.piecedata"+piece.piecenumber).setTextFormat(piecetextformat);
  74.     //create an interval to start growing the piece and increasing its alpha value
  75.     fadeininterval(this);
  76.   };
  77.   //when the user mouses out from the slice, remove the underline from the text in the data table that corresponds to the slice
  78.   //start shrinking the piece back to its original size and decrease the alpha
  79.   piece.onRollOut = function() {
  80.     //create a new text formatting
  81.     var piecetextformat:TextFormat = new TextFormat();
  82.     //set underline to false
  83.     piecetextformat.underline = false;
  84.     //apply the formatting to the piece
  85.     eval("datatable.piecedata"+piece.piecenumber).setTextFormat(piecetextformat);
  86.     //create an interval to restore the shape to its original shape and opacity
  87.     fadeoutinterval(this);
  88.   };
  89. }

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
  1. //This function sets up an interval triggered every 10 milliseconds that grows a moused over piece and increases its opacity
  2. function fadeininterval(piece) {
  3.   //if there is already an interval with the selected piece, clear it so we can set up a new one
  4.   if (piece.intervalid) {
  5.     //clear the current interval
  6.     clearInterval(piece.intervalid);
  7.   }
  8.   //create an interval with piece that calls the fadeinstep function every 10 milliseconds
  9.   piece.intervalid = setInterval(this, "fadeinstep", 10, piece);
  10. }
  11. //this function increases the scale and opacity of the selected piece
  12. function fadeinstep(piece):Void {
  13.   //add 10 to the coloroffset variable, with a max of 255. coloroffset is going to specify the alpha offset
  14.   piece.coloroffset = Math.min(255, piece.coloroffset+10);
  15.   //create a color transform object
  16.   var colorTrans:ColorTransform = new ColorTransform();
  17.   //set the alpha offset to the coloroffset set in the piece
  18.   colorTrans.alphaOffset = piece.coloroffset;
  19.   //associate the color transform with the piece
  20.   var trans:Transform = new Transform(piece);
  21.   //apply the color transform to the transform object
  22.   trans.colorTransform = colorTrans;
  23.   //increase the x and y scale by .5, with a max of mouseoverscale (110)
  24.   piece._xscale = Math.min(mouseoverscale, piece._xscale+.5);
  25.   piece._yscale = Math.min(mouseoverscale, piece._xscale+.5);
  26.   //if the piece is fully opaque, remove the interval
  27.   if (piece.coloroffset == 255) {
  28.     //piece is fully opaque, stop calling this function
  29.     clearInterval(piece.intervalid);
  30.   }
  31. }

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
  1. //this function creates an interval to fade a piece back to its original scale and opacity
  2. function fadeoutinterval(piece) {
  3.   //if there is already an interval established with this piece, remove it
  4.   if (piece.intervalid) {
  5.     //remove the interval associated with this piece
  6.     clearInterval(piece.intervalid);
  7.   }
  8.   //create an interval fired every 10 milliseconds that calls fadeoutstep, which returns a piece to its original scale and opacity
  9.   piece.intervalid = setInterval(this, "fadeoutstep", 10, piece);
  10. }
  11. //this function slightly returns a piece to its original size and opacity each time it is called
  12. function fadeoutstep(piece):Void {
  13.   //decrease the coloroffset variable by 10, with a minimum of 0
  14.   piece.coloroffset = Math.max(0, piece.coloroffset-10);
  15.   //create a new colortransform
  16.   var colorTrans:ColorTransform = new ColorTransform();
  17.   //set the alpha offset to the piece's coloroffset
  18.   colorTrans.alphaOffset = piece.coloroffset;
  19.   //associate the colortransform with the piece
  20.   var trans:Transform = new Transform(piece);
  21.   //apply the colortransform
  22.   trans.colorTransform = colorTrans;
  23.   //decrease the x and y scale by .5, with a minimum of 100
  24.   piece._xscale = Math.max(100, piece._xscale-.5);
  25.   piece._yscale = Math.max(100, piece._xscale-.5);
  26.   //if the piece's coloroffset is back to 0, remove this interval
  27.   if (piece.coloroffset == 0) {
  28.     //remove the interval associated with this piece
  29.     clearInterval(piece.intervalid);
  30.   }
  31. }

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