Flash Photo Grid

February 22nd, 2009

In this post we are going to create a grid of images that is resizable using two text boxes. This task came from the Deconstruct red-issue.com post.

We are going to build a Flash file that will take the list of images and put the in a grid using the gridWidth and gridHeight as well as the text from the width_tb and height_tb text Fields.

To make reusing this file easier we are first going to define a variable to determine the directory the images are stored in. If you want to use the same images as this example you can download them here. Lets assume the images are in an images directory in the same folder as our SWF file.

var imageDir:String = new String( "images /" );

Once this file was being used in a web site, like red-issue.com, it would like get it’s list of images from an XML file. For now lets just store them in an Array.

var gridArray:Array=new Array(
     ({image:"img_0001.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0002.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0003.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0004.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0005.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0006.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0007.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0008.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0009.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0010.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0011.jpg",imageWidth:800,imageHeight:532}),
     ({image:"img_0012.jpg",imageWidth:800,imageHeight:532}));

Next we need a vaiable to determine the width and height of the photo grid.

var gridWidth:Number=new Number(4);
var gridHeight:Number=new Number(3);

Each photo im our grid is going to need a Movie Clip, a Mask and a Loader. Lets assume the number of photos we are putting in our grid is unknown. This means we will need to store these items in three sepereate Arrays.

var movieArray:Array = new Array();
var maskArray:Array = new Array();
var loaderArray:Array = new Array();

In this example we are determining the width of each photo using two Text Fields. When we go back to the Deconstruct red-issue.com post we will be using the Stage width and height. This next excerpt of code will only be used for this example.

width_tb.tabIndex = 1;
height_tb.tabIndex = 2;
submit_btn.tabIndex = 3;
width_lb.text = "Width:";
width_tb.text = "100";
height_lb.text = "Height:";
height_tb.text = "100";
submit_btn.label = "Resize";

When the Stage is resized, or in this example when the submit Button is pressed, we need to re-size our images and masks and then re-position everything in our grid. Lets break this into three seperate steps. All our clickHandler function has to do is trigger these three functions. These three functions will assume that the Movie Clips, Masks and Loaders already exist.

submit_btn.addEventListener(MouseEvent.CLICK,clickHandler);
function clickHandler(evt:Event):void {
     for (var i:int = 0; i < gridArray.length; i ++) {
          resizeMask(i);
          resizeImage(i);
          reposition(i);
     }
}

The first functionwill reposition the Movie Clips and Masks based on the new image width and height.

function reposition(i:int):void{
     maskArray[i].x = i % gridWidth * Number(width_tb.text);
     maskArray[i].y = Math.floor( i / gridWidth ) * Number(height_tb.text);
     movieArray[i].x = i % gridWidth * Number(width_tb.text);
     movieArray[i].y = Math.floor( i / gridWidth ) * Number(height_tb.text);
}

The second function will resize the Masks.

function resizeMask(i:int):void{
     maskArray[i].width = Number(width_tb.text);
     maskArray[i].height = Number(height_tb.text);
}

And the last function will resize the Movie Clips. This is a little more complicated then resizing the masks as the Movie Clips need to remain proportianate.

function resizeImage(i:int):void {
     var widthRatio:Number = Number(width_tb.text)/gridArray[i]["imageWidth"];
     var heightRatio:Number = Number(height_tb.text)/gridArray[i]["imageHeight"];
     if( widthRatio > heightRatio){
          movieArray[i].scaleX = widthRatio;
          movieArray[i].scaleY = widthRatio;
     }else{
          movieArray[i].scaleX = heightRatio;
          movieArray[i].scaleY = heightRatio;
     }
}

Now that all of our functions are set to go we just need to crreate our Masks, Loaders and Movie Clips. The first seven lines of our this for loop create and resize our Masks, the next seven after that create the image Movie Clips and load our images in and finally the two lines set the Masks and execite the reposition function.

for (var i:int = 0; i < gridArray.length; i ++) {
     maskArray[i] = new MovieClip();
     maskArray[i].graphics.lineStyle(0,0x000088);
     maskArray[i].graphics.beginFill(0x000088);
     maskArray[i].graphics.drawRect(0,0,1,1);
     maskArray[i].graphics.endFill();
     resizeMask(i);
     grid_mc.addChild(maskArray[i]);
     movieArray[i] = new MovieClip();
     var request:URLRequest=new URLRequest(imageDir + gridArray[i]["image"]);
     loaderArray[i] = new Loader();
     loaderArray[i].load(request);
     resizeImage(i);
     movieArray[i].addChild(loaderArray[i]);
     grid_mc.addChild(movieArray[i]);
     movieArray[i].mask=maskArray[i];
     reposition(i);
}

The Flash plugin is required to view this object.

Download the Image Grid Flash File

Flash XML Weather Feed

February 22nd, 2009

Integrating Flash with XML is increasingly becoming more and more common and useful! Working with XML has also become a whole lot easier with Actionscript 3.0. In this example we will create a Flash file that will display the current weather. We will get our weather information from Boy Genius. The weather feeds are available here. For this example we are going to use the weather from Toronto Ontario Canada.

Here is the XML we will be working with:

<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="/weather.xsl" alternate="no" ?>
<city>
     <name>Toronto</name>
     <state>Ontario</state>
     <temperatures>
          <fahrenheit>27</fahrenheit>
          <celsius>-2</celsius>
     </temperatures>
     <conditions>FLURRIES</conditions>
     <dewpoint>18</dewpoint>
     <relative_humidity>69</relative_humidity>
     <wind>W28G33</wind>
     <wind_english>West 28 MPH Gusts Reaching 33 MPH </wind_english>
     <barometric_pressure>29.88R</barometric_pressure>
     <notes></notes>
     <wind_chill_fahrenheit>11</wind_chill_fahrenheit>
     <heat_index_fahrenheit>Not Applicable</heat_index_fahrenheit>
     <image>http://www.srh.noaa.gov/weather/images/fcicons/na.gif</image>
     <coordinates>
          <latitude>N/A</latitude>
          <longitude>N/A</longitude>
     </coordinates>
     <last_updated>February 22, 2009 18:19:18 GMT</last_updated>
     <last_updated_seconds_gmt>1235344758</last_updated_seconds_gmt>
     <info_courtesy_of>The National Weather Service</info_courtesy_of>
     <source_url>http://weather.noaa.gov/pub/data/raw/as/asus43.kfgf.rwr.fgf.txt</source_url>
     <feed_problems_contact>xml@boygenius.com</feed_problems_contact>
     <xml_generation_script_author>Todd Finney, Boy Genius Incorporated</xml_generation_script_author>
</city>

Writing the Actionscript to import this XML and display it on the stage is a common assignment I give to my students. In this example we are just going to import the city, state and the image.

First lest define a Text Format Object to use with the weather city and state.

var newFormat:TextFormat = new TextFormat();
newFormat.size = 20;
newFormat.color = 0x666666;
newFormat.font = "Arial";

Next we need to define a URL Loader and an XML Object. We will load the XML using the URL Loader and then on the Load Complete Event we will put the content into an XML Object.

When the Loader Complete Event is triggered all the information about that event will go into the variable evt defined in the function definition. Inside that Event variable is the target of the event and in this case the data loaded. We need to put that data, which is just text at this point, and put it in an XML Object. This was Flash can do all sorts of XML type things.

var xmlLoader:URLLoader = new URLLoader();
var xmlData:XML = new XML();
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
xmlLoader.load(new URLRequest("http://weather.boygenius.com/feeds/ontario-toronto.xml"));
function LoadXML(evt:Event):void {
     xmlData=new XML(evt.target.data);
}

Inside the loadXML function we need to add code that will create a Text Field, use our Text Format Object defined earlier and display the city and state from the XML document. Add the following code to the loadXML function.

To get the city and state our of our XML Object we simply refer to the XML Object and then add a dot and the item we are looking for. So in this example we want xmlData.name and xmlData.state. This gets a little more complicated when there are multiple copies of items in an XML document. For example pulling information about one image out of an XML document that includes information about many images.

     var nameTextField:TextField = new TextField();
     nameTextField.text = String( xmlData.name ) + ", " + String( xmlData.state );
     nameTextField.selectable = false;
     nameTextField.width = 200;
     nameTextField.x = 10;
     nameTextField.y = 10;
     nameTextField.setTextFormat( newFormat );
     addChild(nameTextField);

And lastly we need to add more code to the loadXML function to load the weather image and add it to the stage.

     var imageLoader:Loader = new Loader();
     var imageRequest:URLRequest = new URLRequest(xmlData.image);
     imageLoader.load(imageRequest);
     imageLoader.x = 10;
     imageLoader.y = 100;
     addChild(imageLoader);

The current weather in Toronto Ontario Canada is:

The Flash plugin is required to view this object.

Download the Flash Weather XML File

For anyone who has had some experience with Flash and XML you will quickly learn about a cross-domain policy. For some reason Flash has added a security feature to Flash which prevents an SWF from loading content from another domain at run-time. There is lots of information about this on the Adobe web site. In theory this isn’t a bad idea; trying to prevent people from using other people’s content and their bandwidth is kind of noble. Except this security feature is so easy to get around they may as well have not built it in.

Our Weather example above will work fine when previewing the file in Flash. As soon as you put the file online Flash will refuse to load the XML from the Boy Genius site because they do not have a cross-domain policy. All we have to do is create a PHP file to grab the weather XML from Boy Genius and regurgitate it. Here is a three line PHP file that will regurgitate what ever XML file you tell it to.

<?php
header ("content-type: text/xml");
$file = file( $_GET['url'] );
echo implode( $file, chr(13) );
?>

Lets say for example we want to load the Toronto weather from the Boy Genius web site. All we need to do is call the above file, lets say it is called xmlCaller.php, and pass it a URL. The XML call in Flash would be:

xmlCaller.php?url=http://weather.boygenius.com/feeds/ontario-toronto.xml

Deconstruct neave.com part 2

February 21st, 2009

This post is the completion of deconstructing the web site neave.com. Part one of this deconstructing can be found here.

Now that we have individually built all the components we just need to put them all together into a web site. On your main timeline create the following layer and frame structure:

On the footer timeline create your footer Movie Clip and give it the name footer_mc. On the main layer put your content Movie Clip and name it content_mc. Inside yout content Movie Clip create the following layer and frame structure:

Download the Flash file without the Actionscript

On frame one of our actions layer on the stage enter the following Actionscript:

import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.events.Event;

stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, resizeHandler);

resizeHandler(null);

function resizeHandler(event:Event):void {
     var sw:Number=stage.stageWidth;
     var sh:Number=stage.stageHeight;
     footer_mc.y = sh - 50;
     footer_mc.x = ( sw - 600 ) / 2;
     content_mc.y = ( sh - 200 ) / 2;
     content_mc.x = ( sw - 600 ) / 2;
}

This code imports a few required classesand re-positions our site content based on the size of the Flash viewable area. This code is executed once at the begining and again when ever the stage is resized. This code, and most of teh code on this page, is discussed in more detail in the Deconstructing neave.com Part 1 post. Most of this code was origianly taken from a Creating Liquid GUIs article from the Adobe site.

Next click into the content_mc Movie Clip and on frame one of the actions layer goes the rest of our code. Lets start with importing a few required classes:

import gs.TweenMax;
import gs.easing.*;
import flash.display.Bitmap;
import flash.display.BitmapData;

The first two import commands are importing non Flash classes. You will need to have the TweenMax class in the same folder as your fla. I’ll mention this one more time….most of this code is broken down in more detail in the initial Deconstructing neave.com post.

Next we are going to create a Movie Clip that will play a 320 pixel by 100 pixel FLV file when a menu option triggers a roll over event. The videos we will be using are lines, colours, marsh and waves.

var firstVideo:MovieClip = new MovieClip();
video_mc.addChild(firstVideo);

Lets also set up everything we need to play a video so that all the rollover code will have to do is select the video to play.

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncErrorHandler);
function asyncErrorHandler(evt:AsyncErrorEvent):void {
     // Ignore errors
}
var vid:Video = new Video(320,100);
vid.attachNetStream(ns);
firstVideo.addChild(vid);

The video we play on roll over will need to fill whatever space is available. Lets fill 800 pixels of space using videos that are 320 pixels wide. These two value will be used to determine how may times to repeat the video. There is more information on this concept on the Videos Indefinately post.

var widthToFill:Number = new Number(800);
var videoWidth:Number = new Number(320);

We will also need an unknown number of Movie Clips, Bitmap Objects and Bitmap Data Objects; so each time we create another set of these items we are going to store them in three Arrays.

var videoArray:Array = new Array();
var bitmapArray:Array = new Array();
var bitmapDataArray:Array = new Array();

Now we’re all set….lets create enough Movie Clips and Bimap Objects to fill the space specificed above.

for( var i:int = 1; i < widthToFill / videoWidth; i ++ ) {
     videoArray[i] = new MovieClip();
     videoArray[i].x = i * 320;
     bitmapDataArray[i] = new BitmapData(320,100,false,0x000000);
     bitmapArray[i] = new Bitmap(bitmapDataArray[i]);
     if( i % 2 == 1 ){
          bitmapArray[i].scaleX = -1;
          bitmapArray[i].x = 320;
     }
     videoArray[i].addChild(bitmapArray[i]);
     video_mc.addChild(videoArray[i]);
}

The first line determins how many sets we are going to need and loops the excerpt of code accordingly, The next two commands create a new Movie Clip and positions it. The two after that create a new Bitmap Object and a Bitmap Data Object. The code in the if statement will flip and reposition every second Movie Clip so are videos look seamless. And finally we add our Bitmap Object to our Movie Clip and we add our Movie Clip to our container Movie Clip.

Now we just need to set up a function to update our Bitmap Data Objects on the Enter Frame Event.

function enterFrameHandler(evt:Event):void {
     for( var i:int = 1; i < widthToFill / videoWidth; i ++ ) {
          bitmapDataArray[i].draw(firstVideo);
     }
}

Finally! Our video roll overs are all set to go. Lets move on to adding the roll over code to each of our butons. First we need to give out container Movie Clip a staring blur and zero alpha.

TweenMax.to( video_mc, 0.1, {blurFilter:{blurX:20, blurY:20}, autoAlpha:0} );

This next excerpt is our actual roll over code. When our visitor rolls over our first option a number of things are happening. First we tween the vertial_mc, horizontal_mc and stripes_mc to the desired colours. Next we change the text to inform our visitor what sectino they have just rolloed over, in this example we choose “Neave Games”. Next we tween our mask)mc and stripes_mc to make sure the whole text is visible. In this case 395 is sufficient. There would be some more complex methods of figuring out this number, but for now we just guessed a few times until it looked right. And finally we play the desired video, activate our Enter Frame Functiuon from above and unblur and make visible the video.

option1_mc.buttonMode=true;
option1_mc.addEventListener( MouseEvent.ROLL_OVER, option1OverHandler );
function option1OverHandler( evt:MouseEvent ):void {
     TweenMax.to(vertical_mc, 3, {tint:0x631553});
     TweenMax.to(horizontal_mc, 3, {tint:0xff00cc});
     TweenMax.to(stripes_mc, 3, {tint:0xff00cc});
     title_tb.text = "Neave Games";
     TweenMax.to(mask_mc, 0.5, {x:395});
     TweenMax.to(stripes_mc, 0.5, {x:395});
     ns.play("flash_neave_lines.f4v");
     video_mc.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
     TweenMax.to( video_mc, 1, {blurFilter:{blurX:0, blurY:0}, autoAlpha:1} );
}

This next code is pretty much identical to the above roll over code with a diffent video to play, different colours and a different x position.

option2_mc.buttonMode=true;
option2_mc.addEventListener( MouseEvent.ROLL_OVER, option2OverHandler );
function option2OverHandler( evt:MouseEvent ):void {
     TweenMax.to(vertical_mc, 3, {tint:0x185e1d});
     TweenMax.to(horizontal_mc, 3, {tint:0x1ff82e});
     TweenMax.to(stripes_mc, 3, {tint:0x1ff82e});
     title_tb.text = "Neave Television";
     TweenMax.to(mask_mc, 0.5, {x:475});
     TweenMax.to(stripes_mc, 0.5, {x:475});
     ns.play("flash_neave_marsh.f4v");
     video_mc.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
     TweenMax.to( video_mc, 1, {blurFilter:{blurX:0, blurY:0}, autoAlpha:1} );
}

These last two sets of code can be duplicated for each option you want to have in your menu. On the other hand, on roll out we can reuse the same function for eavery button.

option1_mc.addEventListener( MouseEvent.ROLL_OUT, optionOutHandler );
option2_mc.addEventListener( MouseEvent.ROLL_OUT, optionOutHandler );
option3_mc.addEventListener( MouseEvent.ROLL_OUT, optionOutHandler );
option4_mc.addEventListener( MouseEvent.ROLL_OUT, optionOutHandler );
function optionOutHandler( evt:MouseEvent ):void {
     TweenMax.to(vertical_mc, 3, {tint:0x111111});
     TweenMax.to(horizontal_mc, 3, {tint:0x222222});
     TweenMax.to(stripes_mc, 3, {tint:0x222222});
     title_tb.text = "Neave.com";
     TweenMax.to(mask_mc, 0.5, {x:325});
     TweenMax.to(stripes_mc, 0.5, {x:325});
     ns.pause();
     video_mc.removeEventListener(Event.ENTER_FRAME,enterFrameHandler);
     TweenMax.to( video_mc, 1, {blurFilter:{blurX:20, blurY:20}, autoAlpha:0} );
}

And there we have it…something that resembles neave.com. Maybe not quite as nice but not bad for two three hour classes.

The Flash plugin is required to view this object.

Download the Complete Flash File

Video Indefinately

February 15th, 2009

While Deconstructing Neave we came accross a situation where we needed a video to fill a horizontal space with an unknow width. We are going to achieve this by taking a 320 x 100 pixel video and repeat it using Bitmap Objects until we hit the end of the stage.

If you need n FLV fileyou can download our waves video which was taken from Free Stock Footage. There is already a much more detailed post available on Flash Bitmap Ojects.

First we need to import the Flash Bitmap and Bitmap Data Object:

import flash.display.Bitmap;
import flash.display.BitmapData;

Next we need a Movie Clip that will be used to play the 320 by 100 pixel FLV.

var firstVideo:MovieClip = new MovieClip();
addChild(firstVideo);

We will define any numbers, like our video width and the width to fill, at the top of our code. This practice makes our code easy to resuse. For someone working with this code they do not have to understand the bulk of the code; they just change the settings at the top.

var widthToFill:Number = new Number(690);
var videoWidth:Number = new Number(320);

This code will require an unknow number of Movie Clips, Bitmap Objects and Mitmap DataObjects. We will use an array to store each of these.

var videoArray:Array = new Array();
var bitmapArray:Array = new Array();
var bitmapDataArray:Array = new Array();

The next loop will run once for each required video. It will create a new Movie Clip and add a new Bimap Object to that Movie Clip. For each second Movie Clip it will flip it using the scaleY property so that the videos line up seamlessly.

for( var i:int = 1; i < widthToFill / videoWidth; i ++ ) {
     videoArray[i] = new MovieClip();
     videoArray[i].x = i * 320;
     bitmapDataArray[i] = new BitmapData(320,100,false,0xffffff);
     bitmapArray[i] = new Bitmap(bitmapDataArray[i]);
     if( i % 2 == 1 ){
          bitmapArray[i].scaleX = -1;
          bitmapArray[i].x = 320;
     }
     videoArray[i].addChild(bitmapArray[i]);
     addChild(videoArray[i]);
}

On the Enter Frame Event this loop will copy the data from our first Movie Clip to each required Bitmap Data.

this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
function enterFrameHandler(evt:Event):void {
     for( var i:int = 1; i < widthToFill / videoWidth; i ++ ) {
          bitmapDataArray[i].draw(firstVideo);
     }
}

Now that all the Movie Clips, Bitmap Objects and Bitmap Data Objects are set up all we need to do is play the video:

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncErrorHandler);
function asyncErrorHandler(evt:AsyncErrorEvent):void {
     // Ignore errors
}
var vid:Video = new Video(320,100);
vid.attachNetStream(ns);
firstVideo.addChild(vid);
ns.play("flash_indef_waves.f4v");

And there we have it, a seamless repetition of a video. Click the video to replay it.

The Flash plugin is required to view this object.

Download the Indefinate Flash FLA

Creating a Video Reflection

February 15th, 2009

A lot of this post will use some of the concepts from the post about using the Flash Bitmap Object. We will combine this along with some gradient drawing code taken Learnola to play an FLV video with a reflection using 100% Actionscript. If you need a video you can download our trippy lines video which was taken from Free Stock Footage.

First we need to import a few objects:

import flash.display.Bitmap;
import flash.display.BitmapData;

Next we need to create two Movie Clips. The first on will be used to play an FLV and the second one will be used to hold the Bitmap Object and act as a reflection.

var firstVideo:MovieClip = new MovieClip();
firstVideo.x = 10;
firstVideo.y = 10;
addChild(firstVideo);
var secondVideo:MovieClip = new MovieClip();
secondVideo.x = 10;
secondVideo.y = 492;
addChild(secondVideo);

Next we are going to create a Bitmap Object and link it to a Bitmap Data Object. We are also going to set the scaleY property to -1 to flip the video vertically

var myBitmapData:BitmapData = new BitmapData(320,240,false,0xffffff);
var myBitmap:Bitmap = new Bitmap(myBitmapData);
myBitmap.scaleY = -1;
secondVideo.addChild(myBitmap);

Next we attach a function to the Enter Frame Event. This function, running 24 times a second, will copy the data from our first MovieClip to our Bitmap Data Object.

this.addEventListener(Event.ENTER_FRAME,drawUpdate);
function drawUpdate(evt:Event):void {
     myBitmapData.draw(firstVideo);
}

Then we play our video and add it to the first Movie Clip.

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncErrorHandler);
function asyncErrorHandler(evt:AsyncErrorEvent):void {
     // Ignore errors
}
var vid:Video = new Video();
vid.attachNetStream(ns);
firstVideo.addChild(vid);
ns.play("flash_bitmap_lines.f4v");

This next excerpt of code was taken from Learnola. It uses some of the Flash draing Classes to create a gradient.

var linGrad:String = GradientType.LINEAR;
var linMatrix:Matrix = new Matrix();
linMatrix.createGradientBox(320,30,1.57);
var linColors:Array = [0x000000, 0x000000];
var linAlphas:Array = [0.20, 0];
var linRatios:Array = [0, 255];
var lin:Sprite = new Sprite();
lin.graphics.beginGradientFill(linGrad, linColors, linAlphas, linRatios,linMatrix);
lin.graphics.drawRect(0, 0, 320, 30);
lin.x = 10;
lin.y = 251;
addChild(lin);

And finally we cache both the gradient and video as a bitmap. This allows us to use the gradient as a mask.

secondVideo.cacheAsBitmap = true;
lin.cacheAsBitmap = true;
secondVideo.mask = lin;

And there we have it…a 100% Actionscript reflection, click the video to play is again:

The Flash plugin is required to view this object.

Download the Reflection FLA