sdk:flex 3.2
flex piechart钻取的实现,其实钻取的实现本质是:当单击(或双击)的时候,重新加载piechart的数据,并且加点动画效果,这样感觉就像实现了钻取。本文是双击向下钻取,单击向上钻取。
图一、未钻取时的效果
图二、向下钻取后的效果
一、 PieChart.xml中的实现,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:effects="jac.*"
layout="vertical" verticalAlign="top"
horizontalAlign="center" backgroundGradientColors="[0x000000,0x323232]" paddingTop="0" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.charts.events.ChartItemEvent;
import mx.controls.Alert;
import com.adobe.serialization.json.JSON;
import mx.charts.series.items.ColumnSeriesItem;
[Bindable]
private var medalsAC:ArrayCollection = new ArrayCollection( [
{ Country: "USA", Gold: 35, Silver:39, Bronze: 29 },
{ Country: "China", Gold: 32, Silver:17, Bronze: 14 },
{ Country: "Russia", Gold: 27, Silver:27, Bronze: 38 } ]);
private var downArray:ArrayCollection = new ArrayCollection([
{ Country: "California",Gold:23.3},
{ Country: "Japan",Gold:23.4},
{ Country: "South Korea",Gold:33.3},
{ Country: "England",Gold:20}]);
private function displayGold(data:Object, field:String, index:Number, percentValue:Number):String {
var temp:String= (" " + percentValue).substr(0,6);
return data.Country + ": " + '/n' + "Total Gold: " + data.Gold + '/n' + temp + "%";
}
private function pieItemClick(evt:ChartItemEvent):void{
// Alert.show(""+JSON.encode(evt.hitSet),"tips");
var arr:Array = [];
arr[evt.hitData.chartItem.index] = 0.2;
series.perWedgeExplodeRadius=arr;
// chart.perWedgeExplodeRadius=arr;
}
private function pieItemDBClick(evt:ChartItemEvent):void{//drilldown
// Alert.show("ds","tips");
chart.showDataTips = false;
series.setStyle("showDataEffect", drillDownEffect);
chart.dataProvider=downArray;
chart.removeEventListener(ChartItemEvent.ITEM_DOUBLE_CLICK,pieItemDBClick);
chart.addEventListener(MouseEvent.CLICK, drillUp);
}
private function drillUp(event:MouseEvent):void{
chart.showDataTips = false;
series.setStyle("showDataEffect",drillUpEffect);
chart.dataProvider=medalsAC;
chart.addEventListener(ChartItemEvent.ITEM_DOUBLE_CLICK,pieItemDBClick);
chart.removeEventListener(MouseEvent.CLICK,drillUp);
}
//piechart钻取(drilldown或者drillup,本质是数据的重新加载)
]]>
</mx:Script>
<!--动画效果-->
<effects:DrillDownEffect id="drillDownEffect" duration="1500" effectEnd="chart.showDataTips=true" />
<effects:DrillUpEffect id="drillUpEffect" duration="1500" effectEnd="chart.showDataTips=true"/>
<mx:Panel title="PieChart Control" layout="vertical" color="0xffffff" borderAlpha="0.15" height="350" width="600"
paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10" horizontalAlign="center">
<mx:PieChart id="chart" height="100%" width="100%" paddingRight="5" paddingLeft="5" color="0x323232"
showDataTips="true" dataProvider="{medalsAC}" doubleClickEnabled="true" itemDoubleClick="pieItemDBClick(event);">
<mx:series>
<mx:PieSeries id="series" labelPosition="callout" field="Gold" labelFunction="displayGold" nameField="Country">
<mx:calloutStroke>
<mx:Stroke weight="0" color="0x888888" alpha="1.0"/>
</mx:calloutStroke>
<mx:radialStroke>
<mx:Stroke weight="0" color="#FFFFFF" alpha="0.20"/>
</mx:radialStroke>
<mx:stroke>
<mx:Stroke color="0" alpha="0.20" weight="2"/>
</mx:stroke>
<mx:filters>
<mx:Array/>
</mx:filters>
</mx:PieSeries>
</mx:series>
</mx:PieChart>
<mx:Legend dataProvider="{chart}" color="0x323232" direction="horizontal" horizontalGap="60"/>
</mx:Panel>
</mx:Application>
二、向下钻取效果的实现- DrillDownEffect.as
package jac
{
import mx.charts.effects.SeriesEffect;
import mx.effects.IEffectInstance;
import mx.effects.TweenEffect;
public class DrillDownEffect extends TweenEffect
{
public function DrillDownEffect(target:Object = null)
{
super(target);
instanceClass = DrillDownEffectInstance;
}
public var drillFromIndex:Number = 0;
public var splitDirection:String = "vertical";
override protected function initInstance(inst:IEffectInstance):void
{
super.initInstance(inst);
DrillDownEffectInstance(inst).drillFromIndex = drillFromIndex;
DrillDownEffectInstance(inst).splitDirection = splitDirection;
}
}
}
import mx.charts.effects.effectClasses.SeriesEffectInstance;
import mx.charts.chartClasses.RenderData;
import flash.geom.Rectangle;
import mx.effects.effectClasses.TweenEffectInstance;
import mx.charts.chartClasses.Series;
class DrillDownEffectInstance extends TweenEffectInstance
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*/
public function DrillDownEffectInstance(target:Object)
{
super(target);
}
public var drillFromIndex:Number;
public var splitDirection:String;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var _startingBounds:Rectangle;
private var _state:String = "";
/**
* @private
*/
private var dstRenderData:RenderData;
private var srcRenderData:RenderData;
private var targetBounds:Array;
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function play():void
{
var targetSeries:Series = Series(target);
srcRenderData = RenderData(targetSeries.getRenderDataForTransition("hide"));
dstRenderData = RenderData(targetSeries.getRenderDataForTransition("show"));
targetSeries.getElementBounds(srcRenderData);
targetSeries.getElementBounds(dstRenderData);
if(drillFromIndex >= srcRenderData.elementBounds.length)
drillFromIndex = 0;
var dstCount:Number= dstRenderData.elementBounds.length;
var srcCount:Number = srcRenderData.elementBounds.length;
if(drillFromIndex < srcRenderData.elementBounds.length)
{
_startingBounds = srcRenderData.elementBounds[drillFromIndex].clone();
targetBounds = [];
for(var i:int = 0;i<dstCount;i++)
{
targetBounds[i] = dstRenderData.elementBounds[i].clone();
}
for(i= 0;i<srcCount;i++)
{
dstRenderData.elementBounds[i] = srcRenderData.elementBounds[i].clone();
}
}
else
{
_startingBounds = null;
}
targetSeries.transitionRenderData = srcRenderData;
targetSeries.invalidateDisplayList();
_state = "hiding";
// Create a tween to move the object
tween = createTween(this, [ 0 ],
[ 1 ], duration);
}
/**
* @private
*/
override public function onTweenUpdate(values:Object):void
{
var targetSeries:Series = Series(target);
super.onTweenUpdate(values);
var value:Number = values[0];
if(_startingBounds == null)
return;
var targetBounds:Array = targetBounds;
var n:int;
var i:int;
var interpolation:Number;
var v:Rectangle;
interpolation = Math.min(1,value/.1);
if(_state == "hiding")
{
n = srcRenderData.filteredCache.length;
for (i = 0; i < n; i++)
{
interpolation = value/.1;
if(i != drillFromIndex)
srcRenderData.filteredCache[i].itemRenderer.alpha = (1-interpolation);
}
if(value >= .1)
{
_state = "holding";
n = srcRenderData.filteredCache.length;
for (i = 0; i < n; i++)
{
srcRenderData.filteredCache[i].itemRenderer.alpha = 1;
}
targetSeries.transitionRenderData = dstRenderData;
targetSeries.validateNow();
layoutDestination(0);
}
}
if (_state == "holding")
{
if(value >= .5)
{
_state = "splitting";
}
}
if(_state == "splitting")
{
interpolation = Math.max(value-.5,0)/.5;
layoutDestination(interpolation);
}
targetSeries.invalidateDisplayList();
}
private function layoutDestination(interpolation:Number):void
{
var n:Number = dstRenderData.filteredCache.length;
var startWidth:Number;
var startHeight:Number;
var activeBounds:Array = dstRenderData.elementBounds;
var a:Rectangle;
var vInterpolation:Number;
var hInterpolation:Number;
var target:Rectangle;
var startLeft:Number;
var wDelta:Number;
var hDelta:Number;
var lDelta:Number;
var tDelta:Number;
var newWidth:Number;
var newHeight:Number;
var startTop:Number;
var i:int;
if(splitDirection == "vertical")
{
startWidth = _startingBounds.width / n;
startHeight = _startingBounds.height;
vInterpolation = Math.pow(interpolation,4);
hInterpolation = Math.pow(interpolation,2);
for (i = 0; i < n; i++)
{
startLeft = _startingBounds.left + startWidth * i;
target = targetBounds[i];
a = activeBounds[i];
wDelta = target.width - startWidth;
hDelta = target.height - startHeight;
lDelta = target.left - startLeft;
tDelta = target.top - _startingBounds.top;
newWidth = startWidth + wDelta * hInterpolation;
newHeight = startHeight + hDelta * vInterpolation;
a.left = startLeft + lDelta * hInterpolation;
a.right = a.left + newWidth;
a.top = _startingBounds.top + tDelta * vInterpolation;
a.bottom = a.top + newHeight;
}
}
else
{
startWidth = _startingBounds.width;
startHeight = _startingBounds.height / n;
startLeft = _startingBounds.left;
vInterpolation = Math.pow(interpolation,4);
hInterpolation = Math.pow(interpolation,2/3);
for (i= 0; i < n; i++)
{
startTop = _startingBounds.top + startHeight * i;
target = targetBounds[i];
a = activeBounds[i];
wDelta = target.width - startWidth;
hDelta = target.height - startHeight;
lDelta = target.left - startLeft;
tDelta = target.top - startTop;
newWidth = startWidth + wDelta * hInterpolation;
newHeight = startHeight + hDelta * vInterpolation;
a.left = startLeft + lDelta * hInterpolation;
a.right = a.left + newWidth;
a.top = startTop + tDelta * vInterpolation;
a.bottom = a.top + newHeight;
}
}
}
}
三、向上钻取的实现,类似于向下钻取- DrillUpEffect.as
package jac
{
import mx.charts.effects.SeriesEffect;
import mx.effects.IEffectInstance;
import mx.effects.TweenEffect;
public class DrillUpEffect extends TweenEffect
{
public function DrillUpEffect(target:Object = null)
{
super(target);
instanceClass = DrillUpEffectInstance;
}
public var drillToIndex:Number = 0;
override protected function initInstance(inst:IEffectInstance):void
{
super.initInstance(inst);
DrillUpEffectInstance(inst).drillToIndex = drillToIndex;
}
}
}
import mx.charts.effects.effectClasses.SeriesEffectInstance;
import mx.charts.chartClasses.RenderData;
import flash.geom.Rectangle;
import mx.effects.effectClasses.TweenEffectInstance;
import mx.charts.chartClasses.Series;
class DrillUpEffectInstance extends TweenEffectInstance
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*/
public function DrillUpEffectInstance(target:Object)
{
super(target);
}
public var drillToIndex:Number;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var _drillBounds:Rectangle;
private var _state:String = "";
/**
* @private
*/
private var dstRenderData:RenderData;
private var srcRenderData:RenderData;
private var targetBounds:Array;
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function play():void
{
var targetSeries:Series = Series(target);
srcRenderData = RenderData(targetSeries.getRenderDataForTransition("hide"));
dstRenderData = RenderData(targetSeries.getRenderDataForTransition("show"));
targetSeries.getElementBounds(srcRenderData);
targetSeries.getElementBounds(dstRenderData);
if(drillToIndex >= srcRenderData.elementBounds.length)
drillToIndex = 0;
var dstCount:Number= dstRenderData.elementBounds.length;
var srcCount:Number = srcRenderData.elementBounds.length;
if(drillToIndex < srcRenderData.elementBounds.length)
{
_drillBounds = dstRenderData.elementBounds[drillToIndex].clone();
targetBounds = [];
for(var i:int = 0;i<srcCount;i++)
{
targetBounds[i] = srcRenderData.elementBounds[i].clone();
}
}
else
{
_drillBounds = null;
}
targetSeries.transitionRenderData = srcRenderData;
targetSeries.invalidateDisplayList();
_state = "merging";
// Create a tween to move the object
tween = createTween(this, [ 0 ],
[ 1 ], duration);
}
/**
* @private
*/
override public function onTweenUpdate(values:Object):void
{
var targetSeries:Series = Series(target);
super.onTweenUpdate(values);
var value:Number = values[0];
if(_drillBounds == null)
return;
var targetBounds:Array = targetBounds;
var n:int;
var i:int;
var interpolation:Number;
var v:Rectangle;
if(_state == "merging")
{
n = srcRenderData.filteredCache.length;
interpolation = value/.5;
layoutDestination(interpolation);
if(value >= .5)
{
_state = "holding";
targetSeries.transitionRenderData = dstRenderData;
targetSeries.invalidateDisplayList();
targetSeries.validateNow();
n = dstRenderData.filteredCache.length;
for (i = 0; i < n; i++)
{
if(i != drillToIndex)
dstRenderData.filteredCache[i].itemRenderer.alpha = 0;
else
dstRenderData.filteredCache[i].itemRenderer.alpha = 1;
}
}
}
if (_state == "holding")
{
if(value >= .9)
{
_state = "showing";
}
}
if(_state == "showing")
{
interpolation = Math.max(value-.9,0)/.1;
n = dstRenderData.filteredCache.length;
for (i = 0; i < n; i++)
{
if(i == drillToIndex)
dstRenderData.filteredCache[i].itemRenderer.alpha = 1;
else
dstRenderData.filteredCache[i].itemRenderer.alpha = interpolation;
}
}
targetSeries.invalidateDisplayList();
}
private function layoutDestination(interpolation:Number):void
{
var n:Number = srcRenderData.filteredCache.length;
var endWidth:Number = _drillBounds.width / n;
var endHeight:Number = _drillBounds.height;
var activeBounds:Array = srcRenderData.elementBounds;
var a:Rectangle;
var verticalInterpolation:Number = Math.pow(interpolation,1/4);
for (var i:int = 0; i < n; i++)
{
var endLeft:Number = _drillBounds.left + endWidth * i;
var itemBounds:Rectangle = targetBounds[i];
a = activeBounds[i];
var wDelta:Number = endWidth - itemBounds.width;
var hDelta:Number = endHeight - itemBounds.height;
var lDelta:Number = endLeft - itemBounds.left;
var tDelta:Number = _drillBounds.top - itemBounds.top;
var newWidth:Number = itemBounds.width + wDelta * interpolation;
var newHeight:Number = itemBounds.height + hDelta * verticalInterpolation;
a.left = itemBounds.left + lDelta * interpolation;
a.right = a.left + newWidth;
a.top = itemBounds.top + tDelta * verticalInterpolation;
a.bottom = a.top + newHeight;
}
}
}