ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Dynamic masking using the drawing API II
http://www.actionscript.org/resources/articles/169/1/Dynamic-masking-using-the-drawing-API-II/Page1.html
Patrick Mineault
Freelancer behind 5 1/2 math and physics enthusiast Patrick has a knack for making seemingly simple things overly complicated. Perfect for a tutorial writer. 
By Patrick Mineault
Published on September 9, 2005
 
Written by: Patrick Mineault, http://www.5etdemi.com
Difficulty Level: Advanced
Requirements: MX and experience with actionscript
Topics Covered: Drawing API, dynamic masking masking, curveTo, transitions
Assumed Knowledge: basic actionscript, first tutorial
Download FLA

Page 1 of 2
Written by: Patrick Mineault, [email:patrick@5etdemi.com], http://www.5etdemi.com
Difficulty Level: Advanced
Requirements: MX and experience with actionscript
Topics Covered: Drawing API, dynamic masking masking, curveTo, transitions
Assumed Knowledge: basic actionscript, first tutorial
Download FLA

Dynamic masking using the drawing API II

In a previous tutorial (dynamic masking using the drawing API), I showed you how to create interesting masking effects using the drawing API. If you haven't seen it yet, please read the introduction section in order to get familiar with the drawing API and masking commands. I purposefully avoided to use the curveTo drawing command in the first tutorial as it is a tad more complicated to use than lineTo. However, while designing my new site I discovered quite an interesting application of this command to the dynamic masking method and this is the highly popular 'swoosh' masking effect. Here's an example:

The movie attaches a picture on stage, assigns a mask movie clip to it and draws some shapes inside to reveal a portion of the picture. Since the shapes are curved, we'll need some background on the curveTo command.

The curveTo command

We'll need the following drawing API commands for this movie:

[as]MovieClip.beginFill()
MovieClip.endFill()
MovieClip.lineTo()
MovieClip.curveTo()
[/as]

The first three were covered in the first tutorial so that leaves us with curveTo. The curveTo(controlX, controlY, endX, endY) command draws a quadratic curve from (startX, startY) to (endX, endY). The (startX, startY) point is determined by the last point used by a moveTo, lineTo or curveTo command. The (controlX, controlY) point acts as a control point for the curve. The control point constrains the slope at the two end points of the curve. You can think of the control point as a 'magnetic point' to which the curve is attracted to. It works much like the control points on a Bezier curve, except there's one one per curve instead of two.

Getting back to our movie, the mask for the pics will be created in three successive stages, which are shown in the following diagram:

As you see, the mask will consist at first of a line at the top and will morph into the 4 sided-figure delimited by the blue line and the exterior frame. This will then morph into the red outline figure and finally into a complete rectangle. The crosses represent the approximate locations of the control points of the blue and red lines. We see immediately from this diagram that all of our figures can be represented by a four-sided figure with three straight sides and a curved line at the bottom.

One thing you may have noticed is that for the first and last frames the bottom line will not be curved. In this case the control point may be placed anywhere on the straight line. This will give us some flexibility in animation as you will see later on.

Morphing shapes

We have to morph shapes into one another. Although this may seem like a hard task it's actually as simple as moving an object from point A to point B. We'll move the three points of a first curve (start, control and end points) to the corresponding locations in a second curve. Let's start with a simple example morphing a C shape into a J. Create a new movie and add this script to the first frame:

[as]//Create movie clip and mask
counter = -1;
_root.createEmptyMovieClip('line', 0);
animInt = setinterval(doAnim, 17);
animDir = 1;

function doAnim()
{
        var currMC = _root.line;
        var time = animIndex/60;
        var dist = time;
        with(currMC)
        {
                //Coords for the C shape: (150, 25), (25,100), (150,175)
                //Coords for the J shape: (150, 25), (125,200), (50,125)
                clear();
                lineStyle(0x000000);
                moveTo(150,25);
                curveTo(25+100*dist,100+100*dist,150-100*dist, 175-50*dist);
        }
        
        animIndex += animDir;
        if(animIndex >= 60)
        {
                animDir = -1
        }
        if(animIndex <= 0)
        {
                animDir = 1
        }
}
[/as]

This gives us the following movie:

First, a blank movie clip is created in which the line will be drawn. A setinterval action is called so that the animation will advance every 17 milliseconds. A variable is used to keep track of where we are in the animation: animIndex. In this case, the animation takes 60 steps. The time variable goes from 0 to 1 depending on the value of animIndex. The dist variable ('distance') is set to the value of time to create a linear animation (more on this later).

Since the dist variable takes all values between 0 and 1 an obvious set of formulas are used to move from point a to point b:

[as]x = startX + dist*deltaX;
y = startY + dist*deltaY;
[/as]

Here startX and startY are the starting coordinates and deltaX and deltaY are the x and y distance between the start and end points. So what we do in this script is apply this set of formulas to the start, control and end points. In this case, deltaX and deltaY are 0 for the start point, which further simplifies our code.


Page 2 of 2

Easing, bouncing and other transitions

Now you're probably wondering why we bother to use both a time and a distance variable. Well, let's start by looking at the relationship between time (horizontal axis) and dist (vertical axis) graphically:

As you see, the relationship between time and distance is linear. The slope on this graph corresponds to the speed of the animation. In this case the speed is constant. Now if we want for example to start out fast and end at a null speed, we just have to find a function which has a high slope at t=0 and a null slope at t=1. This could be an upside down parabola or the portion of a sine from 0 to pi/2. By using different functions, we can create different relationships between time and distance and get interesting effects, like easing in and out, bouncing, and more esoteric transitions.

Here are a few examples of functions that may be useful:

 

This gives us the following transitions:

You may be interested in looking at Robert Penner's transition classes, which are based on this principle.

Putting it all together

With all of this in mind, we're ready to introduce the complete script:

[as]//Global vars
mWidth = 350;

//Create movie clip and mask
counter = -1;
createMovieClip();
animInt = setinterval(doAnim, 17);

function createMovieClip()
{
        counter++;
        attachmovie('pic' add (counter % 2), 'pic' add counter, counter);
        createEmptyMovieClip('mask' add counter, counter + 10000);
        this['pic' add counter].setMask(this['mask' add counter]);
}

function doAnim()
{
        var currMC = _root['mask' add counter];
        if(animIndex < 15)
        {
                var time = animIndex/15;
                var dist = 0.5*Math.sin(Math.Pi*(time-0.5)) + 0.5;
                
                with(currMC)
                {
                        clear();
                        beginFill(0x000000);
                        lineTo(mWidth,0);
                        lineTo(mWidth,dist*125);
                        curveTo(250,dist*40,0,10*dist);
                        endFill();
                }
        }
        else if (animIndex < 35)
        {
                var time = (animIndex-15)/20;
                var dist = 0.5*Math.sin(Math.Pi*(time-0.5)) + 0.5;
                
                with(currMC)
                {
                        clear();
                        beginFill(0x000000);
                        lineTo(mWidth,0);
                        lineTo(mWidth,125);
                        curveTo(250-100*dist,40+150*dist,0,10+190*dist);
                        endFill();
                }
        }
        else if (animIndex <= 50)
        {
                var time = (animIndex-35)/15;
                var dist = 0.5*Math.sin(Math.Pi*(time-0.5)) + 0.5;
                
                with(currMC)
                {
                        clear();
                        beginFill(0x000000);
                        lineTo(mWidth,0);
                        lineTo(mWidth,125+75*dist);
                        curveTo(150,190+10*dist,0,200);
                        endFill();
                }
        }
        
        animIndex++;
        if(animIndex > 50)
        {
                animIndex = 0;
                _root['pic' add (counter - 1)].removeMovieClip();
                _root['mask' add (counter - 1)].removeMovieClip();
                createMovieClip();
        }
}
[/as]

The script uses two functions: createMovieClip and doAnim. The createMovieClip function attaches a picture on stage, creates an empty movie clip and sets it as the mask of the picture. Since we are stacking pics one on top of the other, a counter variable is set up to keep track of instances.

The doAnim function is called every 17 milliseconds. The animIndex variable keeps track of the current point in the anim. As I mentioned in the first part, the animation takes place in three steps corresponding to the three intermediate shapes of the mask.

This time, I've decided to use the 'easing in and out' function as sketched above. Now, it's simply matter of drawing the shapes using the drawing API functions. For the first and last straight bottom lines, the position of the control point can be moved to the left and right. Playing with this parameter will influence the intermediary shapes obtained and will give a more or less pleasing result. Experimenting is the way to go.

After each transition, a new pic is added using the createMovieClip function and it starts all over again.

Conclusion

In this tutorial, I've shown you how to create a 'swoosh' mask transition using the drawing API and dynamic masking. By now, you should understand the curveTo method, the basics of shape morphing and how to easily create easing and bouncing transitions. Of course, there's no better way to get a hand on your new knowledge than with practice. The techniques shown here are quite general; here is another version of the same file but with different parameters:

If you need professional help with ActionScript, please visit 5etdemi.com for my portfolio and contact info. Happy flashing!