Flex - (MacOs like) DockBar component

Some week ago for a project I was looking for a flex dockbar component. I found some but, the missing and most requested feature from users (and me too) was the ability to add items to the dockbar simply adding a mxml node so that click and rollover handlers, data bindings etc. could be easily implemented:

<DockBar>
   <Item click="doSomething()" rollOver="doOver" />
   <Item click="doSomethingElse()" rollOver="doOver" toolTip="click me" />
</DockBar>

I could not find anything similar, so I wrote this component.

I did zillions of experiments like this in the past, but I tried to do it easy to set up, and tried to use the less code possible.
I found suprisingly easy to build it upon the HBox and Image components. Maybe not the most sofisticated implementation but so damn simple…

With some good ideas flex can be really easy to code.

See it moving (just move the mouse to the last 20px on the bottom):

Example #1 - Basic implementation
Example #2 - Basic implementation applied to a ViewStack
Example #3 - Tooltip, handcursor, rollover effects etc.

I need a component to place at the bottom of the screen so I hardcoded some params to place it but can be very easily tweaked.

Every example has view source enabled so just right click on it to view the code.

Here’s the code from example #1:

<local:DockBar id="dockbar" horizontalCenter="0" bottom="0" width="100%" height="100" minSize="32" maxSize="80">
		<mx:Label visible="false" id="menuLabel" text="Menu" bottom="0" horizontalCenter="0" fontSize="24" color="#ffffff" alpha=".75" />
		<mx:Canvas id="activationArea" width="100%" height="20" bottom="{-activationArea.height+20}" backgroundColor="#000000" backgroundAlpha="0" rollOver="dockbar.open()" />
		<mx:HBox id="innerCanvas" bottom="{-innerCanvas.height+20}" paddingLeft="{dockbar.minSize}" horizontalCenter="0" 
			backgroundColor="#333333" backgroundAlpha=".4" hideEffect="WipeDown" showEffect="WipeUp">
			<mx:Image click="clicked(1)" source="{icon1}" />
			<mx:Image click="clicked(2)" source="{icon2}" />
			<mx:Image click="clicked(3)" source="{icon3}" />
			<mx:Image click="clicked(4)" source="{icon4}" />
			<mx:Image click="clicked(5)" source="{icon5}" />
			<mx:Image click="clicked(6)" source="{icon6}" />
			<mx:Image click="clicked(7)" source="{icon7}" />
		</mx:HBox>
	</local:DockBar>

Do not remove the menuLabel and the activationArea elements. They was coded inside the actionscript of the component but then I moved them out to the mxml to make them easier to customize.

The activation area is the area to rollOver for the component to be opened (here 20px high… maybe 30 or 40 would be more confortable). The height of the dockbar instead is 100px and is the area to rollOut to make the component close.
Other parameters such minSize and maxSize are quite self-explanatory and limits the size of the icons.

The icons are embedded clips from a swf file. The registration point of the icons must be at the bottom-center.

Ths icons sizes are calculated based on horizontal and vertical distance. Many values are still hardcoded but could be easily parametrized.

You can try changing the backgroundColor and backgroundAlpha of the dockBar and the activation area to better understand how does it work.

Just leave a comment if you like it.
Bye

Back (in #000000)

This morning I delievered the final release of one of the biggest project recently worked to… and my first flex application!

Oh yeah I started with a big ass project and spent two incredibly heavy months arguing with flex (and his bugs.. mostly design ones), but learned a lot.

The project is a new full comprehensive English language course developed for DeAgostini Editore which will be shipped on DVD-Rom in libraries and newspaper shops.

This was the full remaking of my last “language course framework/engine” which was built during years starting in 2004 in “oop” as1. On this engine many CD/DVD language courses have been produced and often with really little time for the customization.

After many years it started to feel very old (once you try as3 and flex everything previous seemed old) and really not solid so we decided to rebuild everything from scratch.

Playing with Flex has been a great experience. I liked a lot many things and hated many others of it, but love surely wins.

The coolest thing personally are not components or mxml itself but (Insert love smilies here) containers. A great part of the project was as3 assembled, but having those awesome hbox, vbox, tile, canvas etc. with auto-appearing scrollbars and fully skinnable via css really helped a lot.

I am sure I will have a little more time now to come back to my experiments, and from today… with Astro (Flash player 10) out in beta I am sure it will me more and more fun!

p.s.: Why in the world previous beta of Hydra toolkit worked perfectly with my ATI Radeon X800 XL @300fps while now that changed name in PixelBender my videocard isn’t supported?
I just tought it was not on the list and so i tried to install the toolkit but a big red alert message clearly says that every filter will be run only by CPU (@0.5fps…).

X800 will not be the most recent graphic card in the world (and I was already planning to buy a new one next week) but I think that Adobe should (and could) really extend the supported vga list before the final release. Maybe it’s already planned but found no mention about it.

Le’ts really hope they will extend this list or will be quite a difficult and risky choice to use advanced filters for commercial project.

See you soon on this pages.

Oaxoa Blog now Gravatar enabled

I just uploaded a Gravatar plugin to this blog and it works :).

A Gravatar is a Globally-Recognized-Avatar… It means that you just need to register to the Gravatar service and once you uploaded a picture of you.. it will be globally appearing on every gravatar-enabled blog.

But the coolest thing is the simple gravatar API (well more than an API it’s just a formatted request).

I am preparing a really cool experiment about Gravatars… however it’s 1.20 here now and tomorrow will be a heavy day :P, so I need some sleep. Please just be patient and check back in a few days (maybe tomorrow).

Actionscript 3 - Fun with 3D Particles

Hi there, some time since I last posted a technical post so here we are.

Brief

I was experimenting with some simple 3d formulas and this is the result…
1200 particles morphing in 3D space.
Added an interactive camera to start having fun.

Usage

Simply move your mouse up/down to move camera forward/backward and left/right for strafing.
Perss SPACE bar to cycle particle conformation between four different shapes.

For better experience move forward and backward while particles are morphing…
Following the morph from behind while moving very fast feel very gratifing for me :)

Performances

On my old CPU it performs @31fps and neither takes the cpu at 100%
No z-sorting here, just fun. I am not trying to build a real 3d engine… and no class for now, just some procedural code, but I am sure my cool readers will not have problems wrapping it inside a class definition if needed ;)

Caurina Tweener needed.

3dparticles.gif
Experience the demo

And here come the code! Simply past it in the first frame actions of a new fla.

import caurina.transitions.Tweener;
import com.oaxoa.components.FrameRater;
import flash.events.KeyboardEvent;
 
var particles:Array=[];
var particlesXY:Array=[];
var flen:Number=100;
var lines:Sprite=new Sprite();
var renderLines:Boolean=false;
var shapeN:int=-1;
 
var camera:Object={px:0, py:0, pz:0};
 
function drawBg():void {
	var bg:Sprite=new Sprite();
	bg.graphics.beginFill(0x333333);
	bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
	bg.graphics.endFill();
	addChild(bg);
	var tf:TextFormat=new TextFormat();
	tf.color= 0x999999;
	tf.font="_sans";
	tf.size=11;
	tf.align="right";
	var t:TextField=new TextField();
	t.width=300;
	t.defaultTextFormat=tf;
	t.text="Press SPACEBAR to change particles shape";
	t.x=stage.stageWidth-t.width-10;
	t.selectable=false;
	bg.addChild(t);
}
 
function addParticle(x:Number, y:Number, z:Number):void {
	var p:MovieClip=new MovieClip();
	p.graphics.beginFill(0xcccccc);
	p.graphics.drawCircle(0, 0, 5);
	p.graphics.endFill();
	p.px=x;
	p.py=y;
	p.pz=z;
	particles.push(p);
}
 
function cycleShape():void {
	if (shapeN<3) {
		shapeN++;
	} else {
		shapeN=0;
	}
	var i:uint;
	var p:MovieClip;
	var px:Number;
	var py:Number;
	var pz:Number;
	var arad:Number;
	if (shapeN==0) {
		var a:Number=0;
		for (i=0; i<particles.length; i++) {
			a+=9;
			a%=360;
			p=particles[i];
			arad=a/180*Math.PI;
			px=Math.cos(arad)*100;
			py=Math.sin(arad)*100;
			pz=Math.floor(i/40)*60;
			Tweener.addTween(p, {time:2, transition:"EaseInOutCubic", px:px, py:py, pz:pz, delay:i/300});
		}
	} else if (shapeN==1) {
		for (i=0; i<particles.length; i++) {
			p=particles[i];
			arad=i/180*Math.PI;
			arad*=2;
			px=Math.sin(arad)*100;
			py=Math.cos(arad)*100;
			pz=i;
			Tweener.addTween(p, {time:2, transition:"EaseInOutElastic", px:px, py:py, pz:pz, delay:i/300});
		}
	} else if (shapeN==2) {
		for (i=0; i<particles.length; i++) {
			p=particles[i];
			px=Math.random()*700-350;
			py=Math.random()*700-350;
			pz=Math.random()*1700;
			Tweener.addTween(p, {time:2, transition:"EaseInOutCubic", px:px, py:py, pz:pz, delay:i/200});
		}
	} else if (shapeN==3) {
		var row:uint=0;
		var col:uint=0;
		for (i=0; i<particles.length; i++) {
			col=(i%50);
			row=Math.floor(i/50);
			p=particles[i];
			px=0
			py=-450+row*40;
			pz=col*40;
			Tweener.addTween(p, {time:2, transition:"EaseInOutBounce", px:px, py:py, pz:pz, delay:i/200});
		}
	}
}
 
 
function render():void {
 
	var c:uint=0;
	for each (var p:MovieClip in particles) {
		var dx:Number=p.px-camera.px;
		var dy:Number=p.py-camera.py;
		var dz:Number=p.pz-camera.pz;
		var scale:Number=flen/(flen+(dz));
		if (scale<0) {
			scale=0;
		}
		p.x=stage.stageWidth/2+dx*scale;
		p.y=stage.stageHeight/2+dy*scale;
		p.scaleX=p.scaleY=scale;
		particlesXY[c]={x:p.x, y:p.y, scale:scale};
		c++;
	}
	// activate lines render, false by default, not really a wireframe
	if (renderLines) {
		lines.graphics.clear();
		lines.graphics.lineStyle(2, 0xCCCCCC);
		for(var i:uint=0; i<particlesXY.length; i++) {;
			var t:Object=particlesXY[i];
			if (i<particlesXY.length-1 && t.scale>0) {
				var t2:Object=particlesXY[i+1];
 
				lines.graphics.moveTo(t.x, t.y);
				lines.graphics.lineTo(t2.x, t2.y);
			}
		}
	}
}
 
stage.addEventListener(KeyboardEvent.KEY_UP, onkey);
function onkey(event:KeyboardEvent):void {
	if(event.keyCode==32) {
		cycleShape();
		render();
	}
}
addEventListener(Event.ENTER_FRAME, onframe);
 
function onframe(event:Event):void {
	var offx:Number=stage.stageWidth/2-mouseX;
 
	camera.px-=offx/5;
	if (camera.px>150) {
		camera.px=150;
	}
	if (camera.px<-150) {
		camera.px=-150;
	}
	var offy:Number=stage.stageHeight/2-mouseY;
	if(offy>70) offy=70;
	if(offy<-70) offy=-70;
 
	camera.pz+=offy/5;
	if (camera.pz>1700) {
		camera.pz=1700;
	}
	if (camera.pz<0) {
		camera.pz=0;
	}
	render();
}
 
function addSprites():void {
	for each (var p:MovieClip in particles) {
		addChildAt(p, 1);
	}
}
 
function init():void {
	for(var i:uint=0; i<1200; i++) {
		var px:Number=0;
		var py:Number=0;
		var pz:Number=0;
		addParticle(px, py ,pz);
	}
}
 
// main methods call
drawBg();
addChild(lines);
init();
addSprites();
cycleShape();
render();
var fr:FrameRater=new FrameRater(0xffffff);
addChild(fr);

Have fun and leave a comment if you like.

Easter cleaning - WP update, new theme and bye bye nofollow

I just updated WP to the latest release, installed a new theme (Dilecto was very cool but too fancy for my flavour) and installed a nofollow-remover plugin.

New theme:

This new templates name is “Not so Fresh” and you can find more info at the bottm of the page. It’s still far from waht I want but is much cleaner than the previous and I am still to busy/lasy to customize it or do my own. I work on too many sites and css designs to have some time to do my own.
As we say here in Italy the shoemaker walks with broken shoes…

Nofollow:

I personally admin every comment so there’s no chance that a spam comment could pass so why not give a little reward to my comments’ authors? I just installed NoFollow Free and from what I read in the features list is the best among the others.

It has many available settings. My settings will work in this way:
The nofollow attribute is removed from authors’ websites links but not from link in the comment text.
The attribute will be removed to author with more than 5 comments.

I am working on some cool experiments so stay tuned for more goodies.

Bye

Happy Easter 2008

It’s almost four months since I started this blog and yet have a lot of aficionados readers and useful comments.
Thanks to everybody for the support and have a happy Easter! :-)

istock_000005200432xsmall.jpg

APE - Actionscript Physics Engine - Test 1

Hi there, I started today playing with APE - Actionscript Physics Engine (Alpha 0.45) and it’s really cool stuff.

My intention would be creating a physical based little funny game, but I need to start with some simple experiments so why not post’em here…

There are issues I still don’t understand so don’t know if are bugs or simply I should read APIs deeper. One thing I cannot realize is why if I change runtime the height or width properties of a RectangleParticle or the radius property of a CircleParticle, collisions are calculated correctly upon the new values, but cannot really manage to force a correct repaint with the new dimensions.
In the code that follows I have trhee rotating RectangleParticles with the alwaysRepaint parameter set to true and I can correctly see the rectangles rotating, but if I change size runtime nothing changes (except the collision). Neither calling the particle.paint() method at everyframe, which is an alternative to alwaysRepaint method, works.

However everything else is really supercool and moving a lot of self colliding particles @60fps!

Here we go with my first test:


ape_test11.gif
Run the test

Simply set the import folders for external classes in your Flash or Flex project pointing to the folder where you downloaded APE and copy these few lines of code to the first frame in you timeline.

import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.filters.GlowFilter;
import org.cove.ape.*;
import com.oaxoa.components.FrameRater;
 
stage.frameRate = 60;
addEventListener(Event.ENTER_FRAME, run);
 
APEngine.init(1/4);
APEngine.container = this;
 
// masslessforce aka gravity
APEngine.addMasslessForce(new Vector(0,4));
 
var defaultGroup:Group = new Group();
defaultGroup.collideInternal = true;
 
// instance the three rectangles
var rp:RectangleParticle = new RectangleParticle(150,250,250,25,0,true);
var rp2:RectangleParticle = new RectangleParticle(400,250,250,25,0,true);
var rp3:RectangleParticle = new RectangleParticle(275,450,150,20,0,true);
 
// set some graphical style
rp.setFill(0x666666);
rp2.setFill(0x666666);
rp3.setFill(0x666666);
rp.setLine(0, 0xff0000, 0);
rp2.setLine(0, 0xff0000, 0);
rp3.setLine(0, 0xff0000, 0);
 
// fixed particles are not always repainted for speed issues, but these ones will
// be rotating, so we need to force the repaint at every frame
rp.alwaysRepaint=true;
rp2.alwaysRepaint=true;
rp3.alwaysRepaint=true;
 
// add rectangles
defaultGroup.addParticle(rp);
defaultGroup.addParticle(rp2);
defaultGroup.addParticle(rp3);
 
APEngine.addGroup(defaultGroup);
 
var a:Number=0;
var r:Number=15;
function run(evt:Event):void {
	// check for removal if the particle is offscreen
	for each (var p:AbstractParticle in defaultGroup.particles) {
		if (p.py>550+r || p.px<-r || p.px>550+r) {
			defaultGroup.removeParticle(p);
		}
	}
 
	// some rotating fun
	a+=4;
	var arad:Number=a/180*Math.PI;
	rp.angle=Math.cos(arad)*45;
	rp2.angle=-Math.cos(arad)*45;
	rp3.angle=a*2;
 
	// do the simulation and the render step
	APEngine.step();
	APEngine.paint();
 
}
// timer to add particles
var timer:Timer=new Timer(50);
timer.addEventListener(TimerEvent.TIMER, ontimer);
timer.start();
function ontimer(event:TimerEvent):void {
	var cp:CircleParticle = new CircleParticle(Math.random()*350+100,-20,Math.random()*r+1);
	cp.setFill(0x444444*Math.random()+0xbbbbbb);
	cp.setLine(0, 0xff0000, 0);
	defaultGroup.addParticle