You're probably aware of those nice reflections appearing in
some of the latest websites, online ads, and even software. It's an effect
common to "Web 2.0" designs, in which something like an album cover
or video player appears to reflect some sort of virtual floor beneath it. This
tutorial steps you through the creation of the Reflect class, a
custom ActionScript 3.0 class that you can apply to reflections on movie clips (and
use to modify them) in your Flash CS3 Professional projects.
The Reflect class is intended for any developer or
designer who would like to achieve an effect that gives a great impact to your
design, but with little effort. Typically it would take a fair amount of effort
to manually create reflections in your Flash files. This task would likely
involve copying and pasting a clip and then modifying it to achieve the look of
the reflection. Every time a change was made, you would need to update your
effect. If you had an animated clip or video that you wanted to reflect, it
might become completley unfeasible or even impossible. However, the Reflect class allows you to apply the reflection only once because it dynamically
updates itself as a true mirror of the clip which it is reflecting.
My work on this ActionScript 3.0 class is a testament to my own love of the Flash community, which is unlike any other pool of developers or designers I know. If you ever need any help with code or features, they are willing to help. After my original code for reflecting movie clips in ActionScript 2.0 was released on my blog, many gracious members of the Flash community posted helpful comments, feature requests, and even code snippets to help improve the class. The updated class described in this article takes into consideration these additions from users who dedicated their time and effort to help me improve it.
In order to make the most of this article, you need the following software and files:
This article is intended for Flash developers who have an intermediate to advanced understanding of ActionScript 2.0 or 3.0 and also of ActionScript classes.
Before we get started, it might be useful to demonstrate the
effect I am describing. As you drag the sliders in the Reflection Explorer (see Figure 1), you will notice that the Reflection Code box dynamically updates and
provides you with the code necessary for applying the reflection to the Flash
file. This example, which requires Adobe Flash Player 9 (version 9,0,45,0 or later), assumes a movie clip
with an instance name of ref_mc.
To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.
Download the free Flash Player now!
Figure 1. Reflection Explorer (drag the sliders to see the effect)
Click the Show Video button at the top of the Reflection
Explorer to toggle between a video and its image-based reflection. You may
notice that when you are applying the Reflect class to the
image, you are setting an update time of –1. This saves on processor overhead because
you don't need to constantly update the reflection, as you would with the
video. I will cover all of these parameters later in this article.
Whenever you start a class file, you should set the package
library. The following line defines the package library path for the Reflect class:
package com.pixelfumes.reflect
For practical purposes, I named this file Reflect.as and nested it within a specific folder structure. This helps ensure that my
code does not conflict with any other existing Reflect class code
library that exists. For my purposes, I nested the class in the package path of com.pixelfumes.reflect. You
really could make this whatever you want, but remember that the Reflect.as file needs to be in a folder structure that follows the package library. As you
can see in the code download, the \com folder contains a \pixelfumes folder,
which contains a \reflect folder in which the Reflect.as file lives.
The Reflect class takes advantage of a number of
classes. As you follow through the code, you will see objects from these
classes in use. First import the classes in order to make use of them within
the Reflect class:
package com.pixelfumes.reflect{
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.utils.setInterval;
import flash.utils.clearInterval;
Now that you have the imported classes set, you can start
writing the actual Reflect class. In this case, you are extending
the MovieClip class. The following code opens the Reflect class and sets
several variables that are used for tracking the information pertinent to the Reflect class:
public class Reflect extends MovieClip{
//Created By Ben Pritchard of Pixelfumes 2007
//Thanks to Mim, Jasper, Jason Merrill and all the others who
//have contributed to the improvement of this class
//static var for the version of this class
private static var VERSION:String = "4.0";
//reference to the movie clip we are reflecting
private var mc:MovieClip;
//the BitmapData object that will hold a visual copy of the mc
private var mcBMP:BitmapData;
//the BitmapData object that will hold the reflected image
private var reflectionBMP:Bitmap;
//the clip that will act as out gradient mask
private var gradientMask_mc:MovieClip;
//how often the reflection should update (if it is video or animated)
private var updateInt:Number;
//the size the reflection is allowed to reflect within
private var bounds:Object;
//the distance the reflection is vertically from the mc
private var distance:Number = 0;
Once you have the global variables for the class's scope
defined, you can write the constructor function. The constructor function of
the Reflect class accepts an object, which is referred to as args in the example
below. The args object is responsible for passing the user-defined data into the Reflect class. For example, a typical instantiation of the Reflect class would
look something like this within the Flash document:
import com.pixelfumes.reflect.*;
var r1:Reflect = new Reflect({mc:ref_mc, alpha:50, ratio:50,
distance:0, updateTime:0, reflectionDropoff:1});
Notice that to use the class, you have to import the Reflect class using the package's code library path already defined—namely, com.pixelfumes.reflect.
After importing the class for use, define a variable to hold a reference to the Reflect class instance and pass it several items wrapped in an object. In this case,
pass in the movie clip being reflected, the alpha for the reflection, the ratio
to use in the gradient mask, the distance of the reflection, the update time,
and the reflection dropoff.
The arguments are then pulled from the args object and used to
set the class variables defined earlier. Based on the data passed into the
class, you can set some additional variables within the class, such as the clip's
height and width and the bounds in which the reflection can appear:
function Reflect(args:Object){
/*the args object passes in the following variables
/we set the values of our internal vars to math the args*/
//the clip being reflected
mc = args.mc;
//the alpha level of the reflection clip
var alpha:Number = args.alpha/100;
//the ratio opaque color used in the gradient mask
var ratio:Number = args.ratio;
//update time interval
var updateTime:Number = args.updateTime;
//the distance at which the reflection visually drops off
var reflectionDropoff:Number = args.reflectionDropoff;
//the distance the reflection starts from the bottom of the mc
var distance:Number = args.distance;
//store width and height of the clip
var mcHeight = mc.height;
var mcWidth = mc.width;
//store the bounds of the reflection
bounds = new Object();
bounds.width = mcWidth;
bounds.height = mcHeight;
Now that you have that out of the way, you can start getting
into the meat of the class. The Reflection class makes heavy use of the BitmapData class, which enables you to take visual snapshots of movie clips with its draw method. Using the draw method, you can snapshot the movie clip
passed into the class and use the data to populate a Bitmap object. Once you
have the Bitmap object created, you can flip it upside down, position it, and add it to the Display Object stack. The following code handles the creation of the BitmapData and Bitmap objects, as well as handles their positioning:
//create the BitmapData that will hold a snapshot of the movie
clip
mcBMP = new BitmapData(bounds.width, bounds.height, true,
0xFFFFFF);
mcBMP.draw(mc);
//create the BitmapData the will hold the reflection
reflectionBMP = new Bitmap(mcBMP);
//flip the reflection upside down
reflectionBMP.scaleY = -1;
//move the reflection to the bottom of the movie clip
reflectionBMP.y = (bounds.height*2) + distance;
//add the reflection to the movie clip's Display Stack
var reflectionBMPRef:DisplayObject = mc.addChild(reflectionBMP);
reflectionBMPRef.name = "reflectionBMP";
If you were to finish the class here by closing up the constructor and package, you would be presented with a movie clip that looks like Figure 2.

Figure 2. Image after creating the
BitmapData and Bitmap objects
As you can see, the class is copying the visual representation of the movie clip, flipping it upside down and positioning itself under the original movie clip.
Now that you have the reflected image in the proper position, you can create a movie clip that holds the gradient that you'll use to create the gradient mask:
//add a blank movie clip to hold our gradient mask
var gradientMaskRef:DisplayObject = mc.addChild(new
MovieClip());
gradientMaskRef.name = "gradientMask_mc";
//get a reference to the movie clip - cast the DisplayObject
that is returned as a MovieClip
gradientMask_mc = mc.getChildByName("gradientMask_mc")
as MovieClip;
With the gradient mask clip added to the display, you can
now work on the actual gradient. This gradient uses several values of interest.
To create the gradient, you can use the alpha and ratio variables that were passed. These variables, along with the bounds I defined
earlier, construct a gradient box within the gradientMask_mc clip.
The following code steps you through the creation of the
linear gradient you will be using. Feel free to experiment with different fillType and spreadMethod instances, as these will alter the final appearance of your gradient mask:
//set the values for the gradient fill
var fillType:String = GradientType.LINEAR;
var colors:Array = [0xFFFFFF, 0xFFFFFF];
var alphas:Array = [alpha, 0];
var ratios:Array = [0, ratio];
var spreadMethod:String = SpreadMethod.PAD;
//create the Matrix and create the gradient box
var matr:Matrix = new Matrix();
//set the height of the Matrix used for the gradient mask
var matrixHeight:Number;
if (reflectionDropoff<=0) {
matrixHeight = bounds.height;
} else {
matrixHeight = bounds.height/reflectionDropoff;
}
matr.createGradientBox(bounds.width, matrixHeight,
(90/180)*Math.PI, 0, 0);
With the variables for the gradient mask now populated, you can
now use the graphics property of the gradientMask_mc clip to
create the GradientFill fill. Once the fill is created within the clip, position gradientMask_mc over reflectionBMP:
//create the gradient fill
gradientMask_mc.graphics.beginGradientFill(fillType, colors,
alphas, ratios, matr, spreadMethod);
gradientMask_mc.graphics.drawRect(0,0,bounds.width,bounds.height);
//position the mask over the reflection clip
gradientMask_mc.y = mc.getChildByName("reflectionBMP").y
- mc.getChildByName("reflectionBMP").height;
For the reflection to be properly masked with the gradient
mask, you need to ensure that both the gradient mask and the reflectionBMP are set to cacheAsBitmap
= true:
//cache clip as a bitmap so that the gradient mask will function
gradientMask_mc.cacheAsBitmap = true;
mc.getChildByName("reflectionBMP").cacheAsBitmap =
true;
//set the mask for the reflection as the gradient mask
mc.getChildByName("reflectionBMP").mask =
gradientMask_mc;
For the final part of the constructor, check to see if you have
a valid update time, which refreshes the BitmapData that
contains a snapshot of the movie clip you are reflecting. Clips that don't move
and are not animated do not need an update time. If you pass in –1, you can
avoid creating an interval to update the BitmapData object and
save processing overhead. Movie clips containing videos or buttons that change upon
rollover should have an update applied to them, so that the reflection remains
visually accurate:
//if we are updating the reflection for a video or animation do
so here
if(updateTime > -1){
updateInt = setInterval(update, updateTime, mc);
}
With the bulk of the class out of the way, all you need now
are a few methods to adjust the reflection. The setBounds method allows
you to define an area in which the reflection is available. Keep in mind that,
because you are taking a snapshot of the area defined by this method; larger
bounds will use more memory. The setBounds method is ideal for clips that
animate along a horizontal access within their own timeline.
Take a look at the animation in Figure 3 (click Start Demo to see the effect, Stop Demo to pause it). If a clip has the Reflect class applied to it at runtime, and then the clip's timeline moves items outside
of the initial bounds of the clip, the Reflect class will not
show them because it had its bounds defined by the size of the clip at runtime.
To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.
Download the free Flash Player now!
Figure 3. Animation using setBounds (click Start Demo to see the effect)
Using setBounds enables the user to compensate for
the size difference by increasing the size of the bounds if needed:
public function setBounds(w:Number,h:Number):void{
//allows the user to set the area that the reflection is
allowed
//this is useful for clips that move within themselves
bounds.width = w;
bounds.height = h;
gradientMask_mc.width = bounds.width;
redrawBMP(mc);
}
The redrawBMP method, used by the setBounds class, serves the role of destroying the current BitmapData and creating
a new one to compensate for the new bounds that were defined:
public function redrawBMP(mc:MovieClip):void {
// redraws the bitmap reflection - Mim Gamiet [2006]
mcBMP.dispose();
mcBMP = new BitmapData(bounds.width, bounds.height, true,
0xFFFFFF);
mcBMP.draw(mc);
}
The update method refreshes the data held within
the BitmapData snapshot of the movie clip. The more often the update method is run,
the more processer-intensive the class becomes but the smoother the reflection
playback looks. A good rule of thumb is to use as high a number as the visual
performance will allow when setting the update time:
private function update(mc):void {
//updates the reflection to visually match the movie clip
mcBMP = new BitmapData(bounds.width, bounds.height, true,
0xFFFFFF);
mcBMP.draw(mc);
reflectionBMP.bitmapData = mcBMP;
}
You need a way to get rid of that reflection once you have
created it. For this, you can create the destroy method, which removes
the reflection items from the DisplayObject stack, clears the intervals, and
removes all traces of the reflection:
public function destroy():void{
//provides a method to remove the reflection
mc.removeChild(mc.getChildByName("reflectionBMP"));
reflectionBMP = null;
mcBMP.dispose();
clearInterval(updateInt);
mc.removeChild(mc.getChildByName("gradientMask_mc"));
}
The Reflect class still has quite a few possibilities, and it leaves plenty of room for extension and improvement. Although using the Reflect class is pretty straightforward when working with static images, it becomes a bit different when reflecting items such as video. If you plan on reflecting a clip containing video, it is best to wait until the video is completely loaded before applying the class.
I would love to see any improvements and additions you would
like to add to the Reflect class. Contact me at my blog, pixelfumes.com/blog. The more that we in the Flash
community can push one another, the further we will push the boundaries of
Flash as a platform.
Ben Pritchard is a senior Flash developer at New Perspective in Pittsburgh, Pennsylvania, and founder of Pixelfumes. Ben has over seven years of experience in the field and has won industry awards, including honors from the American Advertising Federation and Communication Arts. Ben is also co-manager of the Pittsburgh Flash Users Group (PittMFUG) and an active blogger of all things Flash.