AIR Window Scale and Stage Scale - actionscript-3

I have an AIR application that I would like to allow the user to scale from 60% to 200%. I have added a slider in an options menu that controls the scaling of the application. I'm confused on how to go about scaling the entire application properly and need a little help, I've been at this for quite some time now.
The code for this simple test is below. In adding traces everywhere and putting listeners on resize events, I have basically discovered that setting the scaleX and scaleY value of the window does not change the scaleX and scaleY of the stage. But, these stage values cannot be edited and an error is thrown if you try.
So, then I tried adjusting the stage.stageWidth and stage.stageHeight to match stage.width and stage.height. But, in doing that, the contents of the window are scaled again.
Basically what ends up happening is with anything over 100%, or the minWidth & minHeight, components inside the window end up clipping with parts appearing cut off because the stage.stageWidth is not as wide as the stage.width and stage.stageHeight is not as tall as the stage.height.
<?xml version="1.0" encoding="utf-8"?>
<s:Window xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="250" height="300"
minWidth="260" minHeight="280"
showStatusBar="false"
systemChrome="none" transparent="true"
initialize="_init()" creationComplete="_initPostCreation()"
maximizable="false" minimizable="false" xmlns:Components="assets.skins.CompOverlay.Components.*">
<fx:Declarations>
</fx:Declarations>
<s:VGroup left="2" top="2" right="2" bottom="2" gap="0">
<s:Label width="100%" textAlign="center" text="Window Scale" fontSize="14" />
<s:HGroup width="100%">
<s:HSlider id="winScale" minimum="0.6" maximum="2" width="80%" snapInterval="0.01" showDataTip="false" change="_winScale(event)" />
<s:Label width="20%" fontSize="14" textAlign="center" text="{Math.round(winScale.value*100) + '%'}"/>
</s:HGroup>
</s:VGroup>
<fx:Script>
<![CDATA[
import mx.events.ResizeEvent;
private function _init():void
{
}
private function _initPostCreation():void
{
this.addEventListener(Event.CLOSING, this._destructor);
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.addEventListener(Event.RESIZE, _onResize);
this.addEventListener(ResizeEvent.RESIZE, _onWinResize);
this.stage.align = StageAlign.TOP_LEFT;
}
private function _destructor(event:Event):void
{
this.removeEventListener(Event.CLOSING, this._destructor);
this.stage.removeEventListener(Event.RESIZE, _onResize);
this.removeEventListener(ResizeEvent.RESIZE, _onWinResize);
}
private function _winScale(event:Event):void
{
this.scaleX = event.target.value;
this.scaleY = this.scaleX;
this.width = (260*this.scaleX);
this.height = (300*this.scaleY);
trace(this.width, this.height);
trace(this.scaleX, this.scaleY);
}
private function _onResize(event:Event):void
{
trace('start resize----');
trace('stage.width = '+this.stage.width, 'stage.height = '+this.stage.height);
trace('stage.stageWidth = '+this.stage.stageWidth, 'stage.stageHeight = '+this.stage.stageHeight);
if (this.initialized) {
if (this.stage.width != this.stage.stageWidth) {
this.stage.removeEventListener(Event.RESIZE, _onResize);
this.stage.stageWidth = this.stage.width;
this.stage.stageHeight = this.stage.height;
this.stage.addEventListener(Event.RESIZE, _onResize);
}
trace('stage.width = '+this.stage.width, 'stage.height = '+this.stage.height);
trace('stage.stageWidth = '+this.stage.stageWidth, 'stage.stageHeight = '+this.stage.stageHeight);
trace('stage.scaleX = '+this.stage.scaleX, 'stage.scaleY = '+this.stage.scaleY);
}
trace('end resize----');
}
private function _onWinResize(event:ResizeEvent):void
{
trace ('window resize event');
if (this.stage.width != this.stage.stageWidth) {
event.stopPropagation();
trace('prop stopped');
}
}
]]>
</fx:Script>
</s:Window>
Resizing the window a few times results in this trace output:
start resize----
stage.width = 291.5 stage.height = 316.9
stage.stageWidth = 275 stage.stageHeight = 297
window resize event
prop stopped
window resize event
prop stopped
stage.width = 308.45 stage.height = 337.05
stage.stageWidth = 291 stage.stageHeight = 316
stage.scaleX = 1 stage.scaleY = 1 stage.scaleMode = noScale
end resize----
window resize event
prop stopped
start resize----
stage.width = 308.45 stage.height = 339.2
stage.stageWidth = 291 stage.stageHeight = 318
window resize event
prop stopped
window resize event
prop stopped
stage.width = 326.5 stage.height = 361.45
stage.stageWidth = 308 stage.stageHeight = 339
stage.scaleX = 1 stage.scaleY = 1 stage.scaleMode = noScale
end resize----
I appreciate any help, thanks!

Related

Increase height of reflected object

Want to reflect an image, and my code looks like
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
height="500" width="500"
creationComplete="init(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.filters.BlurFilter;
import mx.core.UIComponent;
import mx.events.FlexEvent;
private var loader:Loader = new Loader();
private var picture:BitmapData;
private var reflection:BitmapData;
private var reflectionHolder:Bitmap;
protected function init(event:FlexEvent):void
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("assets/rectangle.png"));
}
private function onComplete (event:Event):void
{
trace("loaded!");
var loaderInfo:LoaderInfo = LoaderInfo(event.target);
picture = new BitmapData(loaderInfo.width, loaderInfo.height, false, 0xFFFFFF);
picture.draw(loaderInfo.loader);
img.source=picture;
trace(picture.height,picture.width);
}
private function createReflection():void
{
reflection=new BitmapData(picture.width,picture.height,true,0x00ffffff);
var flipMatrix:Matrix = new Matrix();
flipMatrix.rotate(Math.PI);
flipMatrix.scale( -1, 1 );
flipMatrix.translate(0, picture.height);
reflection.draw(picture, flipMatrix);
for (var i:int = 0; i < picture.height; i++)
{
var rowFactor:Number = Math.max(0, 1 - (i / picture.height));
for (var j:int = 0; j < picture.width; j++)
{
var pixelColor:uint = reflection.getPixel32(j,i);
var pixelAlpha:uint = pixelColor>>24&0xff;
var pixelRGB:uint = pixelColor&0xffffff;
var resultAlpha:uint = pixelAlpha*rowFactor;
reflection.setPixel32(j, i, resultAlpha << 24 | pixelRGB);
}
}
reflection.applyFilter(reflection, reflection.rect, new Point(0, 0), new BlurFilter(4, 4, 3));
reflectionHolder=new Bitmap(reflection);
reflectionHolder.y=img.y+img.height;
reflectionHolder.x=img.x;
reflectionHolder.width = img.width;
reflectionHolder.height = img.height;
var uIComponent:UIComponent = new UIComponent();
uIComponent.addChild(reflectionHolder);
this.addElement(uIComponent);
}
protected function btn_clickHandler(event:MouseEvent):void
{
createReflection();
}
]]>
</fx:Script>
<s:Group height="100%" width="100%">
<s:BorderContainer backgroundColor="0x0d0d0d" height="100%" width="100%"/>
<s:Image id="img" height="100" width="200" />
<s:Button id="btn" click="btn_clickHandler(event)" />
</s:Group>
</s:WindowedApplication>
it shows result but the out put was not perfect...
Objective:- Reflected object should be same as the real Object and Have Some Distance From Real Object.
It will be very much code to show in one answer. Actually, I searched my very old archives, and I found my old class for reflection (2009 year). It will create reflection for you, It supports animated objects, and also distance from object to start of reflection. With only 2 lines of code:
var test:Sprite = new Sprite();
test.graphics.beginFill(0x990099);
test.graphics.drawRect(0, 0, 100, 50);
addChild(test);
test.x = test.y = 50;
//Reflection
//test - object that will be reflected
//40 - alpha, 40%
//200 - ratio, max value 0xFF or 255, with 0xFF you will see full object in reflection
//-1 - update frame rate, for animated objects
//0 - some dropoff... actually I don't remember it, don't use it by passing zero, It affects reflection bounds
//10 - distance to the reflection in pixels...
var vars:ReflectVars = new ReflectVars(test, 40, 200, -1, 0, 10);
var reflect:Reflect = new Reflect(vars);
And a result:
if you are interested in the class, I could move it to GitHub.
flipMatrix.translate(0, picture.height);
try changing this to
flipMatrix.translate(0, picture.height + 20);
OR
reflectionHolder.y=img.y+img.height;
try changing this to
reflectionHolder.y=img.y+img.height + 20;
for adding space between reflection & real picture

flex 4.6 3D rotation axis

I am trying to perform some 3D rotation on pictures, but it harder than it look.
The fact is that when you rotate a picture on the axis X and/or Y, it look stretched (but it's normal). But when you make a rotation from the axis Z, the picture rotate around the Z axis of the screen instead of it own axis. So it look like a stretched picture which turn around an axis instead of a plate picture, viewed from a side and which turn around it center.
Since I know it's a little confusing, here is the function I am using. You can try it with a circular picture for a better "effect". Just imagine you want a rotating circle, no mater the orientation. You will see that the picture rotate around the axis of the screen insteat of it's own axis.
public function rotate(target:IVisualElement, x:Number = 0, y:Number = 0, z:Number = 0, duration:Number = 1000):void
{
var rotation:Rotate3D = new Rotate3D();
rotation.autoCenterTransform = true;
rotation.angleXTo = x;
rotation.angleYTo = y;
rotation.angleZTo = z;
rotation.duration = duration;
rotation.target = target;
rotation.play();
}
Is there a easy simple way to perform theses rotations without having to redevelop the wheel ?
Finally I managed to have something which work perfectly and without external library. The trick was so simple that I don't understand why I don't tried it before.
Instead of applying X, Y and Z transformations on the same element, I apply the X and Y rotations on a container and the Z axis on the picture inside the container.
Like this, the Z rotation doesn't disturb the other axis anymore.
Here is the working test code:
<fx:Script>
<![CDATA[
private function init():void
{
moveZ();
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, repeat);
timer.start();
}
private function repeat(e:TimerEvent):void
{
moveZ();
}
private function moveX():void
{
rotX += 20;
fx.angleXTo = rotX;
fx.angleYTo = rotY;
fx.angleZTo = 0;
fx.duration = 1000;
fx.play(new Array(container));
}
private function moveY():void
{
rotY += 20;
fx.angleXTo = rotX;
fx.angleYTo = rotY;
fx.angleZTo = 0;
fx.duration = 1000;
fx.play(new Array(container));
}
private function moveZ():void
{
rotZ += 360;
fx.angleXTo = 0;
fx.angleYTo = 0;
fx.angleZTo = rotZ;
fx.duration = 5000;
fx.play(new Array(image));
}
]]>
</fx:Script>
<fx:Declarations>
<fx:Number id="rotX">0</fx:Number>
<fx:Number id="rotY">0</fx:Number>
<fx:Number id="rotZ">0</fx:Number>
<s:Rotate3D id="fx" autoCenterProjection="true" autoCenterTransform="true"/>
</fx:Declarations>
<s:VGroup id="vGroup" left="10" top="10">
<s:Button label="X" buttonDown="moveX()"/>
<s:Button label="Y" buttonDown="moveY()"/>
</s:VGroup>
<s:BorderContainer id="container" width="200" height="200" horizontalCenter="0"
verticalCenter="0">
<s:BitmapImage id="image" width="200" height="200" horizontalCenter="0" smooth="true"
source="#Embed('assets/circle.png')" verticalCenter="0"/>
</s:BorderContainer>

Cannot correctly resize an image via action script

Hi I am trying to add an image when I click on a thumbnail.
I know I have to use a listener (Event.COMPLETE ?), but the image is not resizing correctly when I rotate the tablet.
I believe the problem is that I cannot resize the image within the addImage1() function, as the image has not yet loaded, but I cannot use newImg.width within the listener function to reset the image width.
Any help would be most appreciated.
Code follows:-)
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="ZOOM Pictues with Tap"
resize="view1_resizeHandler(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.events.ResizeEvent;
public var hasImageBeenAdded:Boolean = false;
private var imageLastWidth:Number;
private var imageLastHeight:Number;
private var zoomFactor:Number;
private var imageNumber:Number;
private var rhsKeepWidth:int;
private var rhsKeepHeight:int;
private var rhsKeep:Boolean = false;
protected function view1_resizeHandler(event:ResizeEvent):void
{
if(rhsKeepWidth > rhsKeepHeight) // Was Landscape is now Portrait
{
var tmpWidth2:int = rhsKeepWidth;
var tmpHeight2:int = rhsKeepHeight;
rhsKeepWidth = tmpHeight2-lhs.width;
rhsKeepHeight = tmpWidth2+lhs.width;
}
else //Was Portrait is now Landscape
{
var tmpWidth1:int = rhsKeepWidth;
var tmpHeight1:int = rhsKeepHeight;
rhsKeepWidth = tmpHeight1-lhs.width;
rhsKeepHeight = tmpWidth1+lhs.width;
}
addImage1();
}
protected function removeAllImages():void
{
var totalElements : int = rhs.numElements;
for(var i:int=totalElements-1;i>=0;i--)
{
rhs.removeElementAt(i);
}
}
private function onImageLoaded(e:Event):void
{
var zoomFactor1:Number;
var zoomFactor2:Number;
imageLastWidth = e.target.sourceWidth;
imageLastHeight = e.target.sourceHeight;
if(rhsKeep != true) //Need to set the rhs VGroup dimensions
{
rhs.width = hGroup1.width-lhs.width;
rhs.height = hGroup1.height;
rhsKeep = true;
rhsKeepWidth = rhs.width;
rhsKeepHeight = rhs.height;
}
zoomFactor1 = rhsKeepWidth/imageLastWidth;
zoomFactor2 = rhsKeepHeight/imageLastHeight;
if(zoomFactor1 < zoomFactor2)
{
zoomFactor = zoomFactor1;
}
else
{
zoomFactor = zoomFactor2;
}
trace("zoomFactor=" + zoomFactor);
}
public function addImage1():void
{
var i:int;
var newImg:Image = new Image();
removeAllImages();
newImg.source = "images/1.jpg";
newImg.x = 0;
newImg.y = 0;
newImg.addEventListener(Event.COMPLETE,onImageLoaded);
rhs.addElementAt(newImg,0);
hasImageBeenAdded = true;
imageNumber = 1;
trace("Image Width= " + newImg.width);
newImg.scaleX = newImg.scaleY = zoomFactor;
}
]]>
</fx:Script>
<s:HGroup id="hGroup1" width="100%" height="100%">
<s:Scroller id="scrollerL" height="100%">
<s:VGroup id="lhs" width="15%" height="100%" gap="10"
horizontalAlign="center" verticalAlign="top">
<s:Image width="100" height="100" source="thumbs/1.jpg" click="addImage1()"/>
</s:VGroup>
</s:Scroller>
<s:Scroller id="scroller1" height="100%" width="100%">
<s:VGroup id="rhs" height="100%">
</s:VGroup>
</s:Scroller>
</s:HGroup>
</s:View>
Regarding the device orientation, you should use:
trace(Stage.deviceOrientation);
trace(Stage.orientation);
They are read-only strings outputting information regarding the device's and the screen's orientation. You may manually set the stage's orientation:
Stage.setOrientation(StageOrientation.DEFAULT);
Regarding the image loading, you need an eventListener for Event.COMPLETE and you should also cast the content as Image:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(event:Event):void {
trace("Image loaded");
// Handle code here
// Use: (loader.content as Image)
});
loader.load(new URLRequest("images/1.jpg"));

Flex/AS3: a generic click-and-drag zoom example, working except for one issue

I adopted the code from this website,
http://blog.ninjacaptain.com/2010/03/flex-chart-zoom-window/
Note that the bug mentioned in the web blog concerning displaying datatips appears to have been fixed (at least as of 4.5.1 SDK). I'm able to see the datatips fine.
The code works well except for one issue. I've added the complete code below so you can just copy and paste as a new Flex application and run it.
The problem is simply when the user clicks once without dragging, which gives the following error (make sure you click it AS SOON AS the app first runs):
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]
Is there a way to capture when the user clicks without dragging like this and call some function to handle it? Or, any way to avoid this error? Thanks for any comments/suggestions.
The code is:
<?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>
UPDATE:
Here's the solution, in case it's useful to others. I've added an if statement to check for null dragStart and dragEnd values, as discussed in the answer below. Also, I've removed the drop shadow that flex places by default on the line series, so a warning doesn't appear if the zoom area the user selects is too small.
<?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>
You are getting this error because the variable dragEnd is never set if the user just clicks the mouse. The easiest way to prevent this would be check for null values inside your finishDraw function:
private function finishDraw(e:MouseEvent):void
{
zooming = false;
chartCanvas.clear();
if(dragStart && dragEnd)
{
//your stuff here
//...
}
//reset values for next time
dragStart=null;
dragEnd=null;
}
And this should avoid any further 1009 Errors there. Beware I am getting some Warnings if I drag a tiny small zoom window and then release the mouse because Flash has a limit on how big a DisplayObject can be, so you should validate also the dimensions of the zoom window.
Hope this helps!

Flex 4.5 HSlider and the Accelerometer?

I'm trying to use a spark HSlider on a mobile device. I can get the thumb to move just fine in response to the accelerometer. But there are two problems:
1) the thumb flies right off the end of the screen when you tilt it in either direction. I've put in code below to try to stop this but it doesn't work.
2) I've got a little meter needle that is supposed to follow the position of the thumb, i.e. it points at it. This works fine when I drag the thumb -- when the change event is firing -- but not when the accelerometer is making it move. I think (not sure) that this might work if I could force the change event to fire?
Edit: The trace statements show that h.value isn't changing when it's the that accelerometer makes the thumb move. Also I removed a duplicate call to init().
Code:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView" creationComplete="init()">
<fx:Script>
<![CDATA[
import flash.sensors.Accelerometer;
import mx.events.FlexEvent;
public var a:Accelerometer = new Accelerometer();
private var xSpeed:Number = 0;
private var newX:Number = 0;
private function init():void
{
this.addEventListener( Event.ADDED_TO_STAGE , addedToStageHandler );
}
private function addedToStageHandler(e:Event):void
{
h.value = h.minimum;
needle.rotationZ = h.value + 180;
if(Accelerometer.isSupported)
{
a.addEventListener(AccelerometerEvent.UPDATE, readA);
stage.autoOrients = false;
}
}
private function readA(e:AccelerometerEvent):void
{
xSpeed -= e.accelerationX * 5;
newX = h.thumb.x + xSpeed;
if(h.thumb.x < 0 )
{
newX = 0;
xSpeed = 0;
}
else if (newX > stage.stageWidth - h.thumb.width) //(newX > stage.stageWidth - RADIUS)
{
newX = stage.stageWidth - h.thumb.width;
xSpeed = 0;
}
else
{
newX += xSpeed
}
h.thumb.x += xSpeed;
trace("thumb.x = " + h.thumb.x);
needle.rotationZ = h.value + 180;
trace("h.value = " + h.value);
trace("needle.rotationZ = " + needle.rotationZ);
}
protected function h_changeHandler(event:Event):void
{
// TODO Auto-generated method stub
needle.rotationZ = h.value + 180;
trace(needle.rotationZ);
}
]]>
</fx:Script>
<s:HSlider change="h_changeHandler(event)" minimum="-54" maximum="54" id="h" width="300" x="158" y="74"/>
<s:BorderContainer x="158" y="200" width="300" height="1">
<s:Rect id="needle" x="150" y="0" width="2" height="100" rotation="180">
<s:fill>
<s:SolidColor color="#FF0000"/>
</s:fill>
</s:Rect>
</s:BorderContainer>
</s:View>
Looks like you are moving the thumb's position directly. You should instead be changing the HSlider.value property and letting the component position the thumb accordingly.
The change event only fires if the value of the HSlider is changed via a user interaction (like a drag/click). Sounds like you might want to listen for the valueCommit event which will fire whenever the value of the HSlider changes whether that is programmatically or via user interaction.