Friday, September 28, 2012

Flex: Simple Chart Animation

I was playing with the column charts, a bit of styling, animation, etc... And all of us out there love animations and beautiful stuff therefore here you go. XD

The main source file - SimpleChartAnimation.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" 
      xmlns:mx="library://ns.adobe.com/flex/mx"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <s:RadioButtonGroup change="animateChangeEvent(event)" 
       id="rdoGrpAnimate"/>
  <!-- 
   We need to specify the types of animations over here
   1) For parallel animations, meaning all the effects
      will be executed together.
   2) For sequence animations, the effects will be running
      one by one.
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Sequence id="sequenceEffect">
   <mx:SeriesSlide duration="500" direction="up"/>
   <s:Fade duration="500" alphaFrom="0.5" alphaTo="1"/>
  </s:Sequence>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.series.ColumnSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var firstRun:Boolean = false;
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fills", [0xCDFFCD, 0xCDCDFF]);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));

    //Animation the Column Charts
    animateChangeEvent(null);
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    s += "No. of People: " + e.item.people;
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }  
   
   protected function animateChangeEvent(event:Event):void
   {
    //We will remove all the series attach to the chart
    //first
    columnChart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    sequenceEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    if(rdoGrpAnimate.selectedValue == "parallel")
    {
     localSeries.alpha = 0;
     if(!firstRun)
     {
      localSeries.setStyle("creationCompleteEffect", 
       parallelEffect);
     }else{
      localSeries.setStyle("addedEffect", 
       parallelEffect);
     }
    }else{
     localSeries.alpha = 0.5;
     if(!firstRun)
     {
      localSeries.setStyle("creationCompleteEffect", 
       sequenceEffect);
     }else{
      localSeries.setStyle("addedEffect", 
       sequenceEffect);
     }
    }
    
    firstRun = true;
    
    // Back up the current series on the chart.
    var currentSeries:Array = columnChart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    columnChart.series = currentSeries;
   } 
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:ColumnChart id="columnChart"
         gutterTop="0"
         gutterLeft="50"
         showDataTips="true" 
         width="80%"
         height="80%"
         dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:ColumnChart>
   </s:HGroup>
  </s:BorderContainer>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:VGroup gap="0">
    <s:Spacer height="4"/>
    <s:Label text="Show Column Animation:"/>
   </s:VGroup>
   <s:RadioButton value="parallel"
         group="{rdoGrpAnimate}"
         selected="true"
         label="Parallel Animation"/>
   <s:RadioButton value="sequence"
         group="{rdoGrpAnimate}"
         label="Sequence Animation"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

No comments:

Post a Comment