Chapter 8. Drawing with Vectors

IN THIS CHAPTER

The Graphics Class

The Geometry Package

9-Slice Scaling

Applied Examples

What’s Next?

Flash is well known for popularizing vector graphics on the Web. Put simply, vectors are composed of mathematically generated points, lines, curves, and shapes and are used to create artwork in computer software. Using vectors is optimal when you need to scale artwork because the vectors remain crisp and clean at any size. By contrast, bitmap graphics pixelate when scaled.

Drawing vectors graphics with code brings with it special benefits. Included among them is the freedom to create assets on the fly, rather than relying solely on art drawn or imported prior to publishing your file. Related to this is the additional bonus of reduced file size, because assets are created at runtime rather than occupying space in your SWF. Smaller files mean less time that your viewers spend waiting for your files to load.

In this chapter, we’ll focus on drawing vectors, the first of two ways to originate visual assets with code. Over the next several pages, we’ll cover:

  • The Graphics Class. This class, often referred to as part of the drawing API, contains methods for drawing vectors. You have control over stroke and fill attributes, and can move a virtual pen around the screen, choosing where to draw lines, curves, and shapes like circles and rectangles.

  • The Geometry Package. This utility package contains classes for creating points and rectangles, as well as transforming objects, and creating matrices (a special kind of number array) for complex simultaneous changes to rotation, scaling, and x and y translation. Using matrices, you can achieve effects for which no properties exist, including skew and shear.

  • 9-slice Scaling. Through the use of a dynamically assignable rectangle, 9-slice scaling can prevent the sides and corners of a movie clip from distorting when scaled.

  • Applied Examples. Combining what you’ll learn in this chapter, you’ll write a custom button class that can be reused from project to project, and create the graphics for a color picker. You can then carry the color picker exercise into the next chapter, where you’ll put it to work while composing and creating bitmaps.

The Graphics Class

The Graphics class is the foundation for drawing vectors with code. You use methods of this class to define line and fill styles, and draw lines, curves, and shapes, similar to how you would by using the Flash interface.

Before we get started with syntax-specific discussions, however, here’s a quick word of advice about where to draw your vectors. It is possible to draw vectors directly into the main timeline, but we recommend that you first create one or more movie clips or sprites to serve as canvases for your drawings. This is analogous to an artist drawing on a canvas instead of a studio wall—which makes it a lot easier to move a masterpiece around or exhibit it in a gallery. The same is true of virtual canvases in movie clips.

For example, if you draw into a movie clip, you can change its depth, assign it to a new parent, or change many properties to affect its appearance or functionality. Similarly, as you’ll learn in the next chapter, you can apply special effects and filters to movie clips, which can’t be applied directly to the stage.

This is particularly relevant because you don’t create a new instance of the Graphics class when you want to start drawing. Instead, all methods of the class must be called from the graphics property of the movie clip or sprite you’re drawing into, and it’s useful to create a reference to this property, both as a shortcut and performance enhancement. For example, the following code creates a sprite canvas and stores its graphics object in the variable g. In this snippet, <methodOrProperty> is a placeholder for method or property syntax we are about to introduce.

var canvas:Sprite = new Sprite();
var g:Graphics = canvas.graphics;
g.<methodOrProperty>;

After creating g, you can manipulate all methods and properties of the Graphics class from that reference. This is not only less to type, but it’s faster because the player doesn’t have to retrieve the reference to the graphics object every time it’s used. This isn’t a requirement, and we may not use this method universally throughout this book, but it’s a good habit to get into.

To demonstrate styling and drawing lines, curves, and shapes, we’re going to build the contents of Figure 8-1 over several examples. Continuing the same example over multiple snippets will also emphasize the fact that you can continue drawing from where you left off, move your virtual pen before drawing again, and restyle your stroke or fills while you draw. The finished script can be found in the lines_curves_primitives.fla source file.

The culmination of several Graphics class method calls
Figure 8-1. The culmination of several Graphics class method calls

Drawing Lines

The first step in drawing lines is to set a line style using the lineStyle() method. This is equivalent to setting several stroke properties in the Properties panel of the Flash Professional interface. The typical syntax is as follows:

1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    g.lineStyle(2, 0x000000);

Note

As described in Chapter 4, when you don’t need a timeline, as in this example, you can work with a sprite instead of a movie clip. For more information about when to use MovieClip and when to use Sprite, see the “MovieClip versus Sprite” post at the companion website, http://www.LearningActionScript3.com.

The first parameter of the lineStyle() method represents line thickness in points, and the second is color in 0xRRGGBB hexadecimal format, as described in Chapter 3. When a color is not included, black is used as the default. When a line thickness of 0 is specified, a hairline thickness is used. If you don’t want to use a line at all, you can omit the method. If you want to switch to no line for future shapes, after you’ve already started drawing, call the method with no parameters to clear any existing line style.

Note

The lineStyle() method includes additional properties that are also found in the Properties panel, including alpha, stroke hinting, caps (end cap style: round, square, or none), join (joint style: round, bevel, or miter), and miter (miter limit: degree of joint pointiness). In cases like these, where the Flash Professional interface overlaps an ActionScript method so thoroughly, comparing the Properties panel with the ActionScript documentation can help jump-start your experimentation with these features.

The next step is to draw the line. The process of doing so is similar to physically drawing a line on a piece of paper. Ordinarily, you don’t start drawing a line from the edge of the paper to the intended first point of the line, and then continuing to draw until you reach the second point of the line. Instead, you move your pen to the preferred starting point and then begin drawing. This is also true with the Graphics class. If you don’t first move your virtual pen to the line’s starting point, you will begin drawing from point (0, 0), the upper-left corner of your canvas. The moveTo() method moves the virtual pen to the x and y coordinate specified therein, and the lineTo() method draws from the previous virtual pen location to the x and y coordinates specified. Continuing our script from the prior code block, the following sequence will first move to point (150, 100) and then draw to point (400, 100):

6    //continued from prior section
7    g.moveTo(150, 100);
8    g.lineTo(400, 100);

To continue drawing straight lines, you can add more lineTo() methods. Each successive call will continue drawing the line from the previous location, as if you never lifted pen from paper. You can, however, change line styles at any time during the process.

The following script continuation draws another line 20 pixels down, and then another line back to the left to the x coordinate where we started. It then changes the line style from 2-pixel black to 4-pixel red, moves the pen to a new location 55 pixels below the prior line, and draws another line of the same length back to the right. When this script block finishes executing, it will have drawn the straight black and red line segments seen in Figure 8-1.

9    //continued from prior section
10   g.lineTo(400, 120);
11   g.lineTo(150, 120);
12   g.lineStyle(4, 0xFF0000);
13   g.moveTo(150, 175);
14   g.lineTo(400, 175);

Drawing Curves

As you might imagine, you’re not limited to drawing straight lines. You can also draw curves like those created by vector drawing programs such as Adobe Illustrator. The syntax for drawing a curve requires the addition of a point that will act as a control point, effectively pulling the curve away from an ordinary straight-line appearance. This is equivalent to creating a control point in Illustrator.

Note

Although originally developed by Paul de Casteljau, vector curves are commonly called Bézier curves because they were famously used by French engineer Pierre Bézier in the design of automotive bodies during the early 1960s.

ActionScript, however, uses the quadratic Bézier curve model. Quadratic curves use one control point (often referred to as a handle) for both end points of a line segment. By contrast, other drawing tools (including Illustrator) use the cubic Bézier model, which adds separate control handles for each point. A quadratic Bézier curve is illustrated in Figure 8-2, showing both end points and the control point used to manipulate the curve.

A quadratic Bézier curve with one control point for both end points of a line segment
Figure 8-2. A quadratic Bézier curve with one control point for both end points of a line segment

Though the algorithms used by ActionScript behind the scenes aren’t paramount, remembering that only one control point is used to create a curve can help you remember the syntax of the curveTo() method, used to draw a curve with the drawing API. Here is the method’s signature:

curveTo(controlX:Number, controlY:Number,
anchorX:Number, anchorY:Number):void

Unlike lineTo(), it uses four coordinates. The first two are the x and y values of the control point, and the second two are the x and y values of the destination point.

The following code continues our script by drawing the curve shown at the top of Figure 8-1. It starts by switching to a 2-point blue line and moving the pen to point (150, 100). It then draws a curve that ends at point (400, 100) but is affected by the control point at point (275, 0).

1    //continued from prior section
2    g.lineStyle(2, 0x0000FF);
3    g.moveTo(150, 100);
4    g.curveTo(275, 0, 400, 100);

It’s also possible to draw simple shapes including a circle and a rectangle with or without rounded corners. Before we demonstrate drawing these basic shapes, let’s introduce how to style fills.

Adding Solid Fills

To add a solid-color fill to a drawing, you must use the beginFill() method. It accepts two parameters: color and alpha. Color is a uint (an unsigned integer, or nonnegative integer), and is typically specified in the 0xRRGGBB hexadecimal format. The alpha value is a Number in the percentage range of 0 to 1, with a default of 1 (100 percent).

After setting a fill style, you can continue drawing lines, curves, and shapes, and then conclude with the endFill() method, which uses no parameters. The following code demonstrates two things. First, it shows the benefit of drawing into a dedicated canvas, allowing you to position the display object (and therefore your drawing) anywhere on the stage (lines 20 through 23). It then demonstrates line and fill styling (lines 26 and 27) and moving to, and drawing, a triangle (lines 28 through 31). Finally line 32 ends the fill.

Note

Although the endFill() method can be omitted for simple drawings, doing so can produce unexpected results. See the “Using endFill() with the Drawing API” post at the companion website for more information.

1    //continued from prior section
2    var triangle:Sprite = new Sprite();
3    triangle.x = 50;
4    triangle.y = 250;
5    addChild(triangle);
6
7    var tg:Graphics = triangle.graphics;
8    tg.lineStyle(0);
9    tg.beginFill(0xFF9900, 1);
10   tg.moveTo(50, 0);
11   tg.lineTo(100, 100);
12   tg.lineTo(0, 100);
13   tg.lineTo(50, 0);
14   tg.endFill();

Drawing Shapes

Drawing one line segment at a time is not the only method for drawing shapes. It’s also possible to draw simple shapes using a trio of methods: drawCircle(), drawRect(), and drawRoundRect() (for drawing rectangles with rounded corners). The following code segment concludes our ongoing script by drawing three shapes—with varying fill colors and fill alpha values—into the same canvas, newly created in lines 34 through 37. Drawing multiple objects into one canvas reduces flexibility because you can’t manipulate the objects separately thereafter. However, this is useful when drawing complex shapes that will be treated as a single object.

Lines 41 and 42 show how to use opacity for a special effect. Note that the stroke and fill both have an alpha value of 50 percent. The fill is red and the stroke is blue and 6 pixels thick. In Flash, strokes center on the edge to which they are applied, which, in this case, results in a 3-pixel overlap between stroke and fill edge. The partial opacity of both stroke and fill result in a red circle with the appearance of a 3-pixel purple outline surrounded by a 3-pixel blue outline. Line 43 creates the circle itself, using the drawCircle() method. This method requires the x and y values of the center of the circle (50, 50), and the circle’s radius (50). The end result can be seen in the circle at the bottom of Figure 8-1.

1    //continued from prior section
2    var shapes:Sprite = new Sprite();
3    shapes.x = 150;
4    shapes.y = 250;
5    addChild(shapes);
6
7    var sg:Graphics = shapes.graphics;
8
9    sg.lineStyle(6, 0x0000FF, 0.5);
10   sg.beginFill(0xFF0000, 0.5);
11   sg.drawCircle(50, 50, 50);
12   sg.endFill();
13
14   sg.lineStyle();
15   sg.beginFill(0x0000FF, 0.2);
16   sg.drawRect(125, 0, 100, 100);
17   sg.endFill();
18
19   sg.beginFill(0x0000FF, 0.5);
20   sg.drawRoundRect(250, 0, 100, 100, 50);
21   sg.endFill();

Line 46 shows how to clear a previously existing line style. If you want to begin without a stroke, it’s easy to omit the method. If a stroke already exists, however, and you want to clear it, you must invoke the lineStyle() method with no parameters. (If you use a value of 0, the method creates a hairline stroke.) Line 48 draws a rectangle using the drawRect() method, which accepts the x and y coordinates of the rectangle, followed by the width and height of the rectangle. The last shape method, drawRoundRect() in line 52, is the same as drawRect() but adds a fifth parameter for the corner radius used to draw all four corners of the rectangle. See Figure 8-1 to check the results of this finished script.

Note

An undocumented method called drawRoundRectComplex() allows you to control the corner radius of each corner independently. Here is the method signature:

drawRoundRectComplex(x:Number, y:Number, width:
Number, height:Number, topLeftRadius:Number, topRightRadius:Number,
 bottomLeftRadius:Number, bottomRightRadius:Number):void

The following code, found in the draw_round_rect_complex.fla source file, creates a graphic that looks like a tab, which is convenient for tab-based navigation systems.

var tab:Sprite = new Sprite();
tab.x = tab.y = 50;
addChild(tab);
tab.graphics.beginFill(0x333399);
tab.graphics.drawRoundRectComplex(0, 0, 100, 25,
                                  15, 15, 0, 0);

As with all undocumented code, use at your own risk. Adobe may remove this method at any time. (The likelihood of that is probably low, however, because the method has been part of the Flex ActionScript documentation since Flex 2. Adobe has never made public why they chose not to document this method for Flash Professional users.)

Using Gradient Fills and Lines

ActionScript 3.0 doesn’t restrict you to using solid colors for your fills or lines. You can also use gradients and bitmaps. Let’s first discuss gradients, using the beginGradientFill() method for fills and the lineGradientStyle() method for lines.

Gradient fills

Gradients can be linear (left to right, by default) or radial (radiating from the epicenter of the gradient outward). The content of the gradient is then determined by three parallel arrays (arrays with the same number of items in a corresponding order): colors, alpha values for each color, and ratios—values for each color that determine its weighting within the gradient.

The type of gradient is specified by the GradientType constants LINEAR or RADIAL. The colors of the gradient are specified as an array of color values, typically uint values in hexadecimal format, and listed within the array in the order in which they appear in the gradient. The alpha values for color are specified as an array of Number values between 0 and 1, and correspond with the order of the colors.

The ratio array contains a number for each color that places it within the gradient between 0 (far left, or center of radial) to 255 (far right or outer edge of radial). For simplicity, we’ll use a linear gradient in our description, but the same ideas apply to radial gradients.

Think of the numeric span from 0 to 255 as a distance. If a gradient has only two colors, an evenly distributed gradient would have a ratio array of [0, 255]. In this example, the starting value of one color is at the extreme left and the starting value of the other color is at the extreme right. The mixture between these two colors creates the gradient, as you can see in the center of Figure 8-3.

Gradient color ratios
Figure 8-3. Gradient color ratios

Note

Graphic symbols beneath each gradient in Figure 8-3 mark color positions for demonstration purposes only. Although these symbols make intentional allusions to the Flash Professional Color panel, the gradients in the figure were created solely with ActionScript.

However, you can also weight a color by skewing the ratio array. For example, to favor the right color, move its starting point further to the left—expanding the amount of the right color in the gradient, and reducing the amount of the left color, resulting in a ratio of [0, 127]. The top of Figure 8-3 shows this effect, skewing to black. Using a ratio of [127, 255] will have the reverse effect, favoring the color on the left and skewing red in the bottom of Figure 8-3.

Now let’s put these values to work in the following example. Having shown the appearance of linear gradients in Figure 8-3, let’s take a look at radial gradients. This exercise can be found in the radial_gradient_1.fla source file. Lines 1 through 3 create our drawing canvas and Graphics reference, line 10 creates the gradient fill using variables for each parameter, and line 11 draws a square. The heart of the gradient fill spans lines 5 through 8. Line 5 opts for a radial gradient. Line 6 identifies red and black as the gradient’s colors. Line 7 provides an alpha value of 1 (100 percent) for each color. Finally, line 8 weights the colors evenly across the full distance of the gradient.

1    var canvas = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    var gradType:String = GradientType.RADIAL;
6    var colors:Array = [0xFF0000, 0x000000];
7    var alphas:Array = [1, 1];
8    var ratios:Array = [0, 255];
9
10   g.beginGradientFill(gradType, colors, alphas, ratios)
11   g.drawRect(0, 0, 100, 100);

Figure 8-4 shows the resulting gradient fill. To manipulate the gradient as a whole, such as moving the center of a radial gradient, rotating a linear gradient, or scaling a gradient to include more or less of the color span, you must use a matrix—a special kind of number array, which we’ll introduce later in the chapter. Before that, let’s look at gradient line styles, bitmap fills, and bitmap line styles. Then we’ll revisit these topics to see how matrices can alter their appearance.

A radial gradient fill created with the Graphics class
Figure 8-4. A radial gradient fill created with the Graphics class

Gradient line styles

Using a gradient line style is very much like combining a regular line style with a gradient fill. The only difference is that the gradient is applied to the line, not the fill. In fact, lineGradientStyle(), the method for applying a gradient line style, doesn’t even replace the solid-color lineStyle() method. Instead, both methods work together to define a line style and then paint it with a gradient. If you omitted the basic lineStyle() method, no line would appear at all.

The following script, found in line_style_gradient.fla source file, shows this medley in action. Lines 1 through 5 create and position a canvas, as well as create a reference to its graphics property. Line 6 applies a conventional line style, specifying a black, 20-pixel stroke. Lines 8 through 11 define the gradient properties, just as we did in the last example, specifying a linear gradient, from red to black, at full alpha, and evenly distributed between the two colors. Line 13 applies the gradient, also in a similar fashion to the last example, but this time to the line style, not the fill. Once the line is styled, line 14 draws a 200 × 200 rectangle. The effect is illustrated in Figure 8-5.

A linear gradient line style
Figure 8-5. A linear gradient line style
1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    canvas.x = canvas.y = 10;
6    g.lineStyle(20, 0x000000);
7
8    var gradType:String = GradientType.LINEAR;
9    var colors:Array = [0xFF0000, 0x000000];
10   var alphas:Array = [1, 1];
11   var ratios:Array = [0, 255];
12
13   g.lineGradientStyle(gradType, colors, alphas, ratios);
14   g.drawRect(0, 0, 200, 200);

Note

As discussed in the Gradient fills portion of this section, transforming the line gradient also requires a special mathematical construct called a matrix, which we’ll introduce in the upcoming section The Geometry Package.

Using Bitmap Fills and Lines

In addition to applying gradients to fills and lines, you can use bitmaps to decorate your drawing’s fills and lines. Both the beginBitmapFill() and lineBitmapStyle() methods we cover in this section use instances of the BitmapData class. This class handles pixel color and alpha data and allows low-level manipulation of bitmaps. Conveniently, BitmapData is also the data type of bitmaps instantiated from the Flash Professional library using a linkage class. So, any time we need such an instance in the following examples, using a linkage class with an imported bitmap will fit the bill.

Bitmap fills

Using bitmap fills is an easy way to add art to a shape created with the drawing API. Instead of using the beginFill() method, simply substitute beginBitmapFill(). The method requires a BitmapData instance, such as a bitmap from the library, but all remaining parameters are optional. When using the default values, the bitmap will automatically tile. This is very useful for keeping file size down because you can fill large areas with custom bitmap art by using tiles.

Note

In Chapter 13, we’ll discuss how to load external images so you can use bitmaps that haven’t already been imported into an FLA. For now, let’s focus on the syntax required to use bitmaps, no matter where they originate.

In the following example, the 18 × 19 pixel tile in Figure 8-6 has been imported into the bitmap_fill_tiled.fla source file and been given a linkage class of WeaveTile. The following code fills a 200 × 200 rectangle with the tile resulting in what you see in Figure 8-7. Note in line 5 that you must pass in the size of the bitmap you want to use for your fill when creating the BitmapData instance.

Note

Flash Professional CS5 users can omit the width and height values in this usage. This will be discussed further in the next chapter, when covering the BitmapData class in greater detail.

1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    g.beginBitmapFill(new WeaveTile(18, 19));
6    g.drawRect(0, 0, 200, 200);
7    g.endFill();
A bitmap tile
Figure 8-6. A bitmap tile
A tiled bitmap fill
Figure 8-7. A tiled bitmap fill

If you don’t want to tile the bitmap, you need only adjust an optional parameter of the beginBitmapFill() method. Here is the method signature:

beginBitmapFill(bitmap:BitmapData, matrix:Matrix=null,
repeat:Boolean=true, smooth:Boolean=false):void

The first optional parameter is for a matrix, used to rotate, scale, or adjust the location of a bitmap within your shape. We’ll do that a little later in the chapter. We do need to provide a value here, however, to get to the remaining parameters, because the order of parameters is not arbitrary. So, in this case, we’ll pass in null to make no change.

The second parameter controls tiling. By default, its value is true, but you can turn off tiling by setting its value to false. The third optional parameter smoothes the appearance of the bitmap when scaled, softening up the edges. Smoothing can adversely affect performance, so don’t apply it arbitrarily, and usually not to fast-moving sprites.

The following code, found in the bitmap_fill.fla source file, is nearly identical to the last example, only modifying the arguments in the beginBitmapFill() method. It uses a bitmap with a linkage class of Texture, no matrix, turns off tiling, and turns on smoothing to show you an example of the syntax for the optional parameters (all in line 5). The result is shown in Figure 8-8.

A bitmap fill without tiling
Figure 8-8. A bitmap fill without tiling
8    var canvas:Sprite = new Sprite();
9    addChild(canvas);
10   var g:Graphics = canvas.graphics;
11
12   g.beginBitmapFill(new Texture(550, 400), null, false, true);
13    g.drawRect(0, 0, 200, 200);
14    g.endFill();

Bitmap line styles

Applying bitmaps to line styles will likely feel like familiar territory. It’s similar to applying gradients to line styles, in that the basic lineStyle() method is still required to control things like thickness and alpha values for the line. The lineBitmapStyle() method is then used immediately thereafter to apply the bitmap. This method is similar to the beginBitmapFill() method in that it takes the same parameters: a BitmapData instance, a matrix for bitmap manipulation (null by default), and tiling and smoothing options (true and false by default, respectively).

The only drawing API change the following code (found in the line_style_bitmap_tiled.fla source file) makes to the previous examples is substituting a bitmap line style for a fill style. It again uses the WeaveTile linkage class, putting the tile from Figure 8-6 to use one more time. Because tiling is enabled by default, the result of this simple code is seen in Figure 8-9.

A tiled bitmap line style
Figure 8-9. A tiled bitmap line style
1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    canvas.x = canvas.y = 10;
6    g.lineStyle(20, 0x000000);
7    g.lineBitmapStyle(new WeaveTile(18, 19));
9    g.drawRect(0, 0, 200, 200);

When tiling is turned off, you can apply larger bitmaps to line styles for a less geometric effect. The following code uses the previously mentioned Texture bitmap and sets tiling to false. The result is seen in Figure 8-10, and found in the line_style_bitmap.fla source file.

A bitmap line style without tiling
Figure 8-10. A bitmap line style without tiling
9    var canvas:Sprite = new Sprite();
10   addChild(canvas);
11   var g:Graphics = canvas.graphics;
12
13   canvas.x = canvas.y = 20;
14   g.lineStyle(40, 0x000000);
15   g.lineBitmapStyle(new Texture(550, 400), null, false)
16   g.drawRect(0, 0, 510, 360)

Note

One very important thing to remember is that bitmap line styles are new to Flash Player as of version 10.1. Therefore, your viewers must have that version of the player or later for this feature to work.

Simulating the Pencil Tool

A good way to learn interactive drawing is to simulate the functionality of the Flash Professional Pencil tool. As when you use the Pencil tool in Flash, in ActionScript you select a line size and color, move the mouse to the drawing’s starting point, then click and drag to draw. In both cases, you also release the mouse to move to a new location, and then start drawing again.

This process is outlined in the following script from the pencil.fla source file. Lines 1 through 3 prepare our usual canvas, and line 4 initializes a Boolean to keep track of whether the pencil is drawing. Line 6 sets the line style.

Lines 8 through 18 create a trio of listeners: Line 8 is added to the main timeline (the scope of the script) and updates the art every enter frame. Lines 9 through 11 are added to the stage and toggle the drawing Boolean based on the mouse activity. Finally, lines 21 through 25 move the drawing point with the mouse if the mouse button is up, and draw with the mouse if its button is down. Figure 8-11 is a simple graphic drawn with this code.

Art created using the pencil.fla source file
Figure 8-11. Art created using the pencil.fla source file

Note

In lines 9 and 10 of the pencil.fla code, the mouse event listeners are added to the stage because the stage can easily react to mouse events. If you add a mouse event listener to a movie clip (which the main timeline is), the mouse events will register only if you click on visible content within the movie clip. As this example is a simple drawing application that begins with a blank canvas, attaching mouse events to the main timeline would mean that no mouse event would ever be heard.

1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4    var drawing:Boolean = false;
5
6    g.lineStyle(1, 0x000000);
7
8    this.addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
9    stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown,
10                          false, 0, true);
11    stage.addEventListener(MouseEvent.MOUSE_UP, onUp, false, 0, true);
12
13    function onDown(evt:MouseEvent):void {
14        drawing = true;
15    }
16    function onUp(evt:MouseEvent):void {
17        drawing = false;
18    }
19
20    function onLoop(evt:Event):void {
21        if (drawing) {
22            g.lineTo(mouseX, mouseY);
23        } else {
24            g.moveTo(mouseX, mouseY);
25        }
26    }

Drawing Complex Shapes with drawPath()

image with no caption

If you want to push yourself a bit to use many of the skills you’ve learned throughout this book, you can take a sideline and look over this more advanced technique for drawing with vectors. Feel free to skip this section, if you’re still finding your scripting legs. You can always come back to it when you’re more comfortable with ActionScript 3.0.

This exercise is just a form of self-guided study, and introduces a new feature of Flash Player 10.1. Although it can really expand what you can do in combination with other new features discussed on the companion website, there’s nothing here that you can’t put off for now. In essence, all this feature does is allow you to draw a complex shape all at once, having stored the same drawing methods you’ve just learned, and corresponding points, for later recall.

The drawPath() method allows you to build a collection of drawing commands and draw a vector masterpiece all at once. From a comparison standpoint, drawPath() isn’t very different from executing a list of individual drawing API commands. In the simplest terms, it collects moveTo(), lineTo(), and curveTo() commands into a single method, but it does a bit more if you delve deeper.

First, it stores both the commands and data points using the fast, efficient Vector class. An instance of the Vector class is very different from the vectors we’ve been drawing throughout this chapter. Essentially, the ActionScript construct vector is an array and, in most cases, working with a vector will be the same as working with an array. However, vectors are very fast because they are typed arrays. That is, normal arrays can contain a mixture of many data types, making it impossible for the array as a whole to be checked against a single data type. Each vector, on the other hand, can contain only one data type, so the compiler knows right away what the data type of everything in the vector will be. That makes them fast. If you haven’t used vectors yet, take another look at Chapter 2.

The second, and most beneficial feature of the drawPath() method is that you can save the drawing commands and points for later use; you can recall them again and again to draw complex paths without having to rewrite the code every time. The companion website has more information about this process in a series of posts aptly prefixed “The Drawing API.” For now, however, let’s write a function that will collect polygon coordinates and lineTo() commands to draw finished polygons using the drawPath() method. Two example polygons created by the script, a hexagon and a triangle, are shown in Figure 8-12.

Two shapes created with the drawPath() method
Figure 8-12. Two shapes created with the drawPath() method

The following script can be found in the draw_path_polygons.fla source file. Lines 1 through 7 create two canvases into which we will draw a triangle and hexagon, respectively. We’re using two canvases because our function can draw polygons with three or more sides, and the script will demonstrate both a three-sided polygon (triangle) and a six-sided polygon (hexagon).

1    var hexagon:Sprite = new Sprite();
2    hexagon.x = hexagon.y = 100;
3    addChild(hexagon);
4
5    var triangle:Sprite = new Sprite();
6    triangle.x = triangle.y = 200;
7    addChild(triangle);

The drawPolygon() method, which we defined in lines 9 through 33, uses simple math to calculate points on an invisible circle, and then divides the circumference of that circle into equal segments to find the points of a polygon. In other words, if you divided a circle at two equidistant points along its circumference, you’d end up with two points that describe a straight line (the circle’s diameter). If you divided the circumference into three segments, you’d end up with three points that form a triangle, and so on.

Forming polygons by dividing a circle’s circumference into equal sections and then connecting the equidistant points
Figure 8-13. Forming polygons by dividing a circle’s circumference into equal sections and then connecting the equidistant points

The drawPolygon() method takes as its arguments: a sprite to draw into, the radius and number of sides for the polygon, and the starting angle of the first point in the polygon (lines 9 and 10). Line 11 stores the graphics property of the desired canvas so we can draw into it, and line 12 stores the number of points of the polygon we want to draw to. (The number of points is one larger than the number of sides because we have to draw back to the first point again, to close the shape.)

The number of polygon segments determines the amount by which the angle is incremented each time a line is drawn (line 13). A triangle touches our invisible circle three times, so the angle increment is 360/3 or 120 degrees. A hexagon has 6 sides, so its angle is incremented 60 degrees each time a side is drawn (360 / 6 = 60).

The last initialization steps, in lines 14 and 15, create empty vectors to contain the polygon points and commands. Note that the data type of the vector is added to the process, as discussed in Chapter 2. The points of the polygon will be stored in Number format, and the commands will be stored in int (integer) format. Line 16 adds the first drawing instruction, a moveTo(), to the commands vector. The constant MOVE_TO from the GraphicsPathCommand class contains the required integer, making it easier to remember because you don’t have to recall which integer corresponds to which method. If you prefer to use integers to save space, however, moveTo() is 1, lineTo() is 2, and curveTo() is 3.

Lines 17 through 21 determine the point to which the first move is made. They use the basic circle math discussed in Chapter 7 to find the points of the polygon on our invisible circle. The current angle is first converted from degrees to radians (line 17, calling the function in lines 35 through 37), and then the x and y coordinates of the first point are calculated, using cosine and sine respectively, times the radius of the circle (lines 18 and 19). Finally, lines 20 and 21 push the point into the points vector. Note that the x and y values are stored separately and sequentially, rather than as x-y pairs, to take advantage of the speed boost that comes from processing numbers in a vector.

8    //drawing function
9    function drawPolygon(canvas:Sprite, radius:Number,
10                        numSegments:int, angle:Number=0):void {
11       var g:Graphics = canvas.graphics;
12       var numPoints:int = numSegments + 1;
13       var angleChange:Number = 360/numSegments;
14       var points:Vector.<Number> = new Vector.<Number>;
15       var commands:Vector.<int> = new Vector.<int>;
16       commands.push(GraphicsPathCommand.MOVE_TO);
17       var radians:Number = deg2rad(angle);
18       var xLoc:Number = Math.cos(radians) * radius;
19       var yLoc:Number = Math.sin(radians) * radius;
20       points.push(xLoc);
21       points.push(yLoc);

The for loop in lines 22 through 30 repeats this process for every point in the polygon, with two exceptions. First, line 23 increments the angle to determine the location of the next point. For each subsequent point, the lineTo() method is used to draw a line to the point, rather than move there.

The final part of the function sets a line style, and draws the polygon all at once by walking through each command and matching it with corresponding points (lines 31 and 32, respectively).

22        for (var i:int = 0; i < numPoints; i++) {
23            angle += angleChange;
24            radians = deg2rad(angle);
25            xLoc = Math.cos(radians) * radius;
26            yLoc = Math.sin(radians) * radius;
27            commands.push(GraphicsPathCommand.LINE_TO);
28            points.push(xLoc);
29            points.push(yLoc);
30        }
31        g.lineStyle(1, 0x000000);
32        g.drawPath(commands, points);
33    }
34
35    function deg2rad(deg:Number):Number {
36        return deg * (Math.PI/180)
37    }
38
39    drawPolygon(hexagon, 50, 6);
40    drawPolygon(triangle, 50, 3, 270);

The last step in the process occurs in lines 39 and 40 when the function is called. Each time, a minimum of three things is passed to the function: a movie clip canvas into which the art is drawn, the radius of the desired polygon, and the number of sides used to create the polygon. Line 40 demonstrates the optional parameter, dictating the starting position of the polygon’s first point. By default, this is at angle 0, or to the right. (This is determined by the default value of 0 for the angle parameter in line 10.) To align the triangle upward, we must set the starting angle to 270 degrees.

Don’t forget: there are additional discussions related to this process on the companion website. Two more drawing methods, for example, offer slightly modified syntax for the moveTo() and lineTo() drawing commands. They introduce no new functionality, but are designed to require fewer code edits, should you ever need to switch to drawing a curve later on. More importantly, additional features not covered here can be used to store and redraw graphics data again and again. Push yourself to learn and check out the site when you’re ready.

The Geometry Package

Regardless of whether you intend to use a lot of math in your programming, you will probably use more geometry than you think. You’ve already indirectly referenced points on many occasions, and you might also use rectangles for simple tasks like defining an area or checking to see if something is within a given boundary. Fortunately, simple tasks like these do not require that you calculate your own formulas. In fact, preexisting ActionScript classes can even replace some of the manual coding you’re already doing, such as calculating the distance between two points, discussed in Chapter 7.

The flash.geom package contains a handy set of utility classes that help create and manipulate points, rectangles, and other data used to transform the appearance of objects. Here we’ll focus on three of its classes that most closely relate to drawing with code: Point, Rectangle, and Matrix. We’ll also revisit the Geometry package when discussing color in the next chapter.

Note

Neither the Point nor the Rectangle class draws a shape. These classes define virtual points and rectangles for use with other coding needs.

Creating Points

The Point class allows you to reference an x and y coordinate as a single point. An instance of the Point class contains x and y properties, and creating the instance is as easy as using the new operator, just as you’ve done many times so far. Using an empty constructor, as seen in the first line of the following code block, will automatically create a default point of (0, 0). You can reference another location, however, by passing x and y values into the constructor. The first syntax demonstration that follows creates a default point and traces the point’s x and y properties separately. The second demonstration creates a specific point and traces the point as a whole.

var pt:Point = new Point();
trace(pt.x, pt.y);
//0 0

var pt2:Point = new Point(100, 100);
trace(pt2);
//(x=100, y=100)

In addition to its x and y properties, the Point class also has a handful of useful methods to make processing point data easier. These methods allow you to move a point, add or subtract the x and y values of two points, or determine whether two points are the same. It can even calculate the distance from one point to another, or find an interim location between two points.

The following code is found in the points.fla source file. Traces are used throughout the code to show you the immediate results of each instruction. To start, lines 1 and 2 create two points to work with. Line 3 demonstrates the offset() method, moving the point 50 pixels in both the x and y directions.

Lines 6 and 8 demonstrate adding and subtracting points. These methods work on the point’s x and y values independently, creating a new point that is calculated from the sum or difference of the two point coordinates. Line 10 checks to see if two points are the same using the equals() method. This is very handy for conditionals because you don’t have to test for x and y values independently.

1    var pt1:Point = new Point(100, 100);
2    var pt2:Point = new Point(400, 400);
3    pt1.offset(50, 50);
4    trace(pt1);
5    //(x=150, y=150)
6    trace(pt1.add(pt2));
7    //(x=550, y=550)
8    trace(pt2.subtract(pt1));
9    //(x=250, y=250)
10   trace(pt1.equals(pt2));
11   //false

Two very convenient Point methods are distance() and interpolate(), which really simplify animation math. Essentially, distance() performs the work of the Pythagorean theorem discussed in the previous chapter, so you don’t have to do it yourself. The interpolate() method calculates an interim location between two specified points. The method’s third parameter determines how close to either point you want the new location to be. A value closer to 0 is nearer the proximity of the second point; a value approaching 1 is closer to the first point.

12    trace(Point.distance(pt1, pt2));
13    //353.5533905932738
14    trace(Point.interpolate(pt1, pt2, 0.5));
15    //(x=275, y=275)

Note

See the “Using Points and Rectangles” post on the companion website for additional information.

Creating Rectangles

Rectangles are defined in a way similar to defining points, but by using the Rectangle class. Like using point data, creating and manipulating rectangular areas via ActionScript can be very helpful when positioning objects. For example, a rectangle can be used to establish a boundary within which something must remain or occur—such as keeping a movie clip in a corner of the stage. You will also see in the next chapter that rectangles are valuable for defining areas of data—in much the way a marquee selection or cropping tool behaves in a drawing application.

Here’s an example of creating a rectangle, and checking its location, width, and height. The first line of the following snippet shows the order of arguments that must be supplied when instantiating a rectangle. Comparing the sample output comments to this line shows how the properties and values are related.

//Rectangle(x:Number, y:Number, width:Number, height:Number)
var rect:Rectangle = new Rectangle(0, 0, 100, 100);
trace(rect.x, rect.y);
//0 0
trace(rect.width, rect.height);
//100 100
trace(rect);
//(x=0, y=0, w=100, h=100)

Three sets of properties also give you a more granular look at location and dimension values of the rectangle. For example, in the rectangles.fla source file, you’ll find the following script, which shows how to find the rectangle’s location, width, and height, just as you did with the Point class. Line 4 demonstrates the left, top, right, and bottom properties of the rectangle. You can use these properties to check for the location of an edge of a rectangle. Finally, line 6 uses the topLeft and bottomRight properties to retrieve the appropriately named bounding points of the rectangle.

1    var rect:Rectangle = new Rectangle(50, 50, 200, 100);
2    trace(rect.x, rect.y, rect.width, rect.height);
3    //50 50 200 100
4    trace(rect.left, rect.top, rect.right, rect.bottom);
5    //50 50 250 150
6    trace(rect.topLeft, rect.bottomRight);
7    //(x=50, y=50) (x=250, y=150)

As with the Point class, you can move a rectangle with one call to the offset() method (shown in line 9 of the continuing script that follows), instead of changing both the rectangle’s x and y properties. You can also create a larger rectangle by increasing the width and height on all sides surrounding the initial rectangle’s center point. This is accomplished using the inflate() method and is another way of creating a quick frame around a rectangle. The first parameter of this method is added to the location of the rectangle’s left and right dimensions (enlarging the rectangle horizontally), and the second parameter is applied to the top and bottom dimensions (enlarging the rectangle vertically).

8    //offset and inflate
9    rect.offset(10, 10);
10   trace(rect.left, rect.top, rect.right, rect.bottom);
11   //60 60 260 160
12   rect.inflate(20, 20);
13   trace(rect.left, rect.top, rect.right, rect.bottom);
14   //40 40 280 180

Next, you can use a handful of methods to compare rectangles with points and other rectangles. The following code block compares two new rectangles, rect1 and rect2, and a new point, pnt. Lines 19, 21, and 23 determine whether an object is inside a rectangle. Line 19 checks to see whether x and y locations are both inside the rectangle. Line 21 performs the same test, but allows you to pass in a point instead of discreet x and y values. Line 23 checks to see whether an entire rectangle is within another rectangle. These methods can be handy for programming drag-and-drop exercises.

15    //contains
16    var rect1:Rectangle = new Rectangle(0, 0, 100, 50);
17    var rect2:Rectangle = new Rectangle(50, 25, 100, 50);
18    var pnt:Point = new Point(125, 50);
19    trace(rect1.contains(25, 25));
20    //true
21    trace(rect2.containsPoint(pnt));
22    //true
23    trace(rect1.containsRect(rect2));
24    //false

Line 26 of this ongoing example checks to see if two rectangles overlap, and line 28 returns any area shared by both rectangles. Line 30 returns the union of the two specified rectangles—a new rectangle created from the minimum-bounding area that fully encompasses both original rectangles.

25    //intersection and union
26    trace(rect1.intersects(rect2));
27    //true
28    trace(rect1.intersection(rect2));
29    //(x=50, y=25, w=50, h=25)
30    trace(rect1.union(rect2));
31    //(x=0, y=0, w=150, h=75)

These methods can be used in advanced collision detections, drawing tools, and other efforts. For example, you can rule that two objects collide only if a certain degree of overlap is achieved (rather than first contact). This can be determined by checking the size of the resulting intersection.

Because neither the Rectangle nor Point classes create display objects, Figure 8-14 visualizes the rectangles and points discussed. The blue rectangle represents rect1, the yellow rectangle represents rect2, the red dot represents pnt, and the black dot represents the explicit point (25, 25). The green area depicts the new rectangle created by the intersection of rect1 and rect2, and the dashed line depicts the new rectangle created by the union of rect1 and rect2.

Rectangle class methods demonstrated
Figure 8-14. Rectangle class methods demonstrated

Using Matrices

ActionScript offers predefined properties for affecting a display object’s scale, rotation, and x and y locations, all of which are specified individually. However, there are certain types of objects to which these properties do not apply, such as the gradient fill and line style discussed previously and similar bitmap properties we’ll introduce in a moment and cover in the next chapter.

To change these kinds of objects, you must use a matrix. A matrix is basically a special kind of array of numbers, expressed in a grid. It is not a multidimensional array, as the numbers are stored linearly. However, they relate to each other within the matrix in special ways. Matrix elements can be used independently or together to perform complex object transformations. For example, combinations of elements, such as scale and rotation, can be applied at once, and matrices can even be used to achieve effects that are otherwise not possible with individual properties, such as skewing.

You can also use matrices for more advanced operations such as determining where a point ends up after an object has been transformed. In other words, the point (10, 10) near the upper-left corner of a rectangle will not be at point (10, 10) after a 90-degree rotation. The Matrix class can tell you the new location to which that point has moved, or even the change in location between the new and original points.

The Matrix class provides a basic 3 × 3 matrix for use in several transformation processes. Its structure can be seen in Figure 8-15. Built-in Matrix properties a and d affect scaling. Properties b and c will skew (or shear) an object. The tx and ty properties affect x and y location, respectively. Together, elements a, b, c, and d, affect rotation. The last three values in the matrix, u, v, and w, are not used in ActionScript and can be ignored.

Matrix properties
Figure 8-15. Matrix properties

Table 8-1 shows the transformations possible with a matrix. The first column shows the type of transformation, the second column lists related properties and a simplified class method for accomplishing the goal (if one exists), and the third column shows the values that must be adjusted, if you need to do so manually. It is almost always more convenient to use existing methods, or the a, b, c, d, tx, and ty properties, but writing out the matrix explicitly is useful when you want to make several changes at once. Finally, the last column depicts a representative change in an object when the transformation is applied.

Table 8-1. Matrix values and how they transform objects

Transformation

Properties/Methods

Matrix

Result

Identity

Default matrix, null transformation

a, b, c, d, tx, ty

identity()

Translation

Changes position, x and y, respectively, using pixels

tx, ty

translate(tx, ty)

Scale

Scales along the x and y axes, respectively, using percent

a, d

scale(a, d)

Rotation

Rotates, using radians

a, b, c, d

rotate(q)

Skew (Shear)

Skews along the x and y axes, respectively, using pixels

b, c

None. (See the MatrixTransformer note in the Calculating changes in points after transformations section.)

Skewing with matrices

To test this information, let’s use the Matrix class to do something you can’t do with a built-in property or method—skew a display object. The following script, found in the matrix_skew_1.fla source file, creates a rectangle with the Graphics class and then skews it.

To start with, lines 1 through 7 create a translucent green rectangular sprite with a 1-pixel black border and add it to the display list. The function spanning lines 9 through 10, originally discussed in Chapter 7, converts degrees to radians for use with the Matrix skewing code.

1    var rect:Sprite = new Sprite();
2    addChild(rect);
3    var g:Graphics = rect.graphics;
4    g.lineStyle(1, 0x000000);
5    g.beginFill(0x00FF00, 0.4);
6    g.drawRect(0, 0, 100, 50);
7    g.endFill();
8
9    function deg2rad(deg:Number):Number {
10    return deg * Math.PI / 180;
11    }
12
13    var matrix:Matrix = rect.transform.matrix;
14    matrix.c = Math.tan(deg2rad(20));
15    rect.transform.matrix = matrix;

Finally, lines 13 through 15 apply the skewing effect. Line 13 creates a matrix based on the existing object’s matrix, by retrieving the value of the matrix property of the transform object. This makes sure you are starting from any current transformation, whatever that may be. That is, if an object has already been skewed, starting with a default matrix (also called an identity matrix) will effectively reset the prior skew with the new values.

Line 14 sets the c property of the matrix, which skews along the x-axis using the angle specified. It requires radians instead of degrees, so a value of 20 degrees is passed to the conversion function to get back the required radian value. Finally, the matrix is applied to the object’s matrix property in line 15. The result is seen in the top illustration in Figure 8-16.

A sprite skewed with the Matrix class
Figure 8-16. A sprite skewed with the Matrix class

Note that the skew is applied to the bottom edge of the sprite. This is important because if you wanted to give the sprite the appearance that it was slanted right rather than left, you need to compensate with the correct angle. Angles between 90 and 180 degrees and between 270 and 360 degrees will slant an object to the right but it’s easier to use corresponding negative values. The following change to the existing script (indicated in bold) is found in matrix_skew_2.fla and uses −20 degrees instead of 20 degrees, and the result appears in the middle illustration of Figure 8-16.

16    var matrix:Matrix = rect.transform.matrix;
17      matrix.c = Math.tan(deg2rad(-20));
18    rect.transform.matrix = matrix;

Calculating changes in points after transformations

The sprite slants to the right, but because horizontal skewing affects only the bottom edge, the sprite now appears offset to the left. That is, we successfully skewed the object −20 degrees, but it is no longer where we want it to be. To compensate, we can use the occasionally life-saving methods that calculate the change in a point’s location as a result of a transformation. We’ll demonstrate this feature first. Putting aside the correction we’re seeking for a mometnt, let’s trace the new position of a sprite point, as it exists after the skew.

Let’s focus on the original location of the lower-left corner of the rectangle. We knew that to be (0, 50) because the rectangle had a height of 50 and was positioned at (0, 0) (per our drawRect() instruction in line 6). Therefore, we can pass a point of (0, 50) to the transformPoint() method to see the new value of that location:

19    trace(matrix.transformPoint(new Point(0, 50)));

The new point will trace as approximately (18, 50) because the prior point (0, 50) has been skewed to the left. Calculating this change can require fairly involved trigonometry, so this method is very handy.

If we stopped here, we could determine the difference between the two points and change the location of the sprite accordingly. However, there’s already a method that eliminates the need to calculate the offset. The deltaTransformPoint() method determines the change in the before and after locations of a point, rather than the absolute old and new locations.

We know from the prior trace that the lower-left corner of the rectangle has moved approximately 18 points to the left, and did not move vertically. Therefore, passing (0, 50) to the deltaTransformPoint() method will return a point that is approximately (−18, 50). All we need to do is use that information to correct the location of the sprite. We can, therefore, stubtract the x change in the point from the original x, and the sprite will be restored to its original position. Add lines 16 and 17 to the ongoing example (as in the matrix_skew_2.fla source file) and see the bottom illustration in Figure 8-16.

Note

If you’re a Flash Professional user, check out the timesaving MatrixTransformer class. It’s part of the fl.motion package, added to Flash to support recreating timeline animations with ActionScript.

This class makes matrix transformations even easier than the dedicated methods of the Matrix class. For instance, it has getters and setters for every matrix setting and both degrees and radians are supported, eliminating the need to convert angle values before use.

Here’s an example of using the class to skew the mc movie clip 20 degrees:

var matrix:Matrix = new Matrix();
MatrixTransformer.setSkewX(matrix, 20);
mc.transform.matrix = matrix;

That’s easier than transforming points, as described in the “Skewing with matrices” discussion of this chapter. The class can also automatically rotate a display object around any point, rather than just the object’s transformation point. See the “Using the MatrixTransformer Class” post at the companion website for more information.

20    rect.x -= matrix.deltaTransformPoint(new Point(0, 50)).x;

Manipulating gradient fills and line styles

Now that you know a little bit about matrices, you can exert greater control over gradient fills and line styles. The first time we introduced gradient fills, we filled a rectangle with a radial gradient. The center of the fill did not match the center of the object because we couldn’t control the scale of the gradient. Similarly, the scale of the gradient applied to the line style was too large, not revealing full red on the left or full black on the right.

Using matrices, you can control a number of attributes, including the width, height, rotation, and translation of these fills and line styles. To simplify this process for you, Adobe added the createGradientBox() method to the Matrix class. This method allows you to affect all of these properties with a single method call, and accepts these parameters:

createGradientBox(width, height, rotation, tx, ty);

Let’s see how the optional addition of a matrix to the beginGradientFill() method improves our gradient, by starting with the simplest use of the createGradientBox() method. The following code is derived from the prior radial gradient example and is found in the radial_gradient_2.fla source file. We’ve created a matrix in line 10, and then used the createGradientBox() method in line 11 to set the size of the matrix to match the size of the sprite. Finally, we added that matrix to the fill creation in line 12.

1    var canvas = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    var gradType:String = GradientType.RADIAL;
6    var colors:Array = [0xFF0000, 0x000000];
7    var alphas:Array = [1, 1];
8    var ratios:Array = [0, 255];
9
10    var matrix:Matrix = new Matrix();
11    matrix.createGradientBox(100, 100, 0);
12    g.beginGradientFill(gradType, colors, alphas, ratios, matrix);
13
14    g.drawRect(0, 0, 100, 100);

Figure 8-17 shows the original look of the gradient (top) and its appearance after matching its size to that of the rectangle. After the transformation, the radial gradient is now entirely visible.

A radial gradient before (top) and after (bottom) matrix transformations
Figure 8-17. A radial gradient before (top) and after (bottom) matrix transformations

By adding translation values to the method, you can also now reposition the center of the gradient. For example, using 30 pixels for tx and ty would place the epicenter of the gradient in the lower-right corner of the rectangle, demonstrated in the radial_gradient_3.fla source file.

15    var matrix:Matrix = new Matrix();
16   matrix.createGradientBox(100, 100, 0, 30, 30);

To demonstrate the rotation of a gradient, we’ll change the script in two small ways. First, we’ll switch the gradient type from radial to linear so the rotation is more noticeable (line 5). Then we’ll send a rotation value into the createGradientBox() method (line 11). The degree-to-radian conversion function rounds out the changes in lines 16 through 18 of the following script. Figure 8-18 shows before and after rotating a linear gradient 90 degrees. This code can be found in the linear_gradient_matrix.fla source file.

A linear gradient after (top) and before (bottom) rotation with the Matrix class
Figure 8-18. A linear gradient after (top) and before (bottom) rotation with the Matrix class
17    var canvas = new Sprite();
18    addChild(canvas);
19    var g:Graphics = canvas.graphics;
20
21    var gradType:String = GradientType.LINEAR;
22    var colors:Array = [0xFF0000, 0x000000];
23    var alphas:Array = [1, 1];
24    var ratios:Array = [0, 255];
25
26    var matrix:Matrix = new Matrix();
27    matrix.createGradientBox(100, 100, deg2rad(90));
28    g.beginGradientFill(gradType, colors, alphas, ratios, matrix);
29
30    g.drawRect(0, 0, 100, 100);
31
32    function deg2rad(deg:Number):Number {
33       return deg * (Math.PI / 180);
34    }

Note

The linear_gradient_matrix.fla source file contains additional code to create a second box with a gradient that is not rotated. Comparing the boxes next to each other, as seen in Figure 8-18, will show the effect of the matrix manipulation.

Adjusting gradient line styles

The same matrix adjustment can improve the gradient line style example from earlier in the chapter. The following example, from the line_style_gradient_matrix.fla source file, uses the changes indicated in bold to display the full size of the gradient—exposing full red and full black at the left and right sides, respectively. The result appears in Figure 8-19, which can be compared with Figure 8-5 to see the change.

A gradient line style transformed with a matrix to show the full range of colors in the gradient. Compare with .
Figure 8-19. A gradient line style transformed with a matrix to show the full range of colors in the gradient. Compare with Figure 8-5.
1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    canvas.x = canvas.y = 10;
6    g.lineStyle(20, 0x000000);
7
8    var gradType:String = GradientType.LINEAR;
9    var colors:Array = [0xFF0000, 0x000000];
10   var alphas:Array = [1, 1];
11   var ratios:Array = [0, 255];
12
13    var matrix:Matrix = new Matrix();
14    matrix.createGradientBox(200, 200, 0);
15
16    g.lineGradientStyle(gradType, colors, alphas, ratios, matrix);
17   g.drawRect(0, 0, 200, 200);

Adjusting bitmap line styles

So far, we’ve adjusted the size of gradients to improve their appearance in fills and line styles. Now let’s look at using a matrix to translate the location of a bitmap line style or fill. When a bitmap tiles, it initially tiles relative to a global positioning point. That is, point (0, 0) of your tile won’t necessarily line up with point (0, 0) of your object. The following code, found in line_style_bitmap_tiled_heart.fla, uses a heart tile to fill a 20-pixel line. The initial result, shown in Figure 8-20, demonstrates what can happen when the tile and object don’t initially line up properly.

Bitmap line style with no transformation matrix
Figure 8-20. Bitmap line style with no transformation matrix
1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    canvas.x = canvas.y = 10;
6    g.lineStyle(20, 0x000000);
7
8    g.lineBitmapStyle(new HeartTile(20, 19));
9    g.drawRect(0, 0, 200, 209);

However, we can use a matrix to translate the x and y coordinates of the bitmap so that it better matches our shape. The following adjustments appear in the line_style_bitmap_tiled_heart_matrix.fla source file. The changes to the previous script add a matrix (line 9), use the translate() method to move the bitmap 10 pixels to the left and 9 pixels up, and then apply the matrix when creating the line’s bitmap style (line 11). (To prevent this particular tile from showing an extra pixel at the bottom, we also reduced the height of the rectangle. Be prepared to fiddle with your values a bit, to achieve your goal.) The result can be seen in Figure 8-21.

Bitmap line style corrected with transformation matrix
Figure 8-21. Bitmap line style corrected with transformation matrix
10    var matrix:Matrix = new Matrix();
11    matrix.translate(−10, −9);
12
13    g.lineBitmapStyle(new HeartTile(20, 19), matrix);
14    g.drawRect(0, 0, 200, 209);

Gradient Spread Method

For our last word on gradients, let’s talk about the available gradient spread methods. Using these options, you can control the way a gradient behaves when it fills an area larger than its own dimensions. In Flash Professional’s Color panel this feature is called overflow (or flow in version CS5), but in ActionScript it is called the spread method. The default behavior is extend in the Color panel, which is called pad in ActionScript—specified by the SpreadMethod.PAD constant. This setting continues the last color in the gradient throughout the remaining visible area to which the gradient is applied. This can be seen in all prior figures depicting gradients, as well as in the first illustration of Figure 8-22.

Note

The change in nomenclature for the gradient fill spread method was required because overflow and extend both have important separate meanings in ActionScript.

The other two ActionScript options, SpreadMethod.REFLECT and SpreadMethod.REPEAT, share the same names and functionality with the Color panel. The former reverses the colors as many times as is needed to occupy the available space filled by the gradient, as if the gradient was held against a mirror. The latter fills the visible area in a similar fashion but starts over at the first color as if tiled. Figure 8-22 shows these effects in the middle and bottom illustrations, respectively.

Gradient fill spread method options pad (top), reflect (middle), and repeat (bottom)
Figure 8-22. Gradient fill spread method options pad (top), reflect (middle), and repeat (bottom)

To control this feature, we must add another optional parameter to the beginGradientFill() call. The following code is found in spread_method.fla, and is based on the code from the linear_gradient_matrix.fla source file. The changes in bold, reflect the gradient. Commented lines are included for testing the pad and repeat options, as well. You can switch the comments to see the varying results.

Remember that a gradient needs to spread only when it is smaller than the canvas it is trying to fill. Therefore, this example reduces the width and height of the gradient using the createGradientBox() method to show the effect in action. If both the gradient and rectangle were 100 × 100 pixels, no spreading would occur.

1    var canvas:Sprite = new Sprite();
2    addChild(canvas);
3    var g:Graphics = canvas.graphics;
4
5    var gradType:String = GradientType.LINEAR;
6    var colors:Array = [0xFF0000, 0x000000];
7    var alphas:Array = [1, 1];
8    var ratios:Array = [0, 255];
9
10    var matrix:Matrix = new Matrix();
11    matrix.createGradientBox(50, 50, deg2rad(90), 0, 0);
12
13     //var spread:String = SpreadMethod.PAD;
14     var spread:String = SpreadMethod.REFLECT;
15     //var spread:String = SpreadMethod.REPEAT;
16     g.beginGradientFill(gradType, colors, alphas, ratios, matrix, spread);
17
18    g.drawRect(0, 0, 100, 100);
19
20    function deg2rad(deg:Number):Number {
21        return deg * (Math.PI / 180);
22    }

9-Slice Scaling

Scaling vectors is usually a pleasure because the crispness of the vector art isn’t lost when it’s resized. This is because the vectors are recalculated every time an object is scaled. However, one of the downsides of this default behavior is that certain visual characteristics, such as stroke weight and rounded corners, can become distorted during scaling. This phenomenon can be seen in the top two illustrations of Figure 8-23.

To reduce distortion caused by scaling in many types of display objects, you can use a feature called 9-slice scaling. This feature virtually slices a display object into nine pieces and controls scaling of these pieces independently. A typical grid of nine slices can be seen in Figure 8-23, marked with “9-slice scaling enabled.” The four corners are not scaled. The top and bottom slices between the corners are scaled only horizontally, the left and right slices between the corners are scaled only vertically, and the center slice is scaled in both directions.

9-Slice scaling reduces distortion during scaling
Figure 8-23. 9-Slice scaling reduces distortion during scaling

To enable this feature using ActionScript, you must set the corresponding scale9grid property to a rectangle that, in essence, defines the object’s center slice. ActionScript then extrapolates the corners and perimeter slices by extending the sides of the virtual rectangle. The aforementioned “9-slice scaling enabled” illustration in Figure 8-23 shows this by darkening the center rectangle and outlining the slices with dashed lines. To demonstrate this feature, the following exercise, found in the scale9.fla source file, will create a sprite with rounded corners and then scale it using the mouse.

Lines 1 through 9 follow our familiar routine of creating a sprite, drawing vector assets, and positioning and adding the sprite to the display list. However, there’s one new twist to this process. The lineStyle() method in line 6 contains an optional parameter we haven’t discussed. The third parameter tells the method to give the line an alpha value of 100 percent. This parameter was discussed in the Drawing Shapes section of the chapter when we overlapped a 50-percent fill and a 50-percent line. (See the circle in Figure 8-1.) An alpha value of 1 is the default behavior, but we need to include it here to add our fourth parameter. (It’s not possible to vary the order in which parameters are supplied to this method, so the first three must be present to use the fourth.)

The fourth parameter enables stroke hinting, which aligns strokes along whole pixels, improving legibility. Specifically, this parameter reduces the apparent loss of stroke thickness due to anti-aliasing and improves the look of rounded corners, which is central to this exercise.

Note

It is possible to slice a display object into a different number of slices by repositioning the slice-defining rectangle, but unpredictable results may occur.

1    var canvas:Sprite = new Sprite();
2    canvas.x = canvas.y = 50;
3    addChild(canvas);
4
5    var g:Graphics = canvas.graphics;
6    g.lineStyle(1, 0x000000, 1, true);
7    g.beginFill(0xFFFF00, 0.5);
8    g.drawRoundRect(0, 0, 100, 50, 15);
9    g.endFill();
10
11    var slice9rect:Rectangle = new Rectangle(15, 15, 70, 20);
12    canvas.scale9Grid = slice9rect;
13
14    addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
15    function onLoop(evt:Event):void {
16        canvas.width = Math.max(mouseX - canvas.x, 30);
17        canvas.height = Math.max(mouseY - canvas.y, 30);
18    }

Lines 11 and 12 create a rectangle that is inset from all four sides of the sprite by 15 pixels, and sets the scale9Grid property of the sprite to the specified rectangle. An inset of 15 pixels is just enough to ensure that the rounded corners of the rectangle are positioned in the four corners of the grid, thus preventing scaling.

Note

Remember that providing left-, top-, right-, and bottom-edge coordinates does not specify a Flash rectangle. Instead, the x and y coordinates of the upper-left corner, width, and height of the rectangle are specified. So, a rectangle that insets 15 pixels from a 100 × 50-pixel sprite, must start at the sprite’s point 15, 15, and have dimensions of 70 × 20 pixels.

Finally, an event listener calls the onLoop() function every enter frame, resizing the sprite based on the mouse location. Lines 16 and 17 set the width and height, respectively, of the sprite to the mouse coordinates minus 50, which are the x and y values of the sprite assigned in line 2. So, if the mouse is at point (150, 150), the sprite will have a size of 100 × 100.

One new element, introduced in lines 16 and 17, limits how small the rectangle can become. The max() method of the Math class determines which of the two values provided to it is larger and uses that value. Therefore, if the distance of the mouse from the sprite registration point’s x or y value is greater than 30, that value is used. Conversely, if the mouse is closer than 30 pixels to the sprite’s registration point, 30 will be used. This allows the rectangle to scale but prevents it from getting any smaller than 30 × 30 pixels.

If you want to see a live comparison between using and not using 9-slice scaling, add the bold lines in the following code to your script, or see the source file, which already includes this code. Every time you click the mouse, the feature toggles between on and off by alternately applying or removing the rectangle to the sprite’s scale9Grid property.

19    //switch between default and 9-slice scaling
20    function onLoop(evt:Event):void {
21        canvas.width = Math.max(mouseX - sp.x, 30);
22        canvas.height = Math.max(mouseY - sp.y, 30);
23    }
24
25   stage.addEventListener(MouseEvent.CLICK, onClick, false,
 0, true);
25    function onClick(evt:Event):void {
27        if (canvas.scale9Grid) {
28            canvas.scale9Grid = null;
29        } else {
30            canvas.scale9Grid = slice9rect;
31        }
32    }

Applied Examples

image with no caption

Now let’s use much of what we’ve covered in this chapter in two applied examples. In the first exercise, we’ll create the artwork for a basic color picker. Then we’ll create a custom button tool that can serve as a lightweight, code-only alternative to components. In both cases, let’s build the examples in classes to practice using object-oriented programming.

Starting a Color Picker

Let’s start by writing a class that will build the display portion of a simple color picker. In the next chapter, we’ll show you how to retrieve values from the picker using your mouse. To preview this exercise, test the color_picker_graphics_example.fla source file, which simply instantiates the class we’re about to discuss, and adds it to the display list.

The picker will contain two separate pieces: a color spectrum in vertical blended stripes, and a transparent-to-black gradient overlay, as seen in Figure 8-24. The overlay will allow you to vary how much black is added to a color.

Two layers of the color picker
Figure 8-24. Two layers of the color picker

Note

Creating the two layered gradients for the picker requires the same code with only minor variance in some of the settings. Therefore, it makes sense to define a method to handle the work without a lot of repetition. This way, we can vary the parameters sent to the method and create multiple gradients with the same code.

First we’ll create the color spectrum and add it to the display list. Then we’ll create the transparent-to-black overlay and add it to the display list. Adding it after the color spectrum gradient will position it on top. Because both sprites will be added to the class, all you need to do is add an instance of the finished class to the display list of your project and your color picker artwork will be self-contained and ready for the functional enhancements planned in Chapter 9.

Now take a look at the following code. Lines 1 through 10 cover the basic syntax found in many classes. Line 1 defines the package, including a package location. This means that the ColorPickerGraphics class will be found inside a directory called color, which is inside learningactionscript3, which is inside com.

Note

The reverse domain naming convention is discussed in Chapter 1 and Chapter 6.

Lines 3 through 6 import the necessary classes, line 8 defines the class, and line 10 defines the constructor that will be executed immediately when the class is instantiated. Note in line 8 that the class extends MovieClip. This means that this class will inherit the public and protected properties and methods found in MovieClip. It also means that we can add the class to the display list as if it were a movie clip itself.

Our gradient method requires arrays for colors, alpha values, and color ratios, as previously described. The colors array includes red, yellow, green, cyan, blue, purple, and red again. The alphas array contains a 1 for every color, rendering each step in the gradient at full alpha. The ratios array evenly distributes each color across the 0–255 span, without weighting any one color over another.

Note

Because the artwork is created in the constructor, and the data is passed to the gradient method through parameters, there is no need for these values to be stored in class properties.

The spectrum is next created and added to the display list in lines 18 through 20. The process is then repeated for the overlay. The overlay includes two evenly distributed colors, black at 0 percent alpha, and black at 100 percent alpha (lines 22 through 24). It’s then created and added to the display list. We’ll explain the calls to drawGradientBox(), in lines 18 and 26 when we discuss the method. Review the following code and then we’ll look at the method that creates the artwork.

1    package com.learningactionscript3.color {
2
3        import flash.display.Sprite;
4        import flash.display.GradientType;
5        import flash.geom.Matrix;
6        import flash.display.Graphics;
7
8        public class ColorPickerGraphics extends Sprite {
9
10           public function ColorPickerGraphics() {
11
12               var colors:Array = [0xFF0000, 0xFFFF00, 0x00FF00,
13                                   0x00FFFF, 0x0000FF, 0xFF00FF,
14                                   0xFF0000];
15               var alphas:Array = [1, 1, 1, 1, 1, 1, 1];
16               var ratios:Array = [0, 42, 84, 126, 168, 210, 255];
17
18               var spectrum:MovieClip = drawGradientBox(100, colors,
19                                                        alphas, ratios);
20               addChild(spectrum);
21
22               colors = [0x000000, 0x000000];
23               alphas = [0, 1];
24               ratios = [0, 255];
25
26               var overlay:Sprite = drawGradientBox(100, colors,
27                                                    alphas, ratios,
28                                                    deg2rad(90));
29               addChild(overlay);
30           }

In addition to the aforementioned arrays, the method also requires a size for the artwork (100 for both components) and, optionally, a rotation (90 degrees, in the case of the overlay, sent to the method in line 28). The rotation value can be omitted from the method call that creates the spectrum (lines 18 and 19), not just because it isn’t needed, but also because the matrixRotation parameter of the method (line 34) has a default value.

Note

Because the rotation angle is easier to specify in degrees, the value is converted to radians using the function at the end of the class.

Lines 35 and 36 create a sprite and reference to its graphics property, but the movie clip is not yet added to the display list. Instead, it is returned by the method in line 48 and added to the display list by the constructor, as discussed previously.

Because the gradient data is sent to the method through its parameters, all that remains in lines 38 through 41 is to specify a linear gradient, create the matrix, and modify the matrix with the specified size and rotation, if any. The matrix is then applied using the createGradientBox() method (lines 40 and 41), a 1-pixel black line is specified in line 43, and all the gradient values are passed to the beginGradientFill() method in lines 44 and 45. Finally, lines 46 through 48 draw the rectangle, close the fill, and return the sprite to the constructor.

31        //creating the gradient artwork
32        private function drawGradientBox(size:Number, colors:Array,
33                  alphas:Array, ratios:Array,
34                  matrixRotation:Number=0):Sprite {
35            var canvas:Sprite = new Sprite();
36            var g:Graphics = canvas.graphics;
37
38            var fill:String = GradientType.LINEAR;
39            var matrix:Matrix = new Matrix();
40            matrix.createGradientBox(size, size,
41                                     matrixRotation, 0, 0);
42
43            g.lineStyle(1, 0x000000);
44            g.beginGradientFill(fill, colors, alphas,
45                                ratios, matrix);
46            g.drawRect(0, 0, size, size);
47            g.endFill();
48            return canvas;
49        }
50
51        private function deg2rad(deg:Number):Number {
52            return deg * (Math.PI/180);
53            }
54       }
55   }

To add this first step of our color picker to your project, all you need to do is create an instance of the class and add it to the display list. The source file color_picker_graphics_example.fla has already been created for this purpose.

import com.learningactionscript3.color.ColorPickerGraphics;

var picker:ColorPickerGraphics = new ColorPickerGraphics();
addChild(picker);

Don’t forget that this example just demonstrates the dynamic creation of the picker. (No assets—all code!) In the next chapter, we’ll show you how to retrieve color values from the picker so you can use it in your own projects.

A Custom Button Class

The next applied example is a class that creates functioning buttons entirely with code, and it’s based on your work with the Graphics class in this chapter. The RoundRectButton_example.fla source file shows the class at work. This example introduces two new concepts. The first is the use of the SimpleButton class, which allows you to dynamically create traditional buttons that have up, over, down, and hit states, as well as cursor feedback. As such, they will behave just like buttons you create manually on the stage using Flash Professional’s drawing and symbol tools. The button’s behavior is simulated in Figure 8-25, showing not only cursor feedback in the over and down states (middle and bottom) showing up, over, and down states, but also the use of a brighter color in the over state, and darker color in the down state.

A custom button created by the RoundRectButton class
Figure 8-25. A custom button created by the RoundRectButton class

This color change is a result of the other new concepts discussed in this example: the ability to automatically interpolate a color that falls between two given color values. For example, given red and blue, the code will return purple. This is accomplished through the Color class, which is part of the fl.motion package.

Note

As mentioned previously, the Color class is available to Flash Professional users only. However, we have reproduced the functionality in the com.learningactionscript3.color.ColorUtils class for users of other ActionScript editors. The sample source code for this chapter includes notes on its use.

Our custom button class starts with the standard package syntax through line 19, declaring the package, importing classes, and declaring the class and class properties. Note, again, the custom package path. See the introduction to “Starting a Color Picker” earlier in this chapter for more information.

1    package com.learningactionscript3.ui {
2
3        import flash.display.Graphics;
4        import flash.display.MovieClip;
5        import flash.display.Shape;
6        import flash.display.SimpleButton;
7        import flash.text.TextField;
8        import flash.text.TextFieldAutoSize;
9        import fl.motion.Color;
10
11       public class RoundRectButton extends MovieClip {
12
13           private var _w:Number;
15           private var _h:Number;
16           private var _rad:Number;
17           private var _linW:Number;
18           private var _col:uint;
18           private var _txt:String;
19           private var _txtCol:uint;

The constructor begins with lines 21 through 31, populating the class variables with the parameter values passed in when instantiating the class. These include values for width, height, corner radius, line weight, color, text, and text color. It follows with the creation of a button and text field (both of which we’ll discuss in just a moment), and adding both to the display list of the class instance.

20        //constructor
21        public function RoundRectButton(w:Number, h:Number,
22                                        rad:Number, linW:Number,
23                                        col:uint, txt:String,
24                                        txtCol:uint){
25            _w = w;
26            _h = h;
27            _rad = rad;
28            _linW = linW;
29            _col = col;
30            _txt = txt;
31            _txtCol = txtCol;
32
33            var btn:SimpleButton = createBtn();
34            addChild(btn);
35            var labl:TextField = createLabel();
36            addChild(labl);
37        }

The createBtn() method assembles the button using the SimpleButton class. The createBtn() method calls createRoundRect() (reviewed in just a moment) to create a shape that serves as the background for each button state. The latter method requires only one parameter, which is the color used for the background shape.

We determine these colors in lines 40 and 41 using the static method interpolateColor() from the Color class. Given two colors, the method calculates a color between the two. A third parameter indicates how close to either color the new value should be. For example, if you provided black and white and a weighting of 0.1, the new color would be closer to the first, or a charcoal gray. A weighting of 0.9 would be closer to the second color, or near white.

Note

Static methods are called from the class, not an instance of the class. As such, the new keyword is not used to create an instance before invoking the method.

To create the over-state color (lines 40 through 42), we calculate a value 30 percent between the main button color (visible in the button’s up state) and white. To determine the down state color (lines 43 through 45), we calculate a value 30 percent between the main button color and black. Accordingly, the over state is lighter than the up state, and the down state is darker than the up state. After each state is added to the SimpleButton instance (lines 47 through 50), the button is returned to the constructor.

38    //create all button states
39    private function createBtn():SimpleButton {
40        var ovCol:uint = Color.interpolateColor(_col,
41                                                0xFFFFFF,
42                                                0.3);
43        var dnCol:uint = Color.interpolateColor(_col,
44                                                0x000000,
45                                                0.3);
46        var btn:SimpleButton = new SimpleButton();
47        btn.upState = createRoundRect(_col);
48        btn.overState = createRoundRect(ovCol);
49        btn.downState = createRoundRect(dnCol);
50        btn.hitTestState = btn.upState;
51        return btn;
52    }

The createRoundRect() method (lines 54 through 62) presents no new material, but reviews an idea discussed in Chapter 4 about display lists. Notice that the method returns a shape instead of a sprite or movie clip. You can now create shapes with code, an improvement over prior versions of ActionScript. Unlike sprites and movie clips, shapes don’t support interactivity like mouse event listeners. However, they do require fewer resources to create. Because these shapes will be used inside a SimpleButton instance, which provides all the necessary interactivity, they are well suited for this situation.

53        //create background shape for button states
54        private function createRoundRect(col:uint):Shape {
55            var rRect:Shape = new Shape();
56            var g:Graphics = rRect.graphics;
57            g.lineStyle(_linW, _col);
58            g.beginFill(col, 0.5);
59            g.drawRoundRect(0, 0, _w, _h, _rad);
60            g.endFill();
61            return rRect;
62       }

Finally, the createLabel() method in lines 64 through 74 adds text to the button. Line 65 creates the text field, line 66 sets the width of the field to the width of the button, and line 67 sets the y location to slightly above the midpoint of the button, centering the text vertically. The text is centered horizontally in line 68, and line 69 sets the color of the text to the value passed into the class during instantiation. Finally, the specified button’s text is added to the field in line 70, all mouse interaction with the field is disabled in line 71, and the field is returned to the constructor in line 73.

Note

As discussed in prior chapters, disabling mouse interaction with the text field is vital because if this step is omitted, the field will interfere significantly with the operation of the button. The cursor will change to an I-beam text editing cursor, the text will be selectable, and the field will intercept mouse events.

63        //create text overlay for button
64        private function createLabel():TextField {
65            var txt:TextField = new TextField();
66            txt.width = _w;
67            txt.y = _h / 2 - 6;
68            txt.autoSize = TextFieldAutoSize.CENTER;
69            txt.textColor = _txtCol;
70            txt.text = _txt;
71            txt.mouseEnabled = false;
72
73            return txt;
74            }
75        }
76    }

Using this class is one way to present interface buttons to the user without having to precreate them in the Flash Professional interface. This restricts the ability to use custom artwork for individual buttons but keeps file size to a minimum. This is a simple demonstration, so the class is not very feature-rich when it comes to button styling options. However, that fact presents an ideal opportunity for you to practice what you’ve learned. Try to improve on this class by drawing specialized button shapes or, perhaps, by offering a choice between circular, rectangular, or rounded button shapes.

What’s Next?

Manipulating visual assets with ActionScript is one of the most fun and most satisfying ways to learn the language. Drawing vectors does more than minimize file size. It also provides nearly limitless possibilities for creating generative art. Combining data from other corners of the ActionScript world (user input, sound, mathematical calculations, random numbers, and so on) with vectors opens the door to compelling and instructional experiments. Vectors, however, are only half of the puzzle. Flash also provides an impressive range of classes for manipulating pixel-based assets at runtime.

In the next chapter, we’ll look at working with bitmaps, including:

  • Drawing bitmaps at runtime

  • Applying blend modes such as lighten, screen, and Flash-specific options

  • Using simple filters like drop shadow, bevel, and blur, to enhance assets

  • Using complex filter techniques like convolution, color mixing, and displacement maps for special effects

  • Encoding custom bitmap data and saving those graphics to your hard drive

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.186.248