Welcome to an introduction of the Flash Camouflage Framework. Flash Camo (for short) is a graphics framework that is broken down into 3 core areas: "Decals", the "CSS Parser", and the "CamoDisplay". These systems can be used individually or combined to fit your needs. When used together they form a powerful set of tools to help skin and style any Flash application. With Camo's modular approach, you can use as little or as much of the framework as you want.
In this two part tutorial we're going to build a simple website to show how easy it is to incorporate Flash Camo into your next project.
Preview
We're going to recreate the "Bobble Person Site Launcher" from http://jessefreeman.com.
Step 1: Checkout Flash Camo 2.2
In this tutorial we're going to use Flex Builder. If you haven't worked in Flex Builder or need help getting a Flex Library project setup, take a look at my "Flash Development Sandbox Tutorial" (part 1 and part 2). This tutorial will also walk you through downloading and setting up Flash Camo from SVN. For this project we're going to be using version 2.2 of the framework, you can check it out here:
http://flash-camouflage.googlecode.com/svn/tags/FlashCamo_2.2.0_beta

Step 2: Setting Up Our Project
Create a new project called BobbleHeadApp and make sure you link it to the Flash Camo SWC we checked out in step 1.

You will also need the following resource folders to get started. Download them from here and place them in your html-template folder. I've included an updated html-template file linked to swfObject along with the layout of our external resource folders.

Step 3: Creating a Flash Camo Doc Class
Now that we have our project and resources set up, let's start with our Doc Class (BobbleHeadApp.as). You should replace your doc class with the following code:
package { import flash.display.Loader; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLRequest; [SWF( width="600", height="400", backgroundColor="#3A3E4A", framerate="31" )] public class BobbleHeadApp extends Sprite { private var fontSWF : Loader; public function BobbleHeadApp() { configureStage( ); loadFonts( ); } private function configureStage() : void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; } private function loadFonts() : void { fontSWF = new Loader( ); fontSWF.contentLoaderInfo.addEventListener( Event.COMPLETE, onFontsLoaded ); fontSWF.load( new URLRequest( "swfs/fonts/FontLibrary.swf" ) ); } private function onFontsLoaded(event : Event) : void { fontSWF.contentLoaderInfo.removeEventListener( Event.COMPLETE, onFontsLoaded ); init( ); } protected function init() : void { trace( "Hello World" ); } } }
Sometimes when testing local files you may see the following error:
Error #2044: Unhandled SecurityErrorEvent:. text=Error #2140: Security sandbox violation: ... Local-with-filesystem and local-with-networking SWF files cannot load each other.
If this happens to you, add the following "-use-network=false" in your projects compiler Arguments. This will circumvent any Security Sandbox issues you may get while locally testing.
Now you should be ready to compile the swf and take a look at the output window. You will see "Hello World":

If you check your browser's connections the font swf should have been loaded:

Step 4: Creating a Global PropertySheet Manager
We're going to need a central place to store and access our css properties. Before we do this let's talk about the CamoPropertySheet. You may have used CSS with Flash in the past but this is something completely new and unique to Flash Camo. Camo's custom CSS parser, the "CamoPropertySheet" (found inside of the "camo.core.property" package), goes well beyond the native StyleSheet class by supporting style inheritance, pseudo selectors and merging styles on the fly. The goal of the CamoPropertySheet is to make styles something you can apply to any of your classes instead of just TextFields. CSS is a great way to define your class's properties in an external file and Camo helps convert these css styles into property/value pairs you can apply to any Object. Camo also has a CamoPropertySheetManager (found inside of the "camo.core.managers" package) class for handling multiple CamoPropertySheets in one application.
Since we will need all of our classes to have access to the CamoPropertySheetManager we're going to create a Singleton. This Singleton will enforce one instance of our CamoPropertySheetManager. I'm not a big fan of Singletons, but in situations like this they're a necessary evil. We'll need a single class instance in charge of managing and returning requested PropertySelectors. I will explain later what a ProperySelector is and how it will function in our application.
Let's create a new class called "GlobalPropertySheetManager" and put it in the "com.jessefreeman.managers" package.

Here is the class's code:
package com.jessefreeman.managers { import camo.core.managers.CamoPropertySheetManager; public class GlobalPropertySheetManager { public static const INIT : String = "init"; private static var __instance : CamoPropertySheetManager; public function GlobalPropertySheetManager(enforcer : SingletonEnforcer) { if (enforcer == null) { throw new Error( "Error: Instantiation failed: Use GlobalDecalSheetManager.instance instead." ); } } public static function get instance() : CamoPropertySheetManager { if(GlobalPropertySheetManager.__instance == null) { GlobalPropertySheetManager.__instance = new CamoPropertySheetManager( ); } return GlobalPropertySheetManager.__instance; } } } internal class SingletonEnforcer { }
There are many ways to create a Singleton and the discussion is a little out of scope of this tutorial. Let's quickly talk about what is happening here. Since ActionScript 3 classes are unable to have private constructors there is no way to hide the constructor of a class. Instead of allowing the class to be created through the usual "new" construction we will enforce calling the GlobalPropertySheetManager.instance to get a reference to the class. The instance getter checks to see if the class has already been created, if the class doesn't exist it creates a new CamoPropertySheetManager. If the instance exists it will return a reference to that instance. This ensures that we only have 1 instance of the CamoPropertySheetManager in our entire application. Now we're ready to start loading in our CSS.
Step 5: Loading PropertySheet CSS
The CamoPropertySheetManager has no way to load CSS, instead we'll supply the load mechanism and tell it to parse the loaded data. We'll start by creating our load method by making use of the URLLoader. Add the following methods to our Doc Class after the "onFontsLoaded" function:
private function loadPropertySheet() : void { trace( "Loading CSS" ); propSheet = GlobalPropertySheetManager.instance; var loader : URLLoader = new URLLoader( ); loader.addEventListener( Event.COMPLETE, onPropertySheetLoad ); loader.load( new URLRequest( "css/main.properties.css" ) ); } private function onPropertySheetLoad(event : Event) : void { var target : URLLoader = event.target as URLLoader; target.removeEventListener( Event.COMPLETE, onPropertySheetLoad ); propSheet.parseCSS( "global.properties", target.data ); trace( "PropertySheetSelectors", propSheet.getPropertySheet( "global.properties" ).selectorNames ); init( ); }
You will also need to add the following properties to the Doc Class:
private var propSheet : CamoPropertySheetManager;
Then import the following classes:
import flash.net.URLLoader; import camo.core.managers.CamoPropertySheetManager; import com.jessefreeman.managers.GlobalPropertySheetManager;
Finally, we'll just alter our onFontsLoaded method to call loadPropertySheet by replacing:
init( );
with:
loadPropertySheet();
Since the URLLoader can load in any text file, we simply load in our own custom CSS then pass it off to the CamoPropertySheetManager instance that we got from the GlobalPropertySheetManager. Notice how we called "parseCSS" and passed in a name for our PropertySheet along with the string data we just loaded. That's all you need to do in order to have Camo parse your css. Later we'll talk about how we get our styles out, but here is a little more info on the PropertySheet system in camo.
Once the CSS is parsed, you can retrieve an Array of selector names by calling the "selectedNames" getter. A selector represents the name of the particular CSS style and its collection of values. The "getSelector" method will return the selector and its properties as a "PropertySelector". New selectors can be added to the CamoPropertySheet by calling "newSelector" and passing a selector name and a PropertySelector instance. Finally, you can duplicate a CamoPropertySheet by calling the "clone". We'll cover this more as we start to build out our components.
Step 6: Testing The CSS Parser
Now is a good time to test that our CSS parser works. Let's open up the css file in "html-tempalte/css/main.properties.css" and add the following style:
.TestStyle { x: 100px; y: 100px; width: 100px; height: 100px; background-color: #ff0000; border: 1px solid #000000; }
If you're working in Flex Builder and modify files in your html-template folder you will need clean the project. You can do this by going to Project > Clean... and selecting the projects you want to clean. When you clean a project, it makes a fresh copy of the html-template folder over to the bin folder. Let's say you modify something in html-template then add a new line of code. Flex Builder automatically preforms a clean. This situation only happens when you edit a file in html-template and do not do a clean. Trust me, this step will save you countless hours of frustration trying to figure out why your changes are not showing up in the browser. I usually map the clean project action to option + command + c since I use it so much.

Now if you recompile the swf and look at the output you should see the name ".TestStyle" selector name next to the "PropertySheetSelectors" trace.

Let's try to retrieve our style and trace it out. Replace:
trace( "PropertySheetSelectors", propSheet.getPropertySheet( "global.properties" ).selectorNames );
inside of onCSSLoad from our Doc Class with this:
trace( "Test Selector", propSheet.getSelector(".TestStyle"));
Now when you compile you should see the following:
Test Selector .TestStyle{x:100;height:100background-color:#ff0000;border:1 solid #000000;selectorName:.TestStyle;y:100;width:100;}
This is the string value of our PropertySelector. Looks like an object right? Well that's because it is! Camo's css parser converts css selectors into native objects. It is important to note that each value is still a string. Later we will talk about how to convert these strings into natively typed values so you can apply them to any object or class in your application. Let's start building the foundation for our project.
Step 7: Creating an AbstractComponent
Now we're ready to start building our components. We want all of our components to inherit the ability to automatically find their own CSS style. If you're not familiar with what an Abstract Class is, you may be surprised to hear that you might already be using them.
A simple explanation of an Abstract class is any class you create that has some core functions but has to be extended to added specific functionality.
Abstract Classes, much like our Singleton, should not be directly instantiated. Think of Abstract class as Interfaces but with code in them. We're simply setting up a few functions all components will need and each component will add the additional logic they require. We're going to call our Abstract Class "AbstractComponent" and put it in the "com.jessefreeman.components" package.

Let's take a look at the code:
package com.jessefreeman.components { import camo.core.display.CamoDisplay; import camo.core.property.PropertySelector; import com.jessefreeman.managers.GlobalPropertySheetManager; import flash.errors.IllegalOperationError; public class AbstractComponent extends CamoDisplay { protected static const ID_DELIMITER : String = " "; protected var defaultSelectorNames : Array; public function AbstractComponent(self : AbstractComponent, id : String) { if(self != this) { //only a subclass can pass a valid reference to self throw new IllegalOperationError( "Abstract class did not receive reference to self. " + className + " cannot be instantiated directly." ); } else { parseStyleNames( id ); init( ); } } protected function parseStyleNames(id : String) : void { defaultSelectorNames = id.split( ID_DELIMITER ); this.id = defaultSelectorNames.pop( ); // clean up selectors defaultSelectorNames.unshift( "." + className ); defaultSelectorNames.push( "#" + this.id ); } protected function init() : void { var prop : PropertySelector = GlobalPropertySheetManager.instance.getSelector.apply( null, defaultSelectorNames ); applyProperties( prop ); } } }
Even though this is a small class there's a lot going on. Let's start with the fact that it's extending the CamoDisplay. Normally we would extend Sprite or MovieClip for our display classes but because we want to take advantage of Flash Camo's BoxModel we need to build off of the CamoDisplay. We'll talk about the BoxModel and CamoDisplay a little later.
If you check out the constructor you will notice we're requesting two items, self and id. As we go through the constructor we do a verification that the class can only be instantiated by passing a reference of itself back into the constructor. You will see how this works with our "concrete" components (a concrete class is any fully functional class that extends an Abstract class). The other property id is important because we will be using that to look up the component's id from css. Once the Abstract validation has passed, we call the parseStyleName method.
The parseStyleNames method is doing something special for us. If you have worked in html and css you may know that you can overload a ID or Class tag by supplying multiple CSS selector names separated by a space. In this method we allow you to add multiple IDs separated by a space and we parse them out into individual ids. The last ID becomes the main ID for the class and we add a "#" to it automatically. We also add the class's name, from a getter of the CamoDisplay called "className", to our list of selector names. Let's look at the init function next to see how we use this.
In the init function we're doing two things; getting our selectors from the GlobalPropertySheetManager and then applying the css properties to the component itself through the applyProperties method. Take a look at how we're calling the getSelector method on the GlobalPropertySheetManager. This part may make your head explode but don't worry, I guarantee that at the end of the next few steps your code will compile and run fine whether or not you fully understand what's going on.
First off, we want to get an instance of the CamoPropertySheetManager so we call the GlobalPropertySheetManager's instance property. Remember this is a Singleton and we only have one instance of the CamoPropertySheetManager. Next we're calling its function "getSelector" but instead of passing in the names of each selector we're calling a function of the "getSelector" function. I know this is crazy but it is something built into the language. See "getSelector" is a rest function in Flash. This is a function that does not have a set number of parameters so you can pass in as many items as you want. Here is what the function actually looks like in the source code:
public function getSelector( ... selectorNames) : PropertySelector
See the "... selectorNames"? That tells the function you will get any amount of selector names, so be prepared. Since we can't just pass an array into the "getSelector" method (because it would simply see it as 1 array variable) we have to call a special function called "apply" to pass in an array of arguments. The moral of the story is that you can pass in as many selector names as you want into the "getSelector" function and the CSS parser will merge them all into a single PropertySelector. If you want to understand more about rest statements in Flash check this out.
Now that's out of the way, let's look at an example to illustrate what this class will do.
Step 8: Testing The AbstractComponent
We're going to perform a quick test to show how the AbstractComponent and BoxModel inside of the CamoDisplay work. Create a class called "SimpleDisplay" in the "com.jessefreeman.components" package.

Here is the class's code:
package com.jessefreeman.components { public class SimpleDisplay extends AbstractComponent { public function SimpleDisplay(id : String = "BoxModelDisplay") { super( this, id ); } } }
This is a very simple class, it just extends the AbstractComponent and passes a reference of itself up to super (in order to pass the AbstractComponent validation) and also passes up its id. Let's test it out by going back to our Doc Class and adding the following after our Hello World trace inside of the "init" function:
var component:SimpleDisplay = new SimpleDisplay("BoxModelDisplay"); addChild(component);
and import:
import com.jessefreeman.components.SimpleDisplay
Now if you compile the swf, you won't see anything, but the SimpleDisplay instance is there. Go into your CSS file and change the name of the ".TestStyle" to ".SimpleDisplay". Now recompile and you should see your SimpleDisplay.

Amazing, right? The SimpleDisplay automatically found its CSS Class name and applied its values. Any instance of the SimpleDisplay will automatically apply the ".SimpleDisplay" CSS Style to itself now. Let's add the following style to our CSS sheet:
#BoxModelDisplay { background-color: #00ff00; border-top: 10px; }
Now when you compile you'll see that the ID of the Component is overriding the base class style.

This is a very powerful way to customize the look and feel of your application. Now you can use CSS to style any class that extends the AbstractComponent. This is all made possible by Camo's BoxModelDisplay. The "BoxModelDisplay" allows us to apply Margin, Padding, Border and a Background (Color/Image). Camo's BoxModel works exactly like CSS's. Here is a diagram:

As you may recall, our AbstractComponent extended the CamoDisplay which in turn extends the BoxModel. Well, the CamoDisplay not only inherits all of the BoxModel's layout but it also adds a few extra features. You can read about all of the BoxModel and CamoDisplay's properties in the docs folder of your Flash Camo checkout.
Step 9: LabelComponent
We're going to create a simple component that will handle all of the text labels we need in our application. This is also a good component to help illustrate how the ProperySelector works. To get started, create a new class called "Label" in "com.jessefreeman.components" package.

Here is the code:
package com.jessefreeman.components { import camo.core.enum.CSSProperties; import flash.text.StyleSheet; import flash.text.TextField; import flash.text.TextFormat; public class Label extends AbstractComponent { protected var _styleSheet : StyleSheet; protected var textField : TextField = new TextField( ); protected var proxyTextFormat : TextFormat = new TextFormat( ); protected var _textAlign : String = CSSProperties.LEFT; public function set autoSize(value : String) : void { textField.autoSize = validateAutoSize( value ); invalidate( ); } public function set antiAliasType(value : String) : void { textField.antiAliasType = validateAntiAliasType( value ); invalidate( ); } public function set embedFonts(value : Boolean) : void { textField.embedFonts = value; invalidate( ); } public function set sharpness(value : Number) : void { textField.sharpness = value; invalidate( ); } public function get text() : String { return textField.text; } public function set text(value : String) : void { textField.text = value; invalidate( ); } public function set textAlign(value : String) : void { _textAlign = value; invalidate( ); } public function set textFieldWidth(value : Number) : void { textField.width = value; invalidate( ); } public function set textFieldHeight(value : Number) : void { textField.height = value; invalidate( ); } public function set textFieldAlign(value : String) : void { proxyTextFormat.align = value; invalidate( ); } public function set fontFace(value : String) : void { font = value; invalidate( ); } public function set fontSize(value : Number) : void { size = value; invalidate( ); } public function set font(value : String) : void { proxyTextFormat.font = value; invalidate( ); } public function set color(value : uint) : void { proxyTextFormat.color = value; invalidate( ); } public function set size(value : Number) : void { proxyTextFormat.size = value; invalidate( ); } public function set letterSpacing(value : Number) : void { proxyTextFormat.letterSpacing = value; invalidate( ); } public function Label( id : String = "label" ) { super( this, id ); } override protected function init() : void { textField.selectable = false; textField.autoSize = "left"; addChild( textField ); super.init( ); } override protected function draw() : void { proxyTextFormat.align = _textAlign; textField.defaultTextFormat = proxyTextFormat; textField.setTextFormat( proxyTextFormat ); super.draw( ); } public function setTextFormat(format : TextFormat, beginIndex : int = - 1, endIndex : int = - 1) : void { textField.setTextFormat( format, beginIndex, endIndex ); } public static function validateAntiAliasType(value : String) : String { switch (value) { case CSSProperties.ADVANCED: return value; break; default: return CSSProperties.NORMAL; break; } } public static function validateAutoSize(value : String) : String { switch (value) { case CSSProperties.LEFT: case CSSProperties.RIGHT: case CSSProperties.CENTER: return value; break; default: return CSSProperties.NONE; break; } } } }
As you can see, this label component functions like a TextField through composition. I do this by creating a instance of a TextField and uses setters to update its values. This may seem like over-kill, but by doing things this way I take advantage of two features from Flash Camo. First off I extend the AbstractDisplay > CamoDisplay > BoxModelDisplay set of classes so I get CSS styling and BoxModel for "free". Also, I no longer need to create a separate TextFormat to style the TextField and apply it each time there's a change. This component allows me to simply apply TextFormat properties and it automatically updates the TextField instances properties. This is important when we look at how PropertySelectors are applied.
I have already shown you how our AbstractComponent automatically applies css styles to our components but let's look at what goes on behind the scenes in the CamoDisplay when we call "applyProperties". Here's what the code looks like:
public function applyProperties(style: PropertySelector):void { clearProperties(); PropertyApplierUtil.applyProperties(this, style); invalidate(); }
A few things are going on here. First, all properties are cleared from the CamoDisplay. Next, we use a utility class called PropertyApplierUtil to apply a PropertySelector to the CamoDisplay instance. I mentioned earlier that PropertySelectors are Objects created from CSS selectors. Well the values of the object are strings, but we will need a way to convert a font's size to a number. This utility automates the process by analyzing the value types of a class's public variables and getters, then converts the matching PropertySelector's values to the correct type. Here is an example.
Let's say you want to set the font size on our Label component. You may have a style that looks like this:
.Label { size: 30; }
When you apply the ".Label" PropertySelector to the Label instance (remember this is done automatically for us) the PropertyApplierUtil looks for a property called "size" on the Label instance. Once it finds it, it checks the properties value. Since we have a getter called size that accepts a number value, the utility will automatically convert the PropertySelector's size value into a number and set it on our component. You don't have to do anything special, the utility natively handles Strings, Number, Arrays, Objects, Colors (units) and a few other types. As long as your public variable has a type the utility can convert, any matching properties will automatically be set. We will see this in action in the next step.
Step 10: Using the Label Component
Let's go into our Doc Class and replace our SimpleDisplay demo by replacing the init function with the following:
protected function init() : void { trace( "Hello World" ); createLabel(); }
Then create a new function under our init function:
private function createLabel() : void { label = new Label( "siteLabel" ); addChild( label ); }
Don't forget to add the following parameter:
private var label : Label;
As well as our import:
import com.jessefreeman.components.Label;
Finally, we'll need to add the following styles to our css file:
.Label { font: Arial Black; size: 20px; embedFonts: true; color: #ffffff; anti-alias-type: advanced; } #siteLabel { text: ANATOMY OF JESSE FREEMAN; x: 155; y: 60; size: 18px; rotation: 24px; letter-spacing: -2; text-height: 30px; alpha: .5; }
Now if you compile the swf you should see your label with the text "ANATOMY OF JESSE FREEMAN".

The label automatically used ".Label" along with "#siteLabel" and styled itself. See how easy that was? A few lines of code, some CSS and we're up and running in no time.
Now you have a reusable Label Component that can by styled with css. This class is by no means finished. You can continue to add as many getters or setters as you need to make this Label fully customizable. I simply provided you a base to get started with. As you begin to extend off the framework it's important to understand some of the underlying draw logic.
Step 11: CamoDisplay Invalidation
You may have noticed from our Label component that the "invalidate()" method is called after we change a value in any of the setters. Redrawing the BoxModel and display is very expensive so to help alleviate the burden of constantly refreshing the display, the CamoDisplay waits until the next frame to redraw itself. This technique is used in Adobe's own components and is very efficient in cutting down on waisted cpu processes. It is something you should keep in mind when extending the CamoDisplay and using invalidate on your own custom setters.
The other important method you should know about is "draw()". After the display has been invalidated and a new frame is being rendered by the Flash Player, draw gets called automatically by the CamoDisplay. If you have custom display logic this is the best place to put it. Once "draw" is called, the invalidate flag is reset and the CamoDisplay will not redraw itself until it is invalidated again.
If you are doing complex animations like changing the width and height or require constant updates, you can override the invalidation by calling "refresh()" at any time. This will immediately force the CamoDisplay to redraw. Use this when you need to have constant display updates since it can become very intensive to render.
Step 12: Create A Bobble Container
We're going to create a class called "BobbleContainer" in "com.jessefreeman.components" package

Here's the code:
package com.jessefreeman.components { import com.jessefreeman.components.AbstractComponent; import flash.events.IEventDispatcher; import flash.events.MouseEvent; public class BobbleContainer extends AbstractComponent { protected var _active : Boolean; public var rollOverForce : Number = 1; public var noddingForce : Number = 0; public var noddingAngle : Number = 0; public var noddingRange : Number = 30; public var noddingHit : Number = .7; public var noddingDamp : Number = .985; public function BobbleContainer(id : String = "bobbleContainer") { super( this, id ); } public function set active(value : Boolean) : void { _active = value; if(_active) addEventListeners( this ); else removeEventListeners( this ); } public function get active() : Boolean { return _active; } override protected function init() : void { super.init( ); } protected function addEventListeners(target : IEventDispatcher) : void { target.addEventListener( MouseEvent.ROLL_OVER, onRollOver ); target.addEventListener( MouseEvent.ROLL_OUT, onRollOut ); } protected function removeEventListeners(target : IEventDispatcher) : void { target.removeEventListener( MouseEvent.ROLL_OVER, onRollOver ); target.removeEventListener( MouseEvent.ROLL_OUT, onRollOut ); } public function calculateBobble() : void { if (noddingForce) { if(noddingForce < .05) noddingForce = 0; rotation = Math.sin( noddingAngle ) * noddingForce * (noddingRange * .5); noddingAngle += noddingHit * noddingForce; noddingForce *= noddingDamp; } } protected function onRollOver(event : MouseEvent) : void { if(noddingForce < (rollOverForce * .5)) { noddingAngle = 0; noddingForce = rollOverForce; } } protected function onRollOut(event : MouseEvent) : void { } } }
In our Doc Class, let's create a BobbleContainer instance to test our code with. Let's add the following function below createLabel:
protected function createPerson() : void { bobbleTest = new BobbleContainer("TestContainer"); addChild(bobbleTest); }
You will need the following property:
private var bobbleTest : BobbleContainer;
then import:
import com.jessefreeman.components.BobbleContainer;
In order to get this to work we will need to add a few more things. In our init function add the following:
createPerson(); addEventListener( Event.ENTER_FRAME, onEnterFrame );
Below the init function add:
private function onEnterFrame(event : Event) : void { bobbleTest.calculateBobble(); }
We're almost done. Add the following to your CSS file and hit compile:
.BobbleContainer { active: true; rollOverForce: 1; roll-over-force: 1; nodding-force: 0; nodding-angle: 0; nodding-range: 10; nodding-hit: .7; nodding-damp: .985; } .SimpleDisplay #TestContainer { border: 4 solid white; }
You should now be looking at a red box with a white border which bobbles when you move your mouse cursor over it.

Now that I've fully introduced Flash Camo's CSS parser I'm going to refer to it as Properties/Property Sheet instead of CSS from this point on. You're encouraged to think of Camo's PropertySheets as a natural extension of configuring the property values of your classes. Go ahead and change the values of the "nodding-range" or the "nodding-hit" to see how it works. Imagine configuring your entire application without having to recompile! Not only that but you can simply restyle an app by pointing it to a new PropertySheet.
Before we go, let's take a look at one last thing. Notice in our PropertySheet the ".SimpleDisplay #TestContainer" selector. Flash Camo supports selector inheritance. We refer to "#TestContainer" as the subject and ".SimpleDisplay" would be an ancestor element. If you were to call "getSelector" for " #TestContainer" the parser will automatically return a PropertySelector with properties from ".SimpleDisplay" and "#TestContainer" merged together. Any conflicting properties will be overridden by the subject style. Pretty cool right?
Step 13: Bobble Person
Now we're ready to create a class that will manage our BobbleContainer body parts. Let's create a Class called "BobblePerson" in the "com.jesseFreeman.components" package.

Here's the code:
package com.jessefreeman.components { import com.jessefreeman.components.AbstractComponent; public class BobblePerson extends AbstractComponent { protected var _partIds : Array = new Array( ); protected var partInstances : Array = new Array( ); public var partDisplayId : String; public function set partIds(value : Array) : void { _partIds = value; createParts( ); } public function BobblePerson(id : String = "BobblePerson") { super( this, id ); } public function get partDisplay() : BobbleContainer { var index : int = _partIds.indexOf( partDisplayId ); return (index != - 1) ? partInstances[index] : null; } override protected function init() : void { super.init( ); } public function createParts() : void { var total : int = _partIds.length; var i : int; var part : BobbleContainer; var partID : String; for (i = 0; i < total ; i ++) { partID = _partIds[i]; part = new BobbleContainer( partID ); addChild( part ); partInstances.push( part ); } } public function calculateBobble() : void { var total : int = partInstances.length; for (var i : int = 0; i < total ; i ++) { BobbleContainer( partInstances[i] ).calculateBobble( ); } } } }
As you can see here, we have a few methods revolving around setting up, creating and updating body parts. This class creates parts once the partIds setter is given a value. We're doing this through a setter because we will be using our PropertySheet to pass in the array of parts. Once we have a list of parts the createParts method is called. It loops through the partIds Array and creates new instances of the BobbleContainer. Each container is given an ID then pushed into an array called partInstances so we can keep track of all of the parts.
Our final function is for updating the parts. Remember the enter frame loop we set up in the Doc Class that updated the demo BobbleContainer? We'll connect that loop to the BobblePerson's calculateBobble method and it will manage calling calculateBobble on each of our parts. This way we only have one enter frame loop running in our app at any time.
Now we're ready to set this class up. Let's replace a few things in our Doc Class in order get our BobblePerson up and running. Start by replacing your "createPerson" method with the following:
protected function createPerson() : void { bobblePerson = new BobblePerson( "BobblePerson" ); addChild( bobblePerson ); }
Next do a find and replace for "bobbleTest" with "bobblePerson".

Then replace:
private var bobblePerson : BobbleContainer;
with:
private var bobblePerson : BobblePerson;
Make sure you also import:
import com.jessefreeman.components.BobblePerson;
Finally, you'll need to replace your entire PropertySheet file with the following:
/* CSS file */ .BobblePerson { part-ids: head body leftArm rightArm leftLeg leftFoot rightLeg; part-display-id: body; } .Label { font: Arial Black; size: 20px; embedFonts: true; color: #ffffff; anti-alias-type: advanced; } #siteLabel { text: ANATOMY OF JESSE FREEMAN; x: 155; y: 60; size: 18px; rotation: 24px; letter-spacing: -2; text-height: 30px; alpha: .5; } .BobbleContainer { active: true; rollOverForce: 1; roll-over-force: 1; nodding-force: 0; nodding-angle: 0; nodding-range: 10; nodding-hit: .7; nodding-damp: .985; background-color: silver; background-color-alpha: .5; } #body { x: 355; y: 229; width: 181; height: 144; } #head { x: 449; y: 250; width: 139; height: 143; } #leftArm { x: 381; y: 313; width: 195; height: 80; } #leftFoot { x: 145; y: 213; width: 127; height: 80; } #leftLeg { x: 263; y: 235; width: 125; height: 80; } #rightArm { x: 419; y: 176; width: 184; height: 125; } #rightLeg { x: 275; y: 176; width: 218; height: 171; }
If you do a quick compile you'll see a bunch of semi-transparent boxes overlapping each other, which bounce when you roll your mouse over them.

We have the beginnings of our website. Next up we'll add Labels to each of the parts so we can keep track of them.
Step 14: Adding Labels to BobbleContainers
Let's go into our BobbleContainer class and add the following function after init:
protected function createLabel() : void { label = new Label( id + "Label" ); addChild( label ); }
Next modify the init function by adding the following:
createLabel( );
above :
super.init( );
You'll also need to create the following property:
public var label : Label;
And import our Label class:
import com.jessefreeman.components.Label;
As you can see we're creating our new label and using the parent class's "id + “Label”" for the id. This allows us to give a unique id to each label created, yet still allows us to style each one via its Class Name. Let's add the following to our PropertySheet:
/* BobbleContainer Labels */ #bodyLabel { alpha: 1; rotation: 20; x: -35; y: -30; text-align: center; auto-size: center; } #leftArmLabel { text: bFreeDesign; x: -90; y: 25; rotation: -9; size: 18; letter-spacing: -3; } #leftFootLabel { text: eMail; x: -78; y: -13; rotation: 6; } #leftLegLabel { text: FlashBum; x: -95; y: -40; rotation: 9; } #rightArmLabel { text: FlashArtOfWar; x: -80; y: -75; rotation: 32; font-size: 16; letter-spacing: -1; } #rightLegLabel { text: iLikeToDream; x: -157; y: -83; rotation: 27; }
Now if you compile you'll see that all of our Labels are working and are getting configured by CSS!
To Be Continued..
We've gone over a lot of stuff in this first tutorial. It's best that we take a break and let it all soak in. Next we'll go over how to skin our BobbleContainer using Decals, add some interactivity, optimize/clean up the code and talk about deployment. Stay tuned!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post