Welcome Guest | Signup | Login

Tutorials

 

Tutorials >> XML Image Gallery with Falling Pictures (Snow Effect)
DOWNLOAD SOURCE FILE FOR THIS TUTORIAL

Posted by webzo

The images will be loaded and fall randomly until the user clicks one of these images.

Then the image clicked will enlarge and center on the screen. If the user clicks on another image or outside the enlarged picture, this picture will go back to its original size and continue falling.

The first thing to do here is to load our XML file with the paths to the images; so create an XML file like this one:

Code: Select all
<xml>
<images>
<image1 title = "0.jpg" />
<image2 title = "1.jpg" />
<image3 title = "2.jpg" />
<image4 title = "3.jpg" />
</images>
</xml>

Put as many nodes as you want inside , the attribute "title" is the path to your images.
I have the images ready and saved the XML to "images.xml". Both XML and images need to be in the same folder of your flash file.

Open you flash file and set the size of the stage to 800x500, that's the size we will be using, then open the actions for the first frame in the stage and paste:

Code: Select all
//Import libraries for the BitmapData and Matrix objects
import flash.display.BitmapData;
import flash.geom.Matrix

//Create XML object
var image_xml = new XML();
//Set the ignoreWhite property to false
image_xml.ignoreWhite = true;
//Load the XML file "images.xml"
image_xml.load("images.xml")

//Array to store the paths to our images when we take them from the XML
var images_path = new Array();

//This is a variable we are going to use to keep track of how many images frames we have
frames = 0;
//Later we will write a code to know if all images loaded, if they did we set this variable to true
ready = false;

Now before we write the Event Handler for the XML to get the paths to the images we need to make our Frame MC, just create a new MC and draw a frame anyway you want it:

In my case the size of the rectangle inside the frame is "500x375"; yours can be different, but you will need to change some numbers I use later on this tutorial.

Drag an instance of this MC to the stage and name it "frame1", we are going to duplicate it for our images.

Add this code to the first frame:

Code: Select all
//MovieClipLoader objecto to load our images
MCloader = new MovieClipLoader();

//New object to create an Event Handler
listener = new Object();

//Add the Event Handler bellow to the MovieClipLoader
MCloader.addListener(listener);

//This event is called when the image finished loading
listener.onLoadInit = function(target)
{
//Create new BitmapData with the size "500x375" If you draw a different size, use that size
var bmd:BitmapData = new BitmapData(500,375);

//Get the _x and _y scale for the image
var xscale = 500/target._width
var yscale = 375/target._height

//New Matrix Object
var MT = new Matrix();

//Set the Matrix to scale using the values we got above
MT.scale(xscale, yscale)

//Draw the image that is loaded to our BitmapData using the Matrix
bmd.draw(target, MT);

//Loop the code below, 10 times to make 10 copies of this image
for(i = 0; i < 10; i++)
{
//duplicate MC
duplicateMovieClip(frame1, "copy" + _root.frames + i, _root.getNextHighestDepth());

//Use the variable "mc" to refer to the duplicate
mc = _root["copy" + _root.frames + i]
//Create a new MC inside the duplicate
mc.createEmptyMovieClip("pic", 10);
//Set the position of the new MC
mc.pic._x = -250
mc.pic._y = -187.5
//Attach the Bitmap to the MC
mc.pic.attachBitmap(bmd, 1);
}

//Remove the image that was loaded
target.removeMovieClip();

//Increase this variable by one to get unique names for
//the duplicates the next time this event is called
_root.frames++

//This variable controls whether the MCs can fall or not.
_root.ready = true;
}

Above we coded how the images are loaded, scaled and positioned inside the frames.
Now we are going to load the XML and pass the paths of the images to the MovieClipLoader object. Paste:

Code: Select all
image_xml.onLoad = function()
{
//Loop for each node inside the XML
for(j = 0; j < image_xml.firstChild.firstChild.childNodes.length; j++)
{
//Get paths from the attribute "title" of each node
_root.images_path[j] = image_xml.firstChild.firstChild.childNodes[j].attributes.title;

//Create empty MC
_root.createEmptyMovieClip("frames" + j, _root.getNextHighestDepth());

//Load the image to this MC using the MovieClipLoader Object
_root.MCloader.loadClip(_root.images_path[j], _root["frames" + j]);
}
}

The last thing for this part of the code is to get a random direction for every "frame" MC:

Code: Select all
//Random number between -1 and 1
var dir = Math.random() * 2 - 1;

//The code below will run if "dir" equals to 0 and will run until "dir" is different than 0
while(dir == 0)
{
//Get another random number until its different than 0
dir = Math.random() * 2 - 1;
}

Now we just need to code the MC, so open the actions for the "frame" MC's instance and paste:

Code: Select all
onClipEvent(load)
{
//Import libraries need for the animation
import mx.transitions.Tween;
import mx.transitions.easing.*;

//Create a function called "reset()" to reuse the code below when necessary
function reset()
{
//Random scale between 3 and 9
this._xscale = this._yscale = Math.random() * 6 + 3;

//Random alpha 50 to 100
this._alpha = Math.random() * 50 + 50;

//Rotation 0? to 360?
this._rotation = Math.random() * 360;

//Random position above the stage, between -200 and -100
this._y = Math.random() * 100 - 200;

//Random position for the _x coordinate
this._x = Math.random() * Stage.width;

//Get random _y speed, 2 to 7
yspeed = (Math.random() * 5 + 2);

//Random _x speed, 1 to 5. Speed will be negative if the variable "dir" is negative
xspeed = (Math.random() * 4 + 1) * _root.dir;

//Whether this image is selected or not
select = false;
}

//Call the function "reset()" for the first time
reset();
}

The MC won't fall yet, so paste this:

Code: Select all
onClipEvent(enterFrame)
{
//If the images are loaded and this MC is not selected
if ((!select) &amp;&amp; (_root.ready))
{
//Move on the _x coordinate
this._x += xspeed;

//Move on the _y coordinate
this._y += yspeed;
}

//If this MC left the screen in any direction, call the function reset()
if(this._y > Stage.height)
{
reset();
}

if(this._x < -50)
{
reset();
}

if(this._x > Stage.width + 50)
{
reset();
}
}

Now the images fall, but we still need them to animate when the user clicks. Paste:

Code: Select all
//when the user click the mouse button
onClipEvent(mouseDown)
{
//if the mouse is over this MC and it's not selected already
if ((this.hitTest(_root._xmouse, _root._ymouse, true)) &amp;&amp; (!select))
{
//Bring the MC to the top of the others
this.swapDepths(_root.getNextHighestDepth());

//This MC is now selected so set the variable to true
select = true;

//Create a new Tween object for every property we need to animate
xTw = new Tween(this, "_x", Regular.easeIn, this._x, 400, 2, true);
yTw = new Tween(this, "_y", Regular.easeIn, this._y, 250, 2, true);
xSTw = new Tween(this, "_xscale", Regular.easeIn, this._xscale, 100, 2, true);
ySTw = new Tween(this, "_yscale", Regular.easeIn, this._yscale, 100, 2, true);

alphaTw = new Tween(this, "_alpha", Regular.easeIn, this._alpha, 100, 2, true);

rotTw = new Tween(this, "_rotation", Regular.easeIn, this._rotation, 0, 1, true);
}

//If this MC is selected, but the mouse was not clicked over it
else if ((select) &amp;&amp; (!(this.hitTest(_root._xmouse, _root._ymouse, true))))
{
//This MC is not selected
select = false;

//Call the function "yoyo()" of the Tween objects to
//make the MC go back to its original properties
xTw.yoyo();
yTw.yoyo();
xSTw.yoyo();
ySTw.yoyo();
alphaTw.yoyo();
rotTw.yoyo();
}
}

Test your code, everything should work now.

Code Explanation:


Code: Select all
import flash.display.BitmapData;
import flash.geom.Matrix;

These two line import the libraries BitmapData and Matrix; the BitmapData is used to save the loaded image to be reused in the copies and the Matrix is used to scale the image to fit the frame.

Code: Select all
var image_xml = new XML();
image_xml.ignoreWhite = true;
image_xml.load("images.xml");

Load the XML to get the paths.

Code: Select all
var images_path = new Array();


This variable is used to store the paths from the XML.

Code: Select all
frames = 0;
ready = false;

The first variable is used to know how many frame we have and to give unique names to the copies when the images are loaded, the second variable controls whether the MCs can fall or not.

Code: Select all
MCloader = new MovieClipLoader();
listener = new Object();
MCloader.addListener(listener);

A MovieClipLoader allows us to load images to an MC just like the function "loadMovie()" would, but with the MCL we can know exactly when it's done loading.
But we need to create the Event Handler ourselves; to do that we create a new Object, the handler (below) and we add this object as a listener to the MCL.
The function "addListener()" takes only one parameter, the object with the event handler.

Code: Select all
listener.onLoadInit = function(target)
{

This is the Event Handler for when the "target" is done loading. The parameter target is the object to where we are loading.

Code: Select all
var bmd:BitmapData = new BitmapData(500,375);

Create a new BitmapData with the size you used inside you frame, in my case the size is "500x375".

Code: Select all
var xscale = 500/target._width;
var yscale = 375/target._height;

The image we are loading is probably bigger than the size we want so we need to scale it and we need to know the scale.
We just divide the width and height we want by the width and height of the target (the image). The result will be used in the Matrix object so that 1 equals 100%, 0.5 equals 50%, etc.

Code: Select all
var MT = new Matrix();

The Matrix object can be used as a parameter in the draw() function of the BitmapData, we need to use it to scale the image.

Code: Select all
MT.scale(xscale, yscale);

Set the Matrix to scale using the values we got previously.

Code: Select all
bmd.draw(target, MT);

Draw the Bitmap with the target and the properties of the Matrix.

Code: Select all
for(i = 0; i < 10; i++)
{

Loop ten times to make ten copies of the same image.

Code: Select all
duplicateMovieClip(frame1, "copy" + _root.frames + i, _root.getNextHighestDepth());

Duplicate the MC that is on the stage.

Code: Select all
mc = _root["copy" + _root.frames + i]

Assign the new MC to the variable "mc" to call it like that now.

Code: Select all
mc.createEmptyMovieClip("pic", 10);

Create a new MC called "pic" inside the duplicate we just made.

Code: Select all
mc.pic._x = -250;
mc.pic._y = -187.5;

Set the position of the "pic" MC; this MC is where we are going to attach the Bitmap so its position needs to be where you want the picture to start.

Code: Select all
mc.pic.attachBitmap(bmd, 1);

Attach the Bitmap with the picture to the "pic" MC with the depth of 1.

Code: Select all
}
target.removeMovieClip();

Remove the image that was loaded as we won't need it anymore.

Code: Select all
_root.frames++;
_root.ready = true;
}

Increase "frames" by one and let the MCs fall.

Code: Select all
listener.onLoadInit = function(target)
{
onClipEvent(load)
{
import mx.transitions.Tween;
import mx.transitions.easing.*;

The Tween library is needed for the animation, and the easing for the movement of the animation.

Code: Select all
function reset()
{

We created a function for this because we will reuse the code inside when the MC needs new properties.

Code: Select all
this._xscale = this._yscale = Math.random() * 6 + 3;
this._alpha = Math.random() * 50 + 50;
this._rotation = Math.random() * 360;

Get random values for the scale, alpha and rotation of the MC.

Code: Select all
this._y = Math.random() * 100 - 200;
this._x = Math.random() * Stage.width;

Get a random position for the MC.

Code: Select all
yspeed = (Math.random() * 5 + 2);
xspeed = (Math.random() * 4 + 1) * _root.dir;

select = false;
}

Get random speeds, the variable "dir" determinates whether the MC will go left or right.
Set the variable "select" to false.

Code: Select all
reset();
}

Call the function "reset()".

Code: Select all
onClipEvent(enterFrame)
{
if ((!select) &amp;&amp; (_root.ready))
{
this._x += xspeed;
this._y += yspeed;

Move the MC if it's not selected and the images are loaded.

Code: Select all
}

if(this._y > Stage.height)
{
reset();
}
if(this._x < -50)
{
reset();
}
if(this._x > Stage.width + 50)
{
reset();
}
}

If the MC leaves the screen in any direction we call "reset()" to make the MC fall again.

Code: Select all
onClipEvent(mouseDown)
{
if ((this.hitTest(_root._xmouse, _root._ymouse, true)) &amp;&amp; (!select))
{

If the MC is not selected and the mouse is over it when this event happens we run the code below.

Code: Select all
this.swapDepths(_root.getNextHighestDepth());

Bring this MC to the top so we don't have the smaller ones in front of it.

Code: Select all
select = true;

Now the MC is selected so set this variable to true.

Code: Select all
xTw = new Tween(this, "_x", Regular.easeIn, this._x, 400, 2, true);
yTw = new Tween(this, "_y", Regular.easeIn, this._y, 250, 2, true);
xSTw = new Tween(this, "_xscale", Regular.easeIn, this._xscale, 100, 2, true);
ySTw = new Tween(this, "_yscale", Regular.easeIn, this._yscale, 100, 2, true);
alphaTw = new Tween(this, "_alpha", Regular.easeIn, this._alpha, 100, 2, true);
rotTw = new Tween(this, "_rotation", Regular.easeIn, this._rotation, 0, 1, true);

Create Tween objects for each property we need to animate: _x, _y, _xscale, _yscale, _alpha and _rotation.
Parameters for the Tween object:

Code: Select all
Tween(object, "property", easing effect, start value, end value, second or frames, true=seconds| false=frames);

Code: Select all
} else if ((select) &amp;&amp; (!(this.hitTest(_root._xmouse, _root._ymouse, true)))) {

Run this code instead if the MC is selected and the mouse is not clicking on it.

Code: Select all
select = false;

The MC is not selected anymore.

Code: Select all
xTw.yoyo();
yTw.yoyo();
xSTw.yoyo();
ySTw.yoyo();
alphaTw.yoyo();
rotTw.yoyo();
}
}

Call the function "yoyo()" of each Tween object to return this MC to its original properties.