Making the ultimate dynamic image gallery in Flash 8 – part 2 of 2
On this page, you will understand how the dynamic XML ActionScript image gallery works.
12. The ActionScript code behind the image gallery explained
I will explain you the ActionScript programming code by section, for easier understanding. The code consists of the following parts:
- Defining variables and positioning the main gallery elements
- Importing the DropShadow filter
- Creation of the MovieClipLoader and Listener objects and the functions that enable preloading
- Loading XML
- Parsing XML
- Retrieving gallery data (titles and descriptions)
- Creating menu section buttons dynamically
- Defining the functionality of the menu section buttons
- Enabling the display of the roll over and roll out states
- Defining which thumbnails shall be loaded
- Thumbnail positioning and thumbnail preloading
- Defining the functionality of the thumbnails
- Big image preloading
- Disabling the thumbnails while the big image is preloading
- Selecting the right description for the chosen image
- Making the big image clickable
- Fading in the loaded big image
- Creating the scrolling functionality for the gallery sections menu
Let’s start!
12.1 Defining variables and positioning the main gallery elements
As you can see, some of the variables here are created to serve as shortcuts to various movie clips, buttons and text fields:
var menuDown:Button = menuDown_btn;
var menuUp:Button = menuUp_btn;
var menuButtons:MovieClip = galleryMenu_mc.buttonsHolder_mc;
var galleryMask:MovieClip = galleryMenu_mc.galleryMask_mc;
var imagesHolder:MovieClip = imagesHolder_mc;
var descText:TextField = desc_txt;
This is done in order to reference them more easily and to do it a lot more faster. For example, imagine if you wanted to point to the menuDown_btn
button from a galleryMask_mc
movie clip’s event handler. Without the shortcut, you would have to do it like this:
this._parent._parent.menuDown_btn
But when you have defined the variables as you did above, so that they serve as references to various objects in your SWF movie, all you have to do is write them and Flash instantly knows what movie clip or button you’re talking about, no matter where you reference it. So, instead of writing the above path, all you do is write menuDown
and that’s it!
And of course, when you are referencing a Movie clip symbol, you have to define the variable which serves as its shortcut as a Movie clip type of variable:
var imagesHolder:MovieClip = imagesHolder_mc;
In a similar way, variables that reference buttons must of the Button
type, and of the TextField
type for text fields.
Next, the speed at which the menu will scroll is defined, in the variable menuSpeed
. This value will be used later, when the scrolling of the menu is going to be defined.
var menuSpeed:Number = 6;
menuUp._alpha = 0;
menuUp.enabled = false;
As you can see above, the menuUp
button, which is used to scroll the menu upwards is first made invisible by turning its _alpha
(transparency) property to 0 (zero) and then disabled by setting its enabled
property to false
.
Why is this done? To simply turn off and make invisible the button, not to confuse your users. I have done a little bit of testing and people told me that they don’t know which button to press in the beginning. Since the menu can only be scrolled downwards at first, it makes sense to remove the menuUp
button. This also removes any confusion. So, later you’ll see the explanation for the code that makes the button in question appear, but only once a user has clicked on the menuDown
button.
You must always think about your users and make your websites as user-friendly as possible. This can be tricky with Flash, as it offers far more possibilities than plain old HTML, where navigation is reduced to textual or image links. Flash offers basically infinite possibilities to create rich, interactive experiences, and that’s why you must be careful: Don’t let yourself be seduced by the vast array of eye-candy gizmos that can be made — always keep the user in mind! Have someone try out your Flash project before you launch it: even just a little bit of testing with real users (NOT web designers and such, who are web and computer-savvy) can go a long way towards improving the user experience. I always test my projects before going live. I suggest you do the same and make this a habit.
The variable firstLook is of the Boolean type, which means that its value can equal only true
or false
. This variable will help Flash determine if the user is using the gallery section menu for the first time. You’ll see it later: If the user is indeed using the menu for the first time — by pressing the menuDown button, the menuUp button will appear and become enabled (clickable). After that, the firstLook variable’s value will be set to false
, because once the button for scrolling the menu up appears, there is no need and sense to do it again.
var firstLook:Boolean = true;
After that, the height of the galleryMask movie clip is set via the _height
property of the Movie clip class. This value is expressed in pixels. I have set it to 391 pixels, because I have found that this value is fine for showing up a certain number of gallery section buttons. It just looked good to me, after some experimentation.
galleryMask._height = 391;
See now why it’s fine to make a movie clip out of a mask? You can easily readjust its dimensions via ActionScript — there is no need for you to go back inside the gallery menu movie clip each time that you want to change it, select the mask, resize it manually, etc. With ActionScript, the change is done in a snap!
Now come four variables of the Array type. An array is like a big variable: it can store many values, be those values of the Number type, String (text) type or something else. Think of them as of filing cabinets, where each drawer contains a value. They are perfect for storing many values, which will be used later at different points in your code.
var imagesInGallery:Array = new Array();
var galleryNames:Array = new Array();
var galleryIntros:Array = new Array();
var descriptions:Array = new Array();
They serve to contain the following values:
- The imagesInGallery array will store the information on the number of images in each gallery section.
- The galleryNames array will keep the names (titles) of the sections of your image gallery. If you recall, these are the names of the folders of different gallery sections, which are exactly the same as the values of the title attributes of gallery nodes inside your XML file.
- The galleryIntros array will store the descriptions for each gallery section. These are the values of the intro attributes belonging to each gallery node in your XML file.
- The descriptions array will store inside itself the textual description for each image in your gallery, which is going to be pulled out from the XML file too.
All of these arrays will be populated (filled with data) during the XML parsing process, which is explained later.
The tracker variable is of the Number type (which means it stores numerical values) and it will serve to keep track of the number of thumbnails when user has clicked on a gallery section and will also serve to place the thumbnails correctly and evenly into rows.
var tracker:Number = new Number();
The whatIsLoading variable is of the String type (meaning it will contain text values). This variable’s value will serve to tell Flash if the image that is being loaded is a thumbnail or a big image and to react appropriately in each case.
var whatIsLoading:String = new String();
The variables that follow serve to position different elements of your gallery on the stage:
var galleryBtnLeftMargin:Number = 10;
var galleryBtnUpperMargin:Number = 60;
var galleryBtnVSpace:Number = 23;
var thumbMarginX:Number = 96;
var thumbMarginY:Number = 68;
imagesHolder._x = 243;
imagesHolder._y = galleryBtnUpperMargin;
logo_mc._x = logo_mc._y = galleryBtnLeftMargin;
The galleryBtnLeftMargin and galleryBtnUpperMargin variables hold the values that serve to position the imagesHolder movie clip (which will contain all the thumbnails and big images, once they load) and also to position the logo. The variable called galleryBtnVSpace determines the vertical space between each of the section buttons inside the gallery menu. If you remember, the height of this button was made to be 20 pixels, so adding three more pixels should be just fine. The thumbMarginX and thumbMarginY variables’ values determine the spacing between thumbnails of each gallery section.
Next comes the positioning of the big dynamic text field, referred to as desc_txt…
desc_txt._x = 243;
desc_txt._y = 400;
…or using the shortcut (see above), descText. Below is the text that will appear when the gallery loads and there hasn’t been any interaction with it yet from the user. You must give your users some guidelines for them to be able to use your image gallery more easily.
descText.text = "Click on a gallery name on the left to load its thumbnails. Remember, you can click on a thumbnail only when all the thumbnails in a gallery have been loaded. When you click on a thumbnail to see the big image, clicking on the big image will close it and you will return to the gallery. Use the button(s) above the galleries to scroll through them.";
Of course, you could have devised a graphical element to do this: for example, to give a hint that the big image will close once clicked upon, a “close” button with a small x inside it could be made to show up. There are always many ways to improve your user’s experience. Choose what works best and what you think goes well with your gallery’s overall design.
12.2 Importing the DropShadow filter
I decided to apply the DropShadow filter to the thumbnails to make them even nicer. The first thing that you must do before applying a filter to an object inside Flash is to import it. When you do this, Flash reads from your disk (specifically, from a folder within the Flash installation) and inserts all the needed code for a particular filter into your SWF movie. Then it can be used and applied, once the importation has been made.
import flash.filters.DropShadowFilter;
After that, a variable of the DropShadowFilter
type is created.
var shadowEffect:DropShadowFilter = new DropShadowFilter(3, 45, 0x000000, 100, 3, 3, 1, 3);
The values between the parenthesis are as follows: distance, angle, color, alpha, blurX, blurY, strength and quality. They are pretty much self-explanatory. After that, you add the shadowEffect variable which contains the filter to the thumbsFilter array, which will then be applied to the thumbnails.
var thumbsFilter:Array = [shadowEffect];
For a more detailed explanation of how a filter in Flash works and why you need to create an array that will contain it, check out my tutorial on the use of the blur filter in Flash.
12.3 Creation of the MovieClipLoader and Listener objects and the functions that enable preloading
If you wish to preload external JPEG images into Flash across all browsers, the best method available is the one that uses the MovieClipLoader object. This particular object is specifically made for external content loading. It is great and you will use it to load and preload both the thumbnails and the big images! Here’s how it works:
- The first thing that you must do is create an instance of the MovieClipLoader object. I chose to call the variable which will contain it
loader
.
var loader:MovieClipLoader = new MovieClipLoader();
- Then, you need to make a listener object, which can be a movie clip or an Object object (a generic object, let’s say so). It will listen to what the MovieClipLoader object is doing and will react when things start happening: for example, when the loading is started by the MovieClipLoader or is in progress, the listener is aware of that and reacts by running specific functions (which will, for example, tell Flash how much has been loaded, etc). I called the listener object
myListener
.
var myListener:Object = new Object();
- And to have the MovieClipLoader and the listener work together nicely, you must associate the listener object to the MovieClipLoader, like this, using the
addListener
method:
loader.addListener(myListener);
Fine. But, before any loading/preloading is going to be done, you must first and foremost write two important functions:
- One that will tell Flash what to do while an image is being loaded (called
onLoadProgress
), - And the other that will tell it what to do once the loading is complete (called
onLoadInit
).
I repeat, these functions must be created before any loading begins. Flash must know what to do in advance! On a sideline note, this method of preloading is also explained extensively in my menu preloader system tutorial. Let me show you now how have I set up the listener object’s onLoadInit()
function for the gallery.
myListener.onLoadInit = function(target:MovieClip) {
if (whatIsLoading == "thumb") {
currentThumbnail.percent_txt._visible = false;
currentThumbnail.filters = thumbsFilter;
thumbClickable();
tracker++;
if (tracker
loadThumbnail();
} else {
enableThumbs();
}
} else if (whatIsLoading == "big") {
target._alpha = 0;
displayBigImage.percent_txt._visible = false;
displayBigImage.filters = thumbsFilter;
bigClickable();
fadeIn();
}
};
Flash will automatically execute the onLoadInit()
function once the external JPG image (or a SWF, or a GIF…) has been entirely downloaded to the user’s hard disk.
First, the function is defined with a parameter (the thing between the function’s parenthesis): the target variable which is a Movie clip. This is the empty movie clip into which the external image will be loaded.
myListener.onLoadInit = function(target:MovieClip) {
Inside the function (meaning between its curly braces: {
and }
), the first thing that shows up is an if/else if
conditional statement:
if (whatIsLoading == "thumb") {
currentThumbnail.percent_txt._visible = false;
currentThumbnail.filters = thumbsFilter;
thumbClickable();
tracker++;
if (tracker
loadThumbnail();
} else {
enableThumbs();
}
} else if (whatIsLoading == "big") {
target._alpha = 0;
displayBigImage.percent_txt._visible = false;
displayBigImage.filters = thumbsFilter;
bigClickable();
fadeIn();
}
As you can see, this conditional statement looks at the value of the whatIsLoading variable to decide what to do next. This value is of the string type (meaning it’s text) and can equal either “thumb” or “big”. These two values stand for thumbnail and big image. I defined them as such because it’s logical and I can instantly see what do they refer to.
As I said, this function is defined before the actual loading starts. So, as you will see later, once the loading of an external JPEG image has been triggered, the value of the whatIsLoading variable will be set to either “thumb” or “big”. It is set to “thumb” when the user clicks on a gallery section button — when the thumbnails of that section should be loaded and displayed. It is set to “big” when the user has clicked on a thumbnail to see the big image.
So, what happens when the value is set to “thumb”? The following:
currentThumbnail.percent_txt._visible = false;
currentThumbnail.filters = thumbsFilter;
thumbClickable();
tracker++;
if (tracker < howManyImages) {
loadThumbnail();
} else {
enableThumbs();
}
Here is the above code, broken down line by line:
- The dynamic text field inside the thumbnail holder movie clip of the currently loaded thumbnail is hidden by setting its
_visible
property tofalse
(remember, this is the text field that you created earlier in this tutorial, where the percentage preloader will show up during preloading). - The drop shadow effect is applied to the thumbnail holder via the movie clip’s
filters
property. - The
thumbClickable()
function is invoked (meaning it is called), which will define what will happen when the user clicks on a thumbnail: she or he will begin the loading of its bigger counterpart (the big image). This function comes later in the code, you’ll see it explained too. - The
tracker
variable is incremented by one (1 is added to its current value) — that’s what the two plus signs (++) stand for. Remember, this variable is used to let Flash know which thumbnail is currently being loaded/has been loaded, where it should be placed, etc. - Then, an
if/else
conditional statement shows up, which does the following: - if the value of tracker variable is lesser than the value of howManyImages variable, the
loadThumbnail()
function will be invoked and the next thumbnail will be loaded. - If that condition isn’t true (when
tracker
‘s value is equal to or bigger thanhowManyImages
‘ value), then the thumbnails will be enabled. This means that they will be clickable.
The above means the following: tracker
keeps track of the number of thumbnails in the current gallery section (the one that the user chose to see). The variable howManyImages
holds the number of images that exist in the chosen gallery section. So, if the current thumbnail isn’t the last one — meaning if there are more to be loaded — more will be loaded.
And when all the thumbnails have been loaded, they are all enabled. This means that the user isn’t able to click on any of the thumbnails until all of them have been completely loaded. I made this to be so because I like it like that and also because I don’t have to deal with additional interaction stuff which I’d have to, if I hadn’t done this.
And now, what happens when the value of the whatIsLoading variable has been set to “big”? A slightly different thing:
target._alpha = 0;
displayBigImage.percent_txt._visible = false;
displayBigImage.filters = thumbsFilter;
bigClickable();
fadeIn();
Here’s the breakdown of the above code:
- The
_alpha
property (transparency) of thetarget
movie clip (the one into which the big image will be loaded) is set to zero, meaning that it will become invisible. I have chosen to do this to be able to apply the nice fade-in effect to the big image later :). Why make a simple instant appearance when you can create a cool fade-in effect? - Then, as was the case with the thumbnails, the dynamic text field of the big image holder is made invisible (
_visible = false
), because the percentage preloader needs to disappear once the big image has been fully loaded. - The drop shadow filter is applied to the big image, as it was for the thumbnails.
- The big image is made clickable by calling the
bigClickable()
function, which you’ll see later. This makes possible for the user to click the big image, and once that has been done, the image will be removed and the thumbnails will be visible again. - And finally, the
fadeIn()
function is invoked to smoothly fade in the big image, from invisibility to complete opaqueness.
Cool! What comes next is the onLoadProgress()
function which monitors the downloading of the image(s) to the user’s computer. This function is used to create the preloaders.
myListener.onLoadProgress = function(target:MovieClip, loaded:Number, total:Number) {
percent = Math.floor(loaded/total*100);
if (whatIsLoading == "thumb") {
currentThumbnail.percent_txt._visible = true;
currentThumbnail.percent_txt.text = percent+"%";
} else if (whatIsLoading == "big") {
displayBigImage.percent_txt._visible = true;
displayBigImage.percent_txt.text = percent+"%";
}
};
This function comes with three parameters:
- target — the empty movie clip which into which the external JPEG is loaded,
- loaded — how much of the image has been loaded so far, in bytes and
- total — the total file size of the image that is being loaded.
Inside the function, the first line servers to calculate the percentage of the image that has been loaded so far (loaded/total*100
) which is then rounded to the nearest lower value with the floor()
method of the Math
object.
percent = Math.floor(loaded/total*100);
Next comes a conditional if/else if
statement, which, much like in the previous function, determines if the value of the whatIsLoading
variable equals either “thumb” or “big”. Then, whether it is a thumbnail that is being loaded or a big image, what goes on is basically the same: the percent_txt dynamic text field inside the image holder becomes visible and inside it the information on how much of the image has been loaded so far is displayed, using the percent variable followed by the % character.
For the thumbnail:
currentThumbnail.percent_txt._visible = true;
currentThumbnail.percent_txt.text = percent+"%";
…as well as for the big image:
displayBigImage.percent_txt._visible = true;
displayBigImage.percent_txt.text = percent+"%";
The currentThumbnail
and displayBigImage
variables are defined later, you’ll see. There is a lot going on here in those two functions that hasn’t appeared or been defined in your code yet, but that is, I repeat, because these functions, which are connected to the MovieClipLoader object and its associated listener object have to be defined before you actually proceed to load the external JPEG images.
Great! Let’s move on to the XML loading part now.
12.4 Loading XML
To load XML data from an external XML file into Flash, you must do the following:
- Create a new XML object.
- Create an
onLoad()
function which will tell Flash what to do after loading has been finished, whether it was performed successfully or not. - Tell Flash to load the XML data from the external XML file.
Here is the code that does exactly that:
var imageGallery:XML = new XML();
imageGallery.ignoreWhite = true;
imageGallery.onLoad = function(success) {
if (success) {
parseGalleries();
} else {
descText.text = "Sorry the image data just didn’t load.";
}
};
imageGallery.load(“gallery/gallery.xml”);
The first line creates a new XML object. I chose to call it imageGallery
.
var imageGallery:XML = new XML();
The second one tells Flash to ignore the white space between the chunks of data inside the XML file:
imageGallery.ignoreWhite = true;
This must be done, yes, because blank spaces in an XML file are considered by Flash as data, too. That’s why you must tell Flash to skip it because otherwise you’ll have a mess to deal with.
Now comes the onLoad()
function of your XML object. This function (similarly as the ones used for preloading with the MovieClipLoader and its listener) must be defined before the actual XML loading takes place. Flash must know in advance what to do once the XML data has been loaded.
imageGallery.onLoad = function(success) {
if (success) {
parseGalleries();
} else {
descText.text = "Sorry the image data just didn’t load.";
}
};
As you can see above, the onLoad()
function has one parameter: success
. This is a parameter that gets automatically passed to this function upon XML data loading. It holds a Boolean type of value, which means that it equals either true or false. It yields as true if the XML data loading was performed successfully and false if it was not.
Inisde the function, there is a simple if/else
conditional logic used to help Flash decide what to do if the loading was successful or not. The line
if (success)
is the shorthand way of writing
if (success == true)
And the else
part, of course, is activated only when success
turns out to be false
. So, when XML data loading has been successful, the parseGalleries()
function is invoked (invoked means called in programmers’ jargon). I chose that name for the function, because it will literally parse the XML data to extract every bit of information that is needed for the creation of the image gallery.
parseGalleries();
If the loading of XML data fails, you have to inform your users about that.
descText.text = "Sorry the image data just didn’t load.";
The above text is what will appear in the big dynamic text field. Basically, I don’t know what else can be done if the loading process fails. The user can try to reload the page, but most likely he or she will just close the page with the image gallery and move. If you have created a good XML file and tested your SWF and seen that it works online with no problems, there is not much that you can do if now and then some user can’t see the gallery because the XML data failed to load. That is beyond your power: maybe the server where you host your image gallery went down for a moment or the user has a too strong firewall, router, etc. But it happens rarely. Most people should see your Flash image gallery with no problems at all.
And now, once you have defined what Flash should do upon loading the XML data, tell it to load it! LIke this:
imageGallery.load("gallery/gallery.xml");
Notice the path to the XML file: it is stored inside the gallery folder that you have created earlier in this tutorial, when you created the whole folder structure. This can be a source of problems too! If you change the folder inside which your XML file resides or the name of the XML file itself, you must change it in the above line of code too!
When loading external data, you must reference it in relation to the HTML file inside which your SWF file is embedded, NOT in relation to the SWF file itself, regardless where it is situated. The HTML page is what’s important!
In the next step, I will show you how the parsing of XML data is done.
12.5 Parsing XML: Retrieving gallery data (titles and descriptions) and creating the menu section buttons dynamically
Below is the parseGalleries()
function which serves to parse the XML data and store it in various variables. It looks big… scary… doesn’t it? 🙂 No, it is not! It is just a little bit long, but everything inside it is perfectly logical and understandable and I will explain it to you in no time at all! So let’s get on with it.
function parseGalleries():Void {
if (imageGallery.firstChild.nodeName == "galleries") {
var rootNode:XMLNode = imageGallery.firstChild;
for (i=0; i
if (rootNode.childNodes[i].nodeName == "gallery") {
currentGallery = rootNode.childNodes[i];
imagesInGallery.push(currentGallery.childNodes.length);
galleryNames.push(currentGallery.attributes.title);
galleryIntros.push(currentGallery.attributes.intro);
currentGalleryTitle = rootNode.childNodes[i].attributes.title;
currentGalleryButton = galleryMenu_mc.buttonsHolder_mc.attachMovie("gallery section button", "galleryButton"+i, galleryMenu_mc.buttonsHolder_mc.getNextHighestDepth());
currentGalleryButton._x = 0;
currentGalleryButton._y = galleryBtnVSpace*i;
currentGalleryButton.sectionTitle_txt.text = "0"+(i+1)+" "+currentGalleryTitle.toUpperCase();
for (j=0; j
if (currentGallery.childNodes[j].nodeName == "image") {
currentDescription = currentGallery.childNodes[j].firstChild.toString();
descriptions.push(currentDescription);
}
}
}
}
}
numberOfGalleries = i;
enableButtons(numberOfGalleries);
}
As you probably noticed, there is a semicolon (:
) followed by the ActionScript keyword Void
in the first line of function’s code.
function parseGalleries():Void {
This keyword tells Flash that the parseGalleries()
function does not return a value. It is good to specify this, although it isn’t obligatory. When you have a function that does return a value to the caller, you have to specify this. For example, you may have a function which serves to perform a simple mathematical calculation and so instead of Void
, you would write Number
after the function’s name and the semicolon, because the function would return a numerical value.
Inside the function, almost all of the code (except two lines) is contained within an if
conditional statement:
if (imageGallery.firstChild.nodeName == "galleries") {
Notice that I said an if
conditional statement, not an if/else
one. Why? Because there is no “else” situation here. The if
conditional is used here as to say to Flash “When you find the root node with the name galleries
, do the following”. Also, there is no else
, because Flash will find the node named galleries
because you created it in the XML file. This is a fail-safe procedure! 🙂
Remember, an XML file has only one root node. So when you say imageGallery.firstChild.nodeName
, you tell Flash to look up the name of the root node — it is the first child of the XML file (which was converted to XML object when loaded into Flash) itself.
…
After that, a shortcut variable called rootNode
is created that points to this root node:
var rootNode:XMLNode = imageGallery.firstChild;
This is much like the variables that you have created at the very start of your ActionScript code to be able to reference movie clips and buttons in a shorthand and practical manner.
Next, you have a for
loop that is used to loop through all the child nodes of the root node.
for (i=0; i To be sure that Flash really loops through all the child nodes of the root node, you set the condition of the loop as follows: What follows is an if (rootNode.childNodes[i].nodeName == "gallery") { The variable The gallery that is currently being parsed is stored in the currentGallery = rootNode.childNodes[i]; This si done to be able to reference it more easily in the next few lines of code. Next, the construct imagesInGallery.push(currentGallery.childNodes.length); The Next, the value of the galleryNames.push(currentGallery.attributes.title); Using the same procedure as above, the value of the galleryIntros.push(currentGallery.attributes.intro); Next, by using the procedure that you understand well by now, the value of the currentGalleryTitle = rootNode.childNodes[i].attributes.title; And now, the gallery section button movie clip is being extracted from the Library dynamically and placed inside the gallery menu: currentGalleryButton = galleryMenu_mc.buttonsHolder_mc.attachMovie("gallery section button", "galleryButton"+i, galleryMenu_mc.buttonsHolder_mc.getNextHighestDepth()); The gallery section button that you have created earlier is being attached inside the gallery menu, more specifically to the buttonsHolder_mc empty movie clip inside it: And the currentGalleryButton._x = 0; The first line above serves to position each gallery section button horizontally, via the movie clip On the other side, the vertical position of each section button inside the gallery menu is determined dynamically, because you have to put some space between them: And now you must define the label for the current gallery section button: currentGalleryButton.sectionTitle_txt.text = "0"+(i+1)+" "+currentGalleryTitle.toUpperCase(); As you can see above, this label is composed of a few things, all were my choice. I could have just slapped the plain gallery section title over each button, but I wanted to add numbers and also put all the letters into capitals: And now comes the for (j=0; j Since this loop is contained within the main loop, you must naturally change the variable: it is Again, you have an Then, as each currentDescription = currentGallery.childNodes[j].firstChild.toString(); For example, the value of the first child node of the fourth And the text (which is the description of each particular image) is then stored inside the descriptions.push(currentDescription); Finally, after the big numberOfGalleries = i; This number will be used later to determine the number of gallery section buttons and loop through all of them. And finally, the enableButtons(numberOfGalleries); Whew! That was all about XML parsing and data extraction! Cool! Jump to the next step to see how the functionality of the gallery menu section buttons is created. 12.6 Defining the functionality of the menu section buttons: Enabling the display of the roll over and roll out states and defining which thumbnails shall be loaded Here’s the function enableButtons(numberOfGalleries:Number):Void { Almost all the code of the for (i=0; i Now you see how important the The first thing that needs to be done is to set up another shorcut: a variable which will point to each button in your gallery menu as the loop progresses: pressedButton = galleryMenu_mc.buttonsHolder_mc["galleryButton"+i]; Of course, Flash will process the expression between the angled brackets: Now comes the definition of the onRollOver event handler function, which gets executed when the user rolls her mouse over a button in the gallery section menu: pressedButton.onRollOver = function():Void { Basically, what happens is that the movie clip’s (which acts as a gallery section button) playhead gets sent to the second frame, where there is a different background color, to accentuate the rollover effect. After that, the rollOut event handler function is defined, which gets run once the mouse has been pulled out of the button area. This returns the movie clip’s playhead to the first frame, putting it back to its primary state. pressedButton.onRollOut = function():Void { In both event handler functions, the keyword The diagram below explains this functionality well. The gallery section button has a blue background in the first frame and a crimson one in the second frame, when the onRollOver event has been fired and its event handler function executed. And now, a very important event handler function comes up: pressedButton.onPress = function():Void { Any thumbnails or big images that may have been loaded before are removed with the removeMovieClip(thumbsDisplayer); Then, the tracker is reset to zero. tracker = 0; And the thumbsDisplayer = imagesHolder.createEmptyMovieClip("thumbsDisplayer_mc", imagesHolder.getNextHighestDepth()); There are two parameters used in the Now the clickedGallery = Number(this._name.substr(13)); The clickedGallery = Number(this._name.substr(13)); The first parameter of the The second parameter is the number of characters to be extracted (the length of the new, extracted string). And if it is omitted (like above), then all the characters from that point until the end of the string will be extracted. So it doesn’t matter if your button has the name galleryButton3, galleryButton42 or galleryButton128, the number will always get extracted properly, because the extraction starts at position 13 and gets any remaining characters in the string from that point on. After the extraction has been done, the result is converted to a mathematical value from a plain text character. And this final value is used three times after that: howManyImages = imagesInGallery[clickedGallery]; Specifically, It is used to: All three values are extracted from different arrays. In all three cases, this happens in the same manner, so I will explain it for the gallery title, for example. Let’s suppose again that the user clicked on the fourth (monochrome ) button, so the value of the whichGallery = galleryNames[clickedGallery]; Why so? Because the number between the angled brackets: Now you see how the values for the other two variables were extracted from their respective arrays, too. Next, the values of the variables which serve to place the thumbnails into rows of five are reset to zero: currentRow = 0; Then the loadThumbnail(); Once all of three event handler functions (onRollOver, onRollOut and onPress) for all the gallery section menu buttons have been properly defined, there is still one line of code (outside the enableGalleryNavigation(); This calls the Let me explain you now what happens when a gallery section button has been clicked and the thumbnails begin to load via the 12.7 Thumbnail positioning and thumbnail preloading So, the user has clicked on a gallery section to see its images. Here is the function that will attach the thumbnail holders, place them in an orderly fashion and trigger the preloading, so that the user can see that something is actually happening! function loadThumbnail() { The first action inside the function is the process of attaching a thumbnail holder movie clip from the Library, to the thumbsDisplayer empty movie clip. This is done with the currentThumbnail = thumbsDisplayer.attachMovie("thumbnail holder", "thumbnail"+(tracker+1), thumbsDisplayer.getNextHighestDepth()); The parameters of the method are as follows: And the newly attached movie clip is referenced by the Next, the target variable is defined, which will tell Flash where the external thumbnail will be loaded into. As you can see in the line of ActionScript code below, it is being loaded inside the target = currentThumbnail.thumbImage_mc; And now come the chunks of code which serve to position the thumbnails into rows, 5 thumbnails per row. First, the counting of rows is done. if ((tracker%5) == 0 && tracker != 0) { The important thing to be aware of here is that this line serves to place thumbnails in rows of 5, so the condition(s) will be fulfilled for thumbnail number 6, 11and 16 (when There are two conditions in the For example, what happens at the beginning, when (tracker%5) == 0 && tracker != 0 You don’t even have to look at the first condition in the example above, because the second condition is false. It says that zero does not equal ( OK, let’s see now what happens when (tracker%5) == 0 && tracker != 0 This time, it is the first condition that yields as false (it says that 3 equals 0, which is false) and so the whole thing is again false. And now, the case when the value of tracker reaches either 5, 10 or 15: (tracker%5) == 0 && tracker != 0 Both conditions are now true, because zero does equal zero and five does not equal zero. Here, the value of Ok, you need now to see the code that will ensure that the thumbnails are set apart from each other: if (currentColumn>3) { Translated into english, this if (the value of currentColumn is greater then 3 — meaning it is not 0, 1, 2 or 3) { And finally, the code that actually positions the thumbnails on stage based on what I just explained to you. currentThumbnail._x = currentColumn*thumbMarginX; The And now the dynamic text field inside the thumbnail holder is made visible: currentThumbnail.percent_txt._visible = true; Using the thumbNumber = currentThumbnail._name.substr(9); As I have told previously in this tutorial, the first parameter of the And the value of the thumbPath = "gallery/" + whichGallery + "/thumbs/" + thumbNumber + ".jpg"; The line above is using simple concatenation (the joining of two or more strings together) to create the path to the thumbnail. Again, remember well that this path is relative to the HTML page inside which your image gallery SWF is embedded. For example, if the third thumbnail of the essays gallery section were to be loaded, Flash would create the path like this: thumbPath = "gallery/" + whichGallery + "/thumbs/" + thumbNumber + ".jpg"; Subsequently, you have to tell Flash what is loading, the thumb or the big image: whatIsLoading = "thumb"; And finally, the loader.loadClip(thumbPath, target); The first parameter inside the parenthesis is the external file that is going to be loaded (a JPEG thumbnail in this case). The second parameter is the target empty movie clip into which the external file will be loaded. And now that the Now you know how the placement and preloading of thumbnails is done! What follows is the explanation of how the functionality of the thumbnails is defined. 12.8 Defining the functionality of the thumbnails I will show you now the following things that are part of the thumbnails’ functionality: function thumbClickable():Void { The first thing that can be noticed is that nearly all the code inside the currentThumbnail.onPress = function() { The first that you have to do is make Flash extract the number of the big image that is going to be preloaded once the user clicks on a thumbnail: bigNumber = this._name.substr(9); This is done in exactly the same manner as it was for the thumbnails (explained on the previous page), so I won’t repeat myself here. Next, the big image holder movie clip is pulled out of from the Library and attached to the imagesHolder empty movie clip using the displayBigImage = imagesHolder.attachMovie("big image holder", "bigImage_mc", imagesHolder.getNextHighestDepth()); Of course, once again, a variable is used as a shortcut to point to the newly attached movie clip: And so the empty movie clip (mentioned under no.2 above) is defined as the target for loading the external JPEG and will be passed as such to the MovieClipLoader later: target = displayBigImage.imageHolder_mc; The path to the big JPEG image is defined in a way similar to those of the thumbnails: bigImagePath = "gallery/"+whichGallery+"/"+bigNumber+".jpg"; The variables Just like for the thumbnails before, you have to tell Flash what you’re loading here too: whatIsLoading = "big"; And the thumbnails have to be disabled — this will stay so throughout the preloading process and also while the big image is being displayed. They will be re-enabled only once the user has clicked on the big image and it has disappeared, showing the thumbnails again. This is done through the disableThumbs(); And now the actual command to load the big image is issued to Flash: loader.loadClip(bigImagePath, target); The above is done using the The next chunk of code is the one where the description for the selected big image is being searched for, found and displayed in the big text field below the image: if (clickedGallery > 0) { The search for description is placed inside an First there is the var descPosition:Number = 0; Then, the for (i=0; i Every time a loop iteration is made, the value of It will be best explained by an example. In my gallery example, suppose the user has clicked on the sixth thumbnail in the third gallery section (it is circled in red below, while the gallery section is in rollover state, just for the purpose of showing it here). The text for this image should be “Empty silos.” Since the user has clicked the third gallery, the value of descPosition += imagesInGallery[i]; So the value of Ok, here comes the next loop iteration, when descPosition += imagesInGallery[i]; The value of And after that, the next line is executed, which updates the value of descPosition = descPosition+Number(bigNumber)-1; Still using the example above, the equation would be calculated like this: descPosition = descPosition+Number(bigNumber)-1; First, Then, one is substracted from it, since I chose to give the thumbnails (and subsequently, the big images too) the numbers starting from 1 and not 0. And there you go! The right description is really chosen: it is the 17th description in the imageDesc = descriptions[descPosition]; And now for the else part of the conditional statement: } else { It is used when the user has clicked on the first gallery section. No looping is necessary here, because this section’s descriptions are right at the beginning of the The description of the big image is subsequently displayed in the big dynamic text field below it: descText.text = imageDesc; That’s it for the thumbnail’s onPress event handler. The one line of code that is executed outside this event handler function, but inside the currentThumbnail.enabled = false; …which disables the thumbnail until all of them are loaded for the current gallery section. And here come now the two functions that disable and enable the clickability of the thumbnails. The first one, function disableThumbs():Void { How does it work? It uses a The function that restores the clickability of the thumbnails, function enableThumbs():Void { Allright, that’s it for the thumbnails. But what about the big image? It has to be made clickable, for the user to be able to close it once she or he wishes to return to the thumbnails. That’s what the 12.9 Making the big image clickable Inside the function bigClickable():Void { The line removeMovieClip(this); removes the big image with the After that , the Lastly, the description text is reverted to the one that describes the selected gallery section. descText.text = galleryIntros[clickedGallery]; OK! Let me show you now how that nice alpha fade-in effect was achieved. 12.10 Fading in the loaded big image Why go for a simple image appearance when there is eye-candy like the alpha fading in effect at your disposal? 🙂 function fadeIn():Void { This function consists of an onEnterFrame movie clip event handler. This particular movie clip event is used very often because it makes possible for actions to be performed repeatedly. To be more exact, every line of code contained inside an onEnterFrame event handler will be executed as many times per second as your movie speed is set to. So, if your movie speed is set to 30 fps, the code inside the onEnterFrame event handler will be run 30 times in a second. Ideal for a fade-in effect! What happens inside is that the this._alpha += 10; Remember that the target movie clip’s And once if (this._alpha >= 100) { The keyword And now, the gallery menu scrolling functionality with easing effect is explained. Yes, you are nearing the end of this tutorial :-)! 12.11 Creating the scrolling functionality for the gallery sections menu I have made an example just for the purpose of showing you how the menu will look like when there are plenty of sections inside it. The section buttons in the example below are not clickable, but the menu is perfectly scrollable. Click on the down button to try it and then the up button when it appears! The big function that follows, function enableGalleryNavigation():Void { Let's see how the functionality of the menuDown button is set up. To understand what is going on inside the function, keep in mind the following facts: menuDown.onPress = function() { The first thing inside the event handler function is an if (firstLook) { The condition So, the code between the curly braces, menuUp._alpha = 100; After that, the variable firstLook = false; Next, the menuTop variable is defined. Keep in mind that the menuButtons variable is the shortcut for the buttonsHolder_mc movie clip, which is itself situated inside the galleryMenu_mc movie clip. So, the menuButtons movie clip is the one into which all the gallery section buttons were attached from the Library. var menuTop:Number = menuButtons._height - Math.abs(menuButtons._y); The value of the Next comes an if (menuButtons._y <= 0 && menuTop >= galleryMask._height) { These two conditions ensure that: So, if these conditions have been met, the menu will be scrolled down, thanks again to an onEnterFrame event handler function and a few variables defined before it is executed. The variable that gets defined is the following: var targetPos:Number = menuButtons._y - galleryMask._height; The Next, both buttons that serve for scrolling the menu (menuDown and menuUp) are disabled just before the scrolling begins. You don't want your menu to be moved up and down without control — it is best to lock it until it has reached the targetPos coordinate. menuDown.enabled = false; And now comes the onEnterFrame event handler: menuButtons.onEnterFrame = function():Void { The code line that makes the menu move is the one that adds a value to the menuButtons' Y coordinate each time the onEnterFrame event is fired: menuButtons._y += (targetPos-menuButtons._y)/menuSpeed; Remember that the addition assignment operator ( And each time the menu approaches the target point, a check must be made to see if the point in question has actually been reached. This is done through an if conditional statement, of course. if (menuButtons._y <= (targetPos+0.8)) { The And, once that condition is fulfilled (meaning that the menu has reached the target position), its position is fixed in place by setting it definitely to the rounded menuButtons._y = Math.round(targetPos); Then, of course, you should delete the onEnterFrame event handler, because it has fulfilled its purpose and it is good to take load of the user's computer processor. delete menuButtons.onEnterFrame; After that, the buttons for scrolling the menu up and down are enabled again. menuDown.enabled = true; As for the onPress event handler function that serves to enable scrolling the menu up, the differences are only in the conditions and some other details. menuUp.onPress = function() { The main Congratulations on going through to the end of this tutorial and learning very much about ActionScript! I am glad that you have followed me until the end. There are some things I want to tell you. First, this gallery is modular, expandable and pretty powerful. Of course, it could have been done in many other ways: having many thumbnails in each section instead of a maximum of 20, scrolling thorough thumbnails etc. But that would be another gallery. The main purpose of my tutorial is to teach you to build a powerful dynamic image gallery by combining ActionScript and XML so that you can also build other galleries, which will have different interfaces and menus. I wanted you to show how to handle the main elements of such image galleries: XML, preloading, navigation, visual effects, etc. I hope that you have learned much and that you will use the knowledge in other galleries and create cool Flash websites. Note also that if you are going to have many, many images (let's say, about 100 sections), the XML file is going to be heavy. In that case, you will need to preload the XML too, using the XML object's getBytesLoaded and getBytesTotal methods. Also, note that in this tutorial's code I have limited the number of pictures to 1000. This is more than enough for any practical purposes. If you need even more than that, just change the if/else if/else statement inside the enableButtons function. I have invested much time, energy and work into the creation of this tutorial, because I want you to learn to create cool an amazing stuff in Flash, for free, and with as clear and detailed explanations as possible! Were it printed inside a paper book, the tutorial would be more than 80 pages long! And it's free. If you find it useful, please spread the word about flashexplained.com/, or better yet, link to flashexplained.com/! I will appreciate it very much. As for any questions considering this tutorial, please remember that I am not answering any such questions by e-mail. You can download the source files for this lesson below, comprising the entire folder structure (without the images, of course). See you in other ActionScript tutorials! Download all the source files here You can also leave a comment.i
must be less than the total number of child nodes (rootNode.childNodes.length
). Why less than and not equal to the number of child nodes? Because the loop starts with i
set to zero. Like the position of elements in an array and the position of characters in a string, for example, the XML nodes are also counted starting from zero. It is just how it’s done in ActionScript and the majority of programming languages.if
conditional statement, again. Exactly like for the root node, you tell Flash here to look for child nodes of the root node that are called gallery
.
…
…
i
will be replaced by the number of the current loop, of course: 0, 1, 2, 3, etc… so all child nodes will be parsed.currentGallery
variable:currentGallery.childNodes.length
is used to pull out the number of images in the current gallery section. This value is in turn stored inside the imagesInGallery
array (which you created when you defined all the variables) via the push()
method of the Array object.imagesInGallery
array is very important because the values stored inside it (the number of images in each gallery section) will be used later to tell Flash how many thumbnails should be loaded for a given gallery section.title
attribute for the current gallery
node is extracted and stored inside the galleryNames
array via its push()
method. This value will be later used to tell Flash what gallery section the user has chosen and also to give Flash the proper path to the section’s folder.intro
attribute is extracted and stored inside the galleryIntros
array. These values will be used to show what’s the current gallery section about — this is the description text which will appear in the big dynamic text field once the user clicks on a gallery section button.title
attribute of the current gallery section is extracted and stored inside the currentGalleryTitle
variable. There is an important distinction here: before, the same attribute’s value was extracted and placed inside an array along with its counterparts from other gallery
nodes and now it is placed inside a simple, plain variable. This is done because this variable’s value will be used very soon.galleryMenu_mc.buttonsHolder_mc.attachMovie
. As you remember, thsi is the empty clip beneath the mask in the gallery menu movie clip. The attachMovie()
method has 3 parameters:
"galleryButton"+i
. So, as the loop goes on, this instance name will be galleryButton0, galleryButton1, galleryButton2, etc.getNextHighestDepth()
method is used to place each movie clip on a separate depth level. Think of the depth levels as invisible layers inside the empty movie clip where you are attaching your buttons.currentGalleryButton
variable is used as a shortcut for this newly attached movie clip — the current gallery section button. And now, you see why it is very practical to use such “variable shortcuts” — there are many things that you will now do with the attached movie clip. The first one is positioning:
currentGalleryButton._y = galleryBtnVSpace*i;_x
property, which defines its horizontal position in relation to the left edge of the stage, expressed in pixels. This value is 0 (zero) for each gallery section button, because they must be aligned flawlessly inside the menu.galleryBtnVSpace*i
. If you remember, when you defined all the variables, the galleryBtnVSpace
was set to the value of 23. Like I said, I chose this value in relation to the height of the buttons, to be able to space them nicely. Since in every loop iteration this value is multiplied by i
, the position of buttons will be: 0, 23, 46, 69, etc.
"0"
) in front of each number because it looks cool :-).i+1
because I didn’t want the first label to be preceded with "00"
, but "01"
. Remember, in the main loop, the variable i
starts as zero." "
.currentGalleryTitle.toUpperCase()
. That’s why I advise to always write such elements inside your XML file in lowercase letters: it is easy to transform them any way you like with ActionScript later.for
loop which passes through each image
element inside each gallery node:
currentDescription = currentGallery.childNodes[j].firstChild.toString();
descriptions.push(currentDescription);
}
}j
here. The condition for the loop to continue the iterations is that j
, which starts at 0, must be lesser than the number of the child nodes of the current gallery
node, i.e. the number of image
nodes.if
which checks that the name of each node looked at is indeed "image"
and not something else. Like before, this is a way of telling Flash to look at precisely these nodes.
image
node is found, its first child’s value is converted to a String (text) value.image
node in the first gallery
node above is New York. And since this is an XML node, it has to be converted to a String value, for Flash to be able to display in the dynamic text field later and not to spit out a type mismatch error. Yes, the text between the
opening and closing tag is considered the image
node’s first child.descriptions
array.for
loop has ended after going through all the gallery
nodes, the value of i
, which matches the number of galleries (sections) is stored inside the numberOfGalleries
variable.enableButtons()
function is called and the variable numberOfGalleries
is passed to it.enableButtons()
function which has numberOfGalleries
passed as a parameter to it (which is of the Number
type) and it does not return a value, hence the Void
keyword:
for (i=0; i
pressedButton = galleryMenu_mc.buttonsHolder_mc["galleryButton"+i];
pressedButton.onRollOver = function():Void {
this.gotoAndStop(2);
};
pressedButton.onRollOut = function():Void {
this.gotoAndStop(1);
};
pressedButton.onPress = function():Void {
removeMovieClip(thumbsDisplayer);
removeMovieClip(displayBigImage);
tracker = 0;
thumbsDisplayer = imagesHolder.createEmptyMovieClip("thumbsDisplayer_mc", imagesHolder.getNextHighestDepth());
clickedGallery = Number(this._name.substr(13));
howManyImages = imagesInGallery[clickedGallery];
whichGallery = galleryNames[clickedGallery];
descText.text = galleryIntros[clickedGallery];
currentRow = 0;
currentColumn = 0;
loadThumbnail();
};
}
enableGalleryNavigation();
}enableButtons()
function is wrapped in a for
loop which serves to pass through all the gallery section buttons and define their onRollOver, OnRollOut and onPress event handler functions.numberOfGalleries
variable is — without it, you wouldn’t be able to know the number of gallery section buttons.pressedButton
.[
and ]
, which will result in galleryButton0, galleryButton1, galleryButton2, etc.
this.gotoAndStop(2);
};
this.gotoAndStop(1);
};this
points to the gallery section button movie clip itself, because it is contained inside the functions (inside the curly braces). Thanks to this, there is no need to specify the full path to the movie clip.onPress
, which governs what actions will be taken when the user has clicked a button. What will happen is the following:
removeMovieClip(thumbsDisplayer);
removeMovieClip(displayBigImage);
tracker = 0;
thumbsDisplayer = imagesHolder.createEmptyMovieClip("thumbsDisplayer_mc", imagesHolder.getNextHighestDepth());
clickedGallery = Number(this._name.substr(13));
howManyImages = imagesInGallery[clickedGallery];
whichGallery = galleryNames[clickedGallery];
descText.text = galleryIntros[clickedGallery];
currentRow = 0;
currentColumn = 0;
loadThumbnail();
};removeMovieClip()
method. If there weren’t any present, it doesn’t matter — nothing will happen. This is done by removing the placeholder movie clips that are created when the user clicks on a gallery section button or a thumbnail.
removeMovieClip(displayBigImage);thumbsDisplayer
empty movie clip is created from scratch, using the createEmptyMovieClip()
method. It will hold all the thumbnails of the gallery section that was selected by the user. This movie clip is created inside the imagesHolder
movie clip, the empty movie clip which you have created on the stage during the first stages of this tutorial. You could have created this latter one dynamically via ActionScript too, but I wanted things to be a little more diverse and simplified. Not everything needs to be done through coding.createEmptyMovieClip()
method:
getNextHighestDepth()
method because with it, Flash automatically assigns the first free depth to the new movie clip. And of course, this depth level is the first free inside the imagesHolder
movie clip, the one inside which the new empty movie clip is being created.clickedGallery
variable gets its value assigned. This value will be the identification number of the gallery section movie clip which was clicked. Since it is being extracted from the movie clip’s instance name, you need to pass it through the Number()
method to convert the character into a number.substr()
method of the String object is used to do the extraction, since the instance name of the movie clip is a string (text) value. It goes on like this: suppose that the user clicked on the fourth gallery section button (which is the monochrome section in my gallery example). The instance name of the clicked movie clip will be galleryButton3. Yes, there is the number 3 at the end of the instance name, because, as you remember, in the for
loop which is used to create these buttons, the starting value is always zero. The extraction would proceed like this (this is pseudo-code, not real one, just to show you how this process goes):
clickedGallery = Number("galleryButton3".substr(13));
clickedGallery = Number(3);
clickedGallery = 3;substr()
method is the starting point of extraction. The first character in a string (a piece of text) has the position 0 (zero).
whichGallery = galleryNames[clickedGallery];
descText.text = galleryIntros[clickedGallery];
howManyImages
).whichGallery
).descText.text
).clickedGallery
variable is 3. Flash will proceed like this:
whichGallery = galleryNames[3];
whichGallery = "monochrome";[
and ]
points to the value at the position no. 3 inside the galleryNames
array. You may already know it, but here it is: the first value inside an array in ActionScript has the position 0. The image below explains this nicely.
currentColumn = 0;loadThumbnail()
function is invoked, which will load the thumbnails for the chosen gallery section. This functions comes in the code just after the current one. Remember that all of this happens when the user has clicked on a gallery section button.for
loop, so it is executed once the loop has reached its end) that is going to be run:enableGalleryNaviagtion()
function which sets up the scrollability of the menu via the two buttons (up and down). This function will be defined at the end of all the gallery’s code.loadThumbnail()
function.
currentThumbnail = thumbsDisplayer.attachMovie("thumbnail holder", "thumbnail"+(tracker+1), thumbsDisplayer.getNextHighestDepth());
target = currentThumbnail.thumbImage_mc;
if ((tracker%5) == 0 && tracker != 0) {
currentRow += 1;
}
if (currentColumn>3) {
currentColumn = 0;
} else if (tracker == 0) {
currentColumn = 0;
} else {
currentColumn += 1;
}
currentThumbnail._x = currentColumn*thumbMarginX;
currentThumbnail._y = currentRow*thumbMarginY;
currentThumbnail.percent_txt._visible = true;
thumbNumber = currentThumbnail._name.substr(9);
thumbPath = "gallery/"+whichGallery+"/thumbs/"+thumbNumber+".jpg";
whatIsLoading = "thumb";
loader.loadClip(thumbPath, target);
}attachMovie()
method which you have already encountered in this tutorial.
(tracker + 1)
here because I don’t want the name of the first thumbnails to be thumbnail0, but thumbnail1. Why? Because I will need to extract that number later to point to the thumbnail JPEG image, and the first thumbnail in each gallery section is name 1.jpg and not 0.jpg.getNextHighestDepth()
method.currentThumbnail
variable.thumbImage_mc
empty movie clip of the current thumbnail holder.
currentRow += 1;
}tracker
equals 5, 10 or 15). This means that when those thumbnails’ turn comes, the value of the currentRow
variable will be increased by 1 (currentRow += 1
).if
conditional statement here and both must be fulfilled in order for the code inside the curly brackets to be executed:
tracker
divided by 5 must be equal to zero. It is divided by 5 because I want 5 thumbnails to be inside a row. Check out my tutorial on the use of the modulo operator in ActionScript to see how it is done.tracker
variable must not equal zero. This one serves only not to increase the value of currentRow
when tracker
is still equal to zero. This is a safeguard that ensures the first 5 thumbnails are placed in the first row and not moved below it.tracker
equals 0, or when it equals, say, 3 or 5? Let me show you how Flash makes the calculation in all three cases:
(0%5) == 0 && 0 != 0!=
) zero, which is false. And when just one condition turns out as false, the whole thing is scrapped and the code inside the curly braces (currentRow += 1
) is skipped altogether.tracker
equals 3.
(3%5) == 0 && 3 != 0
3 == 0 && 3 != 0
(5%5) == 0 && 5 != 0
0 == 0 && 5 != 0currentRow
will be increased by 1 and this will be used later to place the subsequent five thumbnails into the next row.
currentColumn = 0;
} else if (tracker == 0) {
currentColumn = 0;
} else {
currentColumn += 1;
}if/else if/else
conditional statement means this:
reset the value of currentColumn to 0
} on the other hand, if (tracker equals zero) {
reset the value of currentColumn to 0
} and in all other cases, meaning when currentColumn is either 0, 1, 2 or 3 {
increase the value of currentColumn by 1
}
currentThumbnail._y = currentRow*thumbMarginY;_x
and _y
properties, as you know by now, position a movie clip horizontally and vertically on the stage. The value of currentColumn
is multiplied by thumbMarginX
and that of currentRow
is multiplied by thumbMarginY
. These two values serve to put some space between the thumbnails. You defined them at the beginning of your code, remember? They are set to 96 and 68, respectively. This is the value of space between the thumbnails expressed in pixels.substr()
method of the String
object (a text object, and the name of a movie clip is a piece of text), the number of the thumbnail is extracted:substr()
method is the place where the extraction starts. And when the second parameter (length) is omitted, as above, all the characters from the exatraction point up to the end of the string will be extracted. Why do this? Because like that, you will be able to extract the number of the thumbnail, whether it is a one or two-digit number. No need to write any conditional statements at all here. The image below explains this clearly (the blue numbers are character positions inside the string):thumbNumber
variable is used next to point to the right thumbnail which needs to be loaded:
thumbPath = "gallery/" + "essays" + "/thumbs/" + "3" + ".jpg";
thumbPath = "gallery/essays/thumbs/3.jpg";loadClip()
method of the MovieClipLoader object (which is called loader
, you created it earlier, remember) is used to tell Flash to start loading (and preloading) the current thumbnail:loadClip()
method has been invoked, the MovieClipLoader and its associated listener will start working together and preload the image and display it, with the DropShadow filter applied to it. You can read again (I recommend it, it will be much more clear to you now) how this is done in the section which explains the use of the onLoadInit() and onLoadProgress() methods.
currentThumbnail.onPress = function() {
bigNumber = this._name.substr(9);
displayBigImage = imagesHolder.attachMovie("big image holder", "bigImage_mc", imagesHolder.getNextHighestDepth());
target = displayBigImage.imageHolder_mc;
bigImagePath = "gallery/"+whichGallery+"/"+bigNumber+".jpg";
whatIsLoading = "big";
disableThumbs();
loader.loadClip(bigImagePath, target);
if (clickedGallery>0) {
var descPosition:Number = 0;
for (i=0; i
descPosition += imagesInGallery[i];
}
descPosition = descPosition+Number(bigNumber)-1;
imageDesc = descriptions[descPosition];
} else {
imageDesc = descriptions[Number(bigNumber)-1];
}
descText.text = imageDesc;
};
currentThumbnail.enabled = false;
}thumbClickable()
function is stored inside the current thumbnail’s onPress
event handler function definition. This is to be expected, because the onPress
event handler tells Flash what to do when the user click on a thumbnail.attachMovie()
method which you now well by now:displayBigImage
. As a reminder, this movie clip contains:
whichGallery
and bigNumber
are used to guide Flash to the right folder and right image. And the path is stored inside the bigImagePath
variable.disableThumbs()
function which I will explain to you later.loadclip()
method of the MovieClipLoader object, exactly in the same manner as was done for the thumbnails. Only the path and the target empty movie clip are different.
var descPosition:Number = 0;
for (i=0; i
descPosition += imagesInGallery[i];
}
descPosition = descPosition+Number(bigNumber)-1;
imageDesc = descriptions[descPosition];
} else {
imageDesc = descriptions[Number(bigNumber)-1];
}
descText.text = imageDesc;if/else
conditional statement. Basically, this conditional logic boils down to this: if the user has clicked any gallery section than the first one (clickedGallery > 0
), a for
loop will be initiated for calculating the precise position of the selected image’s description. If the first gallery section is selected by the user, a much simpler search is performed. I will explain the first case in more detail noe.descPosition
variable, whose initial value is 0. This variable will hold the number which will be the image’s description position inside the descriptions
array.for
loop is initiated. The condition for the loop to exist is that i
, set to zero, must be lesser than the value of clickedGallery
. Remember, clickedGallery
is a variable that holds the number of the clicked gallery.
}descPosition
is updated. The value on the equation’s right side is added to the descPosition
‘s current value, through the use of the addition assignment (+=
) operator. What’s on the right side is a construct that is pulling values out of the imagesInGallery
array. All this is done in order for Flash to be able to find and display the proper image description.clickedGallery
is 2, because they start from zero. The first iteration (when i
equals 0) goes like this:
0 += imagesInGallery[0];
0 += 5;
5descPosition
is now 5. Why? Because the array imagesInGallery
stores values which correspond to the number of images in each gallery section. In my example, they are as follows, from the first to last gallery section: 5,6,19,9,7 (corresponding to architecture, essays, factory, monochrome and nature sections, respectively). The point is that you have to arrive to the gallery section which the user has selected.i
equals 1.
5 += imagesInGallery[1];
5 += 6;
11descPosition
is now 11. Why? Simple. Because the loop has come to en and. Once i
has reached the value of 1, the next iteration won’t be performed because the condition won’t validate any more: when i
equals 2, the condition i < clickedGallery
is false, because clickedGallery
equals 2, in this particular example.descPosition
. Flash has arrived at the third gallery section, but now it must find the description inside this particular section. This boils down to adding a number until you reach the matching description.
descPosition = 11+Number(6)-1;
descPosition = 11+6-1;
descPosition = 16;bigNumber
had to be converted into a numerical value (from a String type to a Number type), because it was a simple text character, not a numerical value, since it was pulled out of current big image’s name.descriptions
array, placed at number 16, because the counting inside an array starts from 0. In this case, it is “Empty silos.” And that’s what precisely the next line of code is about: pulling out this description from the descriptions
array and putting it inside the imageDesc
variable.
imageDesc = descriptions[Number(bigNumber)-1];
}descriptions
array. And that’s why the image description is pulled out from the array in a single line of code.thumbClickable()
function is the following:disableThumbs()
serves to turn off the clickability of the thumbnails. This function is invoked whenever the user clicks on a thumbnail and the big image starts to load.
for (i=0; i
thumbsDisplayer["thumbnail"+(i+1)].enabled = false;
}
}for
loop to loop through all the thumbnails. It will loop as many times as there are thumbnails in the selected gallery section (i < howManyImages
). Then, they are disabled one by one via turning their enabled
property to false
. Each thumbnail is accessed through the expression thumbsDisplayer["thumbnail"+(i+1)]
. Why i+1
? Well, again, I repeat, I have named the thumbnails thumbnail1, thumbnail2, etc, while the starting value of i
in the loop is 0.enableThumbs()
, works in the exact same way, the only difference being the enabled
property turned to true
this time. This function is invoked either when the user clicks on a gallery section button in the menu and all the thumbnails have been successfully loaded or when the big image was clicked, closed and the thumbnails for the selected section have appeared again.
for (i=0; i
thumbsDisplayer["thumbnail"+(i+1)].enabled = true;
}
}bigClickable()
function is here for.bigClickable()
function, the big image’s onPress event handler function is defined.
displayBigImage.onPress = function() {
removeMovieClip(this);
enableThumbs();
descText.text = galleryIntros[clickedGallery];
};
}removeMovieClip()
method, inside the parenthesis of which is the movie clip destined for removal. The keyword this
points to the big image itself, since it is placed inside its onPress
event handler function.enableThumbs()
function call is made. So, the thumbnails will be made clickable again, since the big image has been removed and they can be seen again. You probably noticed that I never made any code that would hide the thumbnails, because the big image effectively covers them all, even when there are twenty of them present. That’s just how I solved the problem, of course, if you wish, you may choose to hide them.
target.onEnterFrame = function():Void {
this._alpha += 10;
if (this._alpha>=100) {
delete this.onEnterFrame;
this._alpha = 100;
}
};
}_alpha
property (transparency) of the target movie clip, into which the big image has been loaded, is increased by 10 each time the event fires._alpha
property was set to 0 (total transparency, i.e. invisibility) once the onLoadInit()
function (one of the two preloading functions) saw that the loaded file was the big image._alpha
becomes equal to or greater than 100 (this._alpha >= 100
), the onEnterFrame event handler is deleted and the alpha set to 100.
delete this.onEnterFrame;
this._alpha = 100;
}this
inside the onEnterFrame event handler function points to the target
movie clip, because it is placed inside an event handler function associated with it (target.onEnterFrame
). This function is invoked once the image has been fully loaded.enableGalleryNavigation()
, is made of two main parts: the definitions of onPress event handler functions for the two buttons that serve for scrolling the menu: menuDown and menuUp. Just as a reminder, here is a screenshot of these two buttons:
menuDown.onPress = function() {
if (firstLook) {
menuUp._alpha = 100;
menuUp.enabled = true;
var firstLook:Boolean = false;
}
var menuTop:Number = menuButtons._height-Math.abs(menuButtons._y);
if (menuButtons._y<=0 && menuTop>=galleryMask._height) {
var targetPos:Number = menuButtons._y-galleryMask._height;
menuDown.enabled = false;
menuUp.enabled = false;
menuButtons.onEnterFrame = function():Void {
menuButtons._y += (targetPos-menuButtons._y)/menuSpeed;
if (menuButtons._y<=(targetPos+0.8)) {
menuButtons._y = Math.round(targetPos);
delete menuButtons.onEnterFrame;
menuDown.enabled = true;
menuUp.enabled = true;
}
};
}
};
menuUp.onPress = function() {
var menuTop:Number = menuButtons._height-Math.abs(menuButtons._y);
if (menuButtons._y<0 && menuTop>0) {
var targetPos:Number = menuButtons._y+galleryMask._height;
menuDown.enabled = false;
menuUp.enabled = false;
menuButtons.onEnterFrame = function():Void {
menuButtons._y += (-menuButtons._y+targetPos)/menuSpeed;
if (menuButtons._y>=(targetPos-0.8)) {
menuButtons._y = Math.round(targetPos);
delete menuButtons.onEnterFrame;
menuDown.enabled = true;
menuUp.enabled = true;
}
};
}
};
}
if (firstLook) {
menuUp._alpha = 100;
menuUp.enabled = true;
firstLook = false;
}
var menuTop:Number = menuButtons._height-Math.abs(menuButtons._y);
if (menuButtons._y<=0 && menuTop>=galleryMask._height) {
var targetPos:Number = menuButtons._y-galleryMask._height;
menuDown.enabled = false;
menuUp.enabled = false;
menuButtons.onEnterFrame = function():Void {
menuButtons._y += (targetPos-menuButtons._y)/menuSpeed;
if (menuButtons._y<=(targetPos+0.8)) {
menuButtons._y = Math.round(targetPos);
delete menuButtons.onEnterFrame;
menuDown.enabled = true;
menuUp.enabled = true;
}
};
}
};if
conditional statement which serves only to activate the menuUp button. If you remember, the menuUp button is invisible in the beginning, to avoid confusing the user. Once the menuDown button has been clicked, its counterpart will appear and the if
statement will never be used again, because the condition will yield as false.
menuUp._alpha = 100;
menuUp.enabled = true;
firstLook = false;
}if (firstLook)
translates as if (firstLook == true)
. And when the menuDown button has been clicked for the first time, this condition will yield as true, because the value of the variable firstLook
has been set as true at the very beginning of your code (when all the variables were defined).{
and }
, will be run. It restores the button's alpha (transparency) property to 100, meaning full opaqueness. After that, the button is enabled (it can be clicked).
menuUp.enabled = true;firstLook
is set to false
and that's why the if
statement will be ignored every subsequent time that the menuDown button is clicked: the other button has appeared and that's it.menuTop
variable is the lowest point of your menu, all section buttons included. I called it menuTop because the Y coordinate values increase when going down, that's why. This value is obtained by substracting the absolute value (meaning the number itself, whether positive or negative, it will be converted to a positive value) of the vertical position (_y
) of the menuButtons movie clip from its height. This value will be used to scroll the menu (to move it) properly, without fear that it will disappear by going off the stage etc.if
conditional statement that checks for two things:
menuButtons._y <= 0
) andmenuTop >= galleryMask._height
).
targetPos
variable is a reference point up to which the menu will be scrolled. Think of it as an anchor towards which the menu is pulled. Its value is obtained by substracting the height of the mask from the position of the menu buttons. It makes sense: you have to scroll your menu by the height of its mask, so that each time the buttons below the current ones appear.
menuUp.enabled = false;
menuButtons._y += (targetPos-menuButtons._y)/menuSpeed;
if (menuButtons._y<=(targetPos+0.8)) {
menuButtons._y = Math.round(targetPos);
delete menuButtons.onEnterFrame;
menuDown.enabled = true;
menuUp.enabled = true;
}
};+=
) adds the value on its right side to the cuurent value of the variable on its left side. And what's added is the result of substracting the position of the menu section buttons from the target position point, divided by menu speed. As the menu approaches the target position, the distance is smaller each time and there you have the easing effect. Remember that the lower the value of menuSpeed, the quicker the menu will move. I found 6 to be an optimal value in combination with the speed of the SWF, which was set to 30 fps at the beginning.if
conditional checks if the menuButtons movie clip's vertical position is lesser than or equal to (<=
) the target position plus 0.8 pixels (targetPos+0.8
). You have to make this so as a safeguard against waiting too long for the menu to reach the target position. This easing method is described in more detail in my sliding image mask tutorial.targetPos
value. I recommend that you always do that in cases like this, because objects placed on round pixels are rendered much better than in other cases. Well, Flash 8 renders graphics greatly, but ensuring this with round coordinates doesn't hurt.
menuUp.enabled = true;
var menuTop:Number = menuButtons._height-Math.abs(menuButtons._y);
if (menuButtons._y<0 && menuTop>0) {
var targetPos:Number = menuButtons._y+galleryMask._height;
menuDown.enabled = false;
menuUp.enabled = false;
menuButtons.onEnterFrame = function():Void {
menuButtons._y += (-menuButtons._y+targetPos)/menuSpeed;
if (menuButtons._y>=(targetPos-0.8)) {
menuButtons._y = Math.round(targetPos);
delete menuButtons.onEnterFrame;
menuDown.enabled = true;
menuUp.enabled = true;
}
};
}
};if
conditional statement checks if the position of menu buttons movie clip is lesser than zero and if the lower edge point of the menu buttons movie clip is greater than zero. This time, the target position is determined by adding the height of the mask to the menuButtons' current position.Conclusion
Afterword