Archive for July, 2010

Easing/tweened Flex Scrolling Canvas

Tuesday, July 27th, 2010

I was looking to add some acceleration/easing to the scrolling of a Flex container.

I used Tweener API and applied it to a Flex Canvas (the canvas has its scrolling policies turned off, and scrolling is provided with an external/floating scrollbar).

Click to run the swf (view source enabled)

You can see the Tweener trainsitions on their online documentation here (click on ‘transition types’).

I have tweener.swc in my libs folder.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	creationComplete="init()">

	<mx:Script>
		<![CDATA[
			import mx.effects.Glow;
			import mx.controls.Button;
			import mx.events.IndexChangedEvent;
			import mx.controls.Label;
			import caurina.transitions.Tweener;
			import mx.effects.Tween;
			import mx.events.ScrollEvent;
			import mx.controls.scrollClasses.ScrollBar;
			import mx.graphics.GradientEntry;
			import mx.graphics.LinearGradient;

			private function init():void{

				addItemsToCanvas();
				scrollControlCanvas.addEventListener(ScrollEvent.SCROLL, onCanvasScroll);
			}

			private function onCanvasScroll(event:ScrollEvent):void{
				event.stopImmediatePropagation();

				switch (event.direction) {
					case "horizontal":
						Tweener.addTween(viewCanvas,
							{horizontalScrollPosition:scrollControlCanvas.horizontalScrollPosition,
											time:0.6, transition:"linear"});
						break;
					case "vertical":
						// If you need a vertical tween add it here
						break;
				}
			}

			public function addItemsToCanvas():void{

				gradientCanvas(contentCanvas, 0x55D4FF, 0xFFD400, 0); //FF00000

				var button:Button;
				var dividerWidth:Number = 100;
				var numDividers:int = Math.floor(contentCanvas.width/dividerWidth);

				for(var i:int = 0; i < numDividers; i++){
					button = new Button;
					button.width = 40;
					button.setStyle("fontSize", 10);
					button.label = i.toString();
					button.x = ((i+1)*dividerWidth) - (dividerWidth/2) - (button.width/2) ;
					button.y = contentCanvas.height/2 - 50;
					contentCanvas.addChild(button);

					contentCanvas.graphics.beginFill(0xFFFFFF, 0.5);
					contentCanvas.graphics.drawRect((i+1)*dividerWidth, 0,
											3, contentCanvas.height);
					contentCanvas.graphics.endFill();
				}
			}

			public function gradientCanvas (cnv:Canvas, color1:uint, color2:uint, angle:int):void
			{
				var w:Number = cnv.width;
				var h:Number = cnv.height;
				var g:Graphics = cnv.graphics;

				g.clear();

				var fill:LinearGradient = new LinearGradient();
				var g1:GradientEntry = new GradientEntry(color1,0,1);
				var g2:GradientEntry = new GradientEntry(color2,1,1);

				fill.entries = [g1,g2];
				fill.angle = angle;
				g.moveTo(0,0);
				fill.begin(g,new Rectangle(0,0,w,h));
				g.lineTo(w,0);
				g.lineTo(w,h);
				g.lineTo(0,h);
				g.lineTo(0,0);
				fill.end(g);
			}
		]]>
	</mx:Script>

	<!-- The canvas you actually see. NOTE: scroll is off-->
	<mx:Canvas x="74" y="69" width="441" height="203" id="viewCanvas"
		 horizontalScrollPolicy="off" verticalScrollPolicy="off">
		<mx:Canvas x="0" y="0" id="contentCanvas" height="100%" width="3000">
		</mx:Canvas>
	</mx:Canvas>

	<!-- This canvas acts only as the scrollBar - NOTE: the dummyCanvas is empty,
		 but is exactly the same dimensions as the viewCanvas -->
	<mx:Canvas x="74" y="280" width="441" height="30" id="scrollControlCanvas"
		 verticalScrollPolicy="off">
		<mx:Canvas x="0" y="0" id="dummyCanvas" height="{contentCanvas.height}"
			width="{contentCanvas.width}">
		</mx:Canvas>
	</mx:Canvas>

</mx:Application>

Inspired by a Flash solution from Soundstep, and a more complex (than my method) animated canvas by Vladimir Tsvetkov.

Example multidimensional / 3D indexed array in Flex AS3

Monday, July 26th, 2010

Scenario: I recently created a Flex component that extended the Canvas, and was split into a large number of ‘cells’ using constraint rows/columns. In each of these cells could be placed any number of UIComponents.

Problem: At any time I could tell what cell I was mousing over e.g.  (0,1), but I also needed to get an array of the components in that cell. It was not optimal iterating accross all the UIComponents on the canvas to check their constraint row/column id’s to find the relevant ones.

Solution: Created a 3D indexed array with references to each cell’s UIComponents. To retrieve the components from cell (3, 5) I would simply use: resultsArray = _cellComponents[rowNum][columnNum];

The image below represents a basic 3D array, with each cell containing just random objects: ints, strings and an image object. The sample app code that follows shows you how to create and populate a 3D array, and has a simple interface so you can test retrieving the stored objects.

3D array visual

Simple UI for 3D Array


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import mx.controls.Button;
			import mx.events.IndexChangedEvent;

			private var _array3D:Array = new Array();
			private var _numRows:int = 2;
			private var _numColumns:int = 4;

			private function init():void{

				for(var i:int = 0; i < _numRows; i++){

					var rowArray:Array = new Array();
					_array3D.push(rowArray);

					// Create a 3d array (basically a 2d array of array objects)
					for(var j:int = 0; j < _numColumns; j++){

						var columnArray:Array = new Array();
						_array3D[i].push(columnArray);
					}
				}

				// Populate a 3d array
				_array3D[0][0].push(1, 2, 3, 4);
				_array3D[0][3].push("A", "B", "C", "D");
				_array3D[1][0].push(5, 6, 7);
				_array3D[1][1].push(8);
				_array3D[1][2].push(9, 10);
				// You can add any type of Object to the array.
				var image:Image = new Image();
				image.name = "arrayImage";
				_array3D[1][3].push(image);

			}

			private function getValues(row:String, col:String):void{

				var r:int = parseInt(row);
				var c:int = parseInt(col);

				var cell:Array = _array3D[r]1;
				var output:String = "";

				if(cell != null){

					for(var i:int = 0; i <cell.length; i++){
						output = output + " " + cell[i].toString();
					}

					if(output == ""){
						outputText.text = "No data in cell"
					}
					else{
						outputText.text = output;
					}
				}
				else{
					outputText.text = "Not a valid row/column";
				}
			}

		]]>
	</mx:Script>

	<mx:Canvas id="myCanvas" x="65" y="49" width="308" height="99" backgroundColor="#FFFFFF">
		<mx:ApplicationControlBar x="0" y="0" width="100%">
			<mx:Label text="Row"/>
			<mx:TextInput id="row" width="56"/>
			<mx:Label text="Col."/>
			<mx:TextInput id="col" width="56"/>
			<mx:Button label="Get Values" click="getValues(row.text, col.text)"/>
		</mx:ApplicationControlBar>
		<mx:Text id="outputText" x="0" y="52" width="100%" textAlign="center"/>
	</mx:Canvas>
</mx:Application>

Flex: Date.month is zero-based, Date.date is one-based!!

Wednesday, July 7th, 2010

This little issue caught me out a bit when debugging an app: the month property of a Flex Date object is zero-based, whereas the Date.date property is an integer from 1 to 31. Crazy or what!

For 07/07/2010 (07/July/2010) at 09:33 GMT, the date object is shown below:

Flex Date for 07/July/2010 09:33 GMT