このウェブサイトのコードを採用しました、
http://blog.ninjacaptain.com/2010/03/flex-chart-zoom-window/
データヒントの表示に関してWebブログで言及されているバグは修正されているように見えることに注意してください(少なくとも4.5.1 SDK以降)。データチップをうまく見ることができます。
コードは、1つの問題を除いてうまく機能します。以下に完全なコードを追加したので、新しいFlexアプリケーションとしてコピーして貼り付けて実行できます。
問題は、ユーザーがドラッグせずに1回クリックすると、次のエラーが発生することです(アプリが最初に実行されたらすぐにクリックしてください)。
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.charts.chartClasses::CartesianDataCanvas/localToData()[E:\dev\4.5.1\frameworks\projects\charts\src\mx\charts\chartClasses\CartesianDataCanvas.as:580]
ユーザーがこのようにドラッグせずにクリックしたときにキャプチャし、それを処理する関数を呼び出す方法はありますか?または、このエラーを回避する方法はありますか?コメント/提案をありがとう。
コードは次のとおりです。
<?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"
initialize="init()" width="600" height="520">
<fx:Script>
<![CDATA[
[Bindable]
private var profits:Array;
private var dragStart:Point;
private var dragEnd:Point;
private var zooming:Boolean;
// initializes the data provider with random data
private function init():void{
profits = new Array({month: 0, profit: 15});
for(var i:int=1; i<40; i++)
profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
}
// sets the start point of the zoom window
private function startDraw(e:MouseEvent):void{
zooming = true;
dragStart = new Point(series1.mouseX, series1.mouseY);
}
// draws the zoom window as your mouse moves
private function showDraw(e:MouseEvent):void{
if(zooming){
dragEnd = new Point(series1.mouseX, series1.mouseY);
// finds the top-left and bottom-right ponits of the zoom window
var TL:Point = new Point(); // top-left point
var BR:Point = new Point(); // bottom-right point
if(dragStart.x < dragEnd.x){
TL.x = dragStart.x;
BR.x = dragEnd.x;
}
else{
TL.x = dragEnd.x;
BR.x = dragStart.x;
}
if(dragStart.y < dragEnd.y){
TL.y = dragStart.y;
BR.y = dragEnd.y;
}
else{
TL.y = dragEnd.y;
BR.y = dragStart.y;
}
// prevents the zoom window from going off the canvas
if(TL.x < 0) TL.x = 0;
if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
if(TL.y < 0) TL.y = 0;
if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;
// draw the actual zoom window
chartCanvas.graphics.clear();
chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
chartCanvas.graphics.endFill();
}
}
// clears the drawing canvas and sets the new max/mins
private function finishDraw(e:MouseEvent):void{
zooming = false;
chartCanvas.graphics.clear();
// converts the drag coordinates into axis data points
var chartValStart:Array = chartCanvas.localToData(dragStart);
var chartValEnd:Array = chartCanvas.localToData(dragEnd);
// sets the new maximum and minimum for both axes
haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
}
// resets the axis max/mins
private function resetZoom():void{
haxis.minimum = NaN;
haxis.maximum = NaN;
vaxis.minimum = NaN;
vaxis.maximum = NaN;
}
]]>
</fx:Script>
<s:VGroup>
<mx:Panel title="Line Chart">
<mx:LineChart id="chart1"
mouseDown="startDraw(event)"
mouseMove="showDraw(event)"
mouseUp="finishDraw(event)"
width="510">
<!-- zoom window is drawn here -->
<mx:annotationElements>
<mx:CartesianDataCanvas id="chartCanvas"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:LinearAxis id="haxis"/>
</mx:horizontalAxis>
<mx:verticalAxis>
<mx:LinearAxis id="vaxis"/>
</mx:verticalAxis>
<mx:series>
<mx:LineSeries filterData="false" id="series1" xField="month" yField="profit"
displayName="Profit" dataProvider="{profits}"/>
</mx:series>
</mx:LineChart>
</mx:Panel>
<mx:Button label="Reset Zoom" click="resetZoom()" />
</s:VGroup>
</s:Application>
アップデート:
他の人に役立つ場合に備えて、これが解決策です。以下の回答で説明するように、nullのdragStart値とdragEnd値をチェックするifステートメントを追加しました。また、フレックスがデフォルトでラインシリーズに配置するドロップシャドウを削除したため、ユーザーが選択したズーム領域が小さすぎる場合でも警告は表示されません。
<?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"
initialize="init()" width="600" height="520">
<fx:Script>
<![CDATA[
[Bindable]
private var profits:Array;
private var dragStart:Point;
private var dragEnd:Point;
private var zooming:Boolean;
// initializes the data provider with random data
private function init():void{
profits = new Array({month: 0, profit: 15});
for(var i:int=1; i<40; i++)
profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
}
// sets the start point of the zoom window
private function startDraw(e:MouseEvent):void{
zooming = true;
dragStart = new Point(series1.mouseX, series1.mouseY);
}
// draws the zoom window as your mouse moves
private function showDraw(e:MouseEvent):void{
if(zooming){
dragEnd = new Point(series1.mouseX, series1.mouseY);
// finds the top-left and bottom-right ponits of the zoom window
var TL:Point = new Point(); // top-left point
var BR:Point = new Point(); // bottom-right point
if(dragStart.x < dragEnd.x){
TL.x = dragStart.x;
BR.x = dragEnd.x;
}
else{
TL.x = dragEnd.x;
BR.x = dragStart.x;
}
if(dragStart.y < dragEnd.y){
TL.y = dragStart.y;
BR.y = dragEnd.y;
}
else{
TL.y = dragEnd.y;
BR.y = dragStart.y;
}
// prevents the zoom window from going off the canvas
if(TL.x < 0) TL.x = 0;
if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
if(TL.y < 0) TL.y = 0;
if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;
// draw the actual zoom window
chartCanvas.graphics.clear();
chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
chartCanvas.graphics.endFill();
}
}
// clears the drawing canvas and sets the new max/mins
private function finishDraw(e:MouseEvent):void{
zooming = false;
chartCanvas.graphics.clear();
if (dragStart && dragEnd) { // Solution to original posted quesion
// converts the drag coordinates into axis data points
var chartValStart:Array = chartCanvas.localToData(dragStart);
var chartValEnd:Array = chartCanvas.localToData(dragEnd);
// sets the new maximum and minimum for both axes
haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
}
// reset values for next time
dragStart=null;
dragEnd=null;
}
// resets the axis max/mins
private function resetZoom():void{
haxis.minimum = NaN;
haxis.maximum = NaN;
vaxis.minimum = NaN;
vaxis.maximum = NaN;
}
]]>
</fx:Script>
<s:VGroup>
<mx:Panel title="Line Chart">
<mx:LineChart id="chart1"
mouseDown="startDraw(event)"
mouseMove="showDraw(event)"
mouseUp="finishDraw(event)"
width="510">
<!-- zoom window is drawn here -->
<mx:annotationElements>
<mx:CartesianDataCanvas id="chartCanvas"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:LinearAxis id="haxis"/>
</mx:horizontalAxis>
<mx:verticalAxis>
<mx:LinearAxis id="vaxis"/>
</mx:verticalAxis>
<mx:series>
<mx:LineSeries filterData="false" id="series1" xField="month" yField="profit"
displayName="Profit" dataProvider="{profits}"/>
</mx:series>
<mx:seriesFilters>
<fx:Array/>
</mx:seriesFilters>
</mx:LineChart>
</mx:Panel>
<mx:Button label="Reset Zoom" click="resetZoom()" />
</s:VGroup>
</s:Application>