Flex 3D panels navigation experiment / tutorial

Sometimes you want a more creative way to navigate an application. Well 3D navigation is a good thing to try. A few months ago I did some experiments in Flash 8, AS 2 and today I felt like “let’s implement it in Flex 2.0 “:-). The technics to have the desired 3D result are rather simple so read on so you can maybe implement it in your application. Before we start looking to the code, you can see the endresult here.

http://www.youtube.com/watch?v=DXrixb7f-1w 

Let’s have a look at the code:
For this tutorial you can start using a basic Flex project. Also make sure you have  a directory called “valueObjects“. That is the directory where we are going to place our custom component classes . We then make a new Actionscript class inside the valueObjects directory called “Camera3D.as“. This is just a simple object which defines three properties coX, coY and coZ, as you may be thinking it are indeed three coordinates used to represent a 3D view.

We also write some traditional getter and setter methods in this class so that we can put that properties all private. The get and set methods need to have a different name then the properties declared at the top of your class so take care with the capital ones.

The total code for this class looks as follows:

package valueObjects{ public class Camera3D {  //properties  private var coX:Number;  

  private var coY:Number;  

  private var coZ:Number;  

  //constructor function  

  public function Camera3D():void{    

  }  

  //getter and setter methods  

  public function set COX(value:Number):void{  

   this.coX = value;  

  }  

  public function get COX():Number{  

   return this.coX;  

  }  

  public function set COY(value:Number):void{  

   this.coY = value;  

  }  

  public function get COY():Number{  

   return this.coY;  

  }  

  public function set COZ(value:Number):void{  

   this.coZ = value;  

  }  

  public function get COZ():Number{  

   return this.coZ;  

  }   

 }  

}

The next thing to do is making the custom Panel here called “Panel3D“. The extra thing in this custom Panel version is also the three extra coordinates; it extends the Panel class. 
So in the same way as previous class we now have for the Panel3D class following code:

package valueObjects{ import mx.containers.Panel; public class Panel3D extends Panel { 	private var coX:Number;  

 	private var coY:Number;  

 	private var coZ:Number;  

 	private var videoSource:String;  

 	public function Panel3D()  

 	{  

 		super();  

 		this.COX = 0;  

 		this.COY = 0  

 		this.COZ = 0;  

 	}  

 	//getter and setter methods  

 	public function set COX(value:Number):void{  

 		this.coX = value;  

 	}  

 	public function get COX():Number{  

 		return this.coX;  

 	}  

 	public function set COY(value:Number):void{  

 		this.coY = value;  

 	}  

 	public function get COY():Number{  

 		return this.coY;  

 	}  

 	public function set COZ(value:int):void{  

 		this.coZ = value;  

 	}  

 	public function get COZ():int{  

 		return this.coZ;  

 	}  

}  

}

Now we can start adding our custom Panels to an MXML Flex file. Therefore you must add your custom namespace to the application tag; I took vo as prefix: xmlns:vo=”valueObjects.*” . In this example we will just place three empty Panel3D. components. You can drag your own Panel component (in design view) from the components pane under the division “custom”. Because we inherited from the traditional Panel class we can do everything with this Panel as we can do with a regular one. You can also put sub elements in between the Panel tag: this is an example with an embedded videoDisplay component:

<vo:Panel3D click="selectObjectInSpace(event, vid1)"  id="vid1" x="184" y="52"  width="264" height="303" title="3D Panel 1" layout="absolute" alpha="1.0" cornerRadius="0" backgroundAlpha="1.0" borderAlpha="1"> <mx:VideoDisplay  id = "video1" source="assets/video1.flv" autoPlay="true" volume="0"/>   

</vo:Panel3D> 

As you can see I use the click  event to call the selectObjectInSpace(event, vid1) eventHandler. Next thing to do is to write this function. Before we do that we must add  a script block just beneath the opening application tag. In that script tag import our Camera3D class.

import valueObjects.Camera3D;

Next we define some variables we will need to calculate the scaling to have a realistic 3D effect:

private var focalLength:Number = 500 ;private var nNumber:Number;private var pansize:Number = 50;private var cameraView:Camera3D = new Camera3D();private var targetPoint:Camera3D = new Camera3D();  

private var centerX:Number = 450;  

private var centerY:Number = 100;

As you see we instantiate two objects from our custom Camera3D class. One representing a camera with his coordinates (=the user ) and one holding the target coordinates. We also specify the center of our application because we will have to do a correction on the positions. As you know the Flash player his X and Y axis are starting in the upper left corner, and actually the z-axis is pointing towards the user. Because we want to position our objects rather in the middle of our screen we can use those centering variables.
In the creationComplete event of the application we ask to start with the initApp() function. This function initialisez our camera3D instances and also our Panel3D thingys.
The code is as follows:

private function initApp():void{ //initialise camera cameraView.COX = 0; cameraView.COY = 0; 
 cameraView.COZ = 0;  

 //initialize targetPoint  

 targetPoint.COX = 0;  

 targetPoint.COY = 0;  

 targetPoint.COZ = 0;  

 //initialize the 3 panels  

 vid1.COX = 450;  

 vid1.COY = -20;  

 vid1.COZ = 3000;  

 //  

 vid2.COX = 350;  

 vid2.COY = 20;  

 vid2.COZ = 500;  

 //  

 vid3.COX = -150;  

 vid3.COY = -20;  

 vid3.COZ = 10;  

// let play videos  

 //starteasing  

}

Under the starteasing comment we are going to add an event listener for the Enter Frame event. Because we want to update the positions constantly. We can add an eventListener as follows:

//starteasingaddEventListener(Event.ENTER_FRAME, easeToTarget);

As you can see we have an event handler specified easeToTarget so we first write this function which is also private:

private function easeToTarget(event:Event):void{ // ease each camera point to its target point cameraView.COX += (targetPoint.COX-cameraView.COX)/5; cameraView.COY += (targetPoint.COY-cameraView.COY)/2.5; 
 cameraView.COZ += (targetPoint.COZ-cameraView.COZ)/2  

 ;  

 // run the "placing" function for each object  

 placeVideo(vid1);  

 placeVideo(vid2);  

 placeVideo(vid3);  

}

We just need now a function who actually changes the X, Y, scaleX and scaleY properties of our panels on screen. Well this is where placeVideo(vid) function comes in as you already were thinking…
That function looks like following code and is only using one formula (calculating a scalefactor) to have the desired 3D effect and give an illusion of a virtual space:

 private function placeVideo(custPanel:Panel3D):void{     var newX:Number = custPanel.COX-cameraView.COX;     var newY:Number = custPanel.COY-cameraView.COY;  

     var newZ:Number = custPanel.COZ-cameraView.COZ;  

     //if you put next if statement in comments ==> see some cool "buggy" graphic !! :-)==> normally the image must flip but seems like something else... but also cool :-)  

     if (newZ<0) {  

      custPanel.visible = false;  

     } else {  

      custPanel.visible = true;  

     }  

     var scaleRatio:Number = focalLength/(focalLength+newZ);  

     custPanel.x = centerX+(newX*scaleRatio);       

     custPanel.y = centerY+(newY*scaleRatio);       

     if(scaleRatio == 0){  

      custPanel.visible = false;  

     }else{  

       custPanel.visible = true;  

     }  

     custPanel.scaleX = custPanel.scaleY =  scaleRatio;      

            

    }

So that’s it. I hope you can use it some moment when making really cool applications. You can use the same technics on other components just by extending that component and give it the three coordinates as showed with the Panel3D class.
Have fun playing with Flex 2.0 !!

Happy coding,

koen (newmovieclip.com)

Advertisements

18 responses to “Flex 3D panels navigation experiment / tutorial

  1. Austin K 12 November , 2006 at 1:41 am

    Unable to see it for some reason…

  2. Pingback: Nice Flex 3D panels navigation experiment at Web 2.0 Log

  3. FlexDud 15 November , 2006 at 6:35 pm

    dont you miss this function selectObjectInSpace(); ??

  4. Alexander 17 November , 2006 at 7:07 am

    I concur. Wherefore art selectObjectinSpace()?

  5. Terry 29 November , 2006 at 9:33 pm

    I also encountered the same issue. The function selectObjectInSpace() isn’t defined. Appears to be a neat idea. I’d like to implement it.

  6. Koen 30 November , 2006 at 12:54 am

    just a fast reply …Flexdude, Alex and Terry, you are damn right ! Something went wrong when I uploaded the code but I will put the function online before Flash on the beach…I promise

  7. FlexDud 10 December , 2006 at 11:56 am

    here is somthing when we wait for the Guru 🙂

    private function selectObjectInSpace(event:MouseEvent,custPanel:Panel3D):void{
    targetPoint.COX = custPanel.COX;
    targetPoint.COY = custPanel.COY;
    targetPoint.COZ = custPanel.COZ;
    }

  8. Koen 10 December , 2006 at 12:31 pm

    That’s it FlexDud, the missing function is indeed like you are saying !
    happy coding,
    koen

  9. FlexDud 13 December , 2006 at 1:50 pm

    🙂 by the way it seeams like the panels are fliping and make a nice effect, do you have any idea how to fix the bugg ?

  10. kanu 9 April , 2007 at 10:36 am

    Some error comes up when i copy the same code in Flex file 😦

    can u pls give me the url to download this file.

    Error is at line 8:
    The prefix “mx” for element “mx:Application” is not bound.

    i did this in mxml file

    /////////////////////////////////////////////////////

    see some cool “buggy” graphic !! :-)==> normally the image must flip but seems like something else… but also cool

    if (newZ

  11. kanu 9 April , 2007 at 10:37 am

    see some cool “buggy” graphic !! :-)==> normally the image must flip but seems like something else… but also cool

    if (newZ

  12. voyzer 19 October , 2007 at 3:52 am

    in placeVideo

    var scaleRatio:Number = focalLength / (focalLength + Math.abs(newZ));

  13. abhishek 15 November , 2007 at 7:52 am

    its working good

  14. Freddy 21 November , 2007 at 12:06 pm

    Hi,

    this is really cool. I have searched for long time for a how to implement a camera view in Flex. I will enhance this to a full zoom and pan view.

  15. Vinayaka Krishnamurthy 29 January , 2008 at 10:56 am

    Hi,

    This is really fantastic, I have been looking for this effect for a long time. It would be great to have more of such tutorials for a more advanced handling of the camera.

    Thanks….

  16. Nuno 27 May , 2008 at 10:31 pm

    Hi

    i have just now discover this and it is great, congrats.
    but i´m getting a wierd error 1046 error constant:panel

    private function selectObjectInSpace(event:MouseEvent,custPanel:Panel3D):void{
    targetPoint.COX = custPanel.COX;
    targetPoint.COY = custPanel.COY;
    targetPoint.COZ = custPanel.COZ;
    }

    private function placeVideo(custPanel:Panel3D):void{

    var newX:Number = custPanel.COX-cameraView.COX;
    var newY:Number = custPanel.COY-cameraView.COY;
    var newZ:Number = custPanel.COZ-cameraView.COZ;

    can some one help me with this

  17. Nijscoman 1 July , 2008 at 8:53 am

    In addition to this great script I wrote code to assure correct overlapping of all panels. Of course you want panels to appear in the right order! (panels further away need to be placed behind panels closer by automatically)

    this is the code:

    in the placeVideo function you will need to add the following line at the end of the function:

    checkDepth(custPanel);

    the “checkDepth” function looks like this:

    private function checkDepth(d:DisplayObject):void
    {
    if (d.parent != this)
    {
    return;
    }
    var i:int = getChildIndex(d);
    while (i > 0 && d.scaleX < getChildAt(i-1).scaleX)
    {
    i–;
    }
    while (i getChildAt(i+1).scaleX)
    {
    i++;
    }
    setChildIndex(d, i);
    }

    Hope this will help you creating a much more realistic 3D panel environment!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s