Chapter 12. How Do I Draw with Code?

12.0 Introduction

In addition to importing assets, or creating them in the Flash authoring environment, you can include assets in your projects by drawing them dynamically with ActionScript at runtime. Much of the last half of this book will take advantage of this approach to minimize the number of custom assets required, and let you generate content exclusively with code. For that reason, this section starts off with a brief introduction to drawing with code.

A code-only approach doesn’t easily offer the artistic range afforded if you can use imported or hand-drawn assets, but significant tradeoffs include increased flexibility and reduced file size. Indeed, an entirely new creative horizon becomes available with code-generated art, and using ActionScript combined with previously created assets is, of course, the best of both worlds.

You have two primary methods of drawing with code: manipulating vectors with the Graphics class, and manipulating pixels with the BitmapData and/or related classes. This chapter will primarily focus on the former, but will also discuss a few simple pixel-based techniques, such as bitmap caching and basic filters.

Note

To effectively demonstrate some of the concepts in this chapter, you need to use a display object or two. Part I and Part II of this book covered the creation and use of display objects throughout, and you’ll look at some of those concepts again in Chapter 13. For clarity, however, the first recipe in this chapter reviews the process of creating a display object into which you can draw, since you need a display object for the subsequent recipes.

12.1 Creating a Display Object Dynamically

Problem

You want to create a display object that will serve as an empty canvas for drawing.

Solution

Use the new keyword to create an instance of a class that is part of the DisplayObject class hierarchy, and then add it to the display list.

Discussion

Display objects and the display list are covered in more detail in Chapter 13. However, in case you haven’t yet read the first half of this book, you’ll find it handy now if you know just enough about the display list to create an empty display object to serve as a canvas on which you can draw with code. The combination of creating a new display object and drawing on it with the techniques discussed here lets you create code-only movie clips, sprites, buttons, and more.

All display objects can be created using the new keyword. The following two examples create a movie clip and sprite, respectively:

var mc:MovieClip = new MovieClip();
var sp:Sprite = new Sprite();

Neither the movie clip nor the sprite, however, is ever visible to the user unless you add them to the display list. To add a display object to the display list, use the addChild() method. The following, for example, adds the previously created sprite to the display list:

addChild(sp);

In this scenario, the sprite would be visible because you added it to the display list, but the movie clip would not be visible. (So far, the sprite has no content but is still technically viewable as an empty canvas. The remainder of this chapter will show how to draw into the sprite.) The movie clip, however, cannot be seen even if it already has content, until you add it to the display list.

See Also

13.2 Creating a New Display Object for more information on creating a display object.

12.2 Referencing an Object’s Graphics Property

Problem

You want to efficiently reference the graphics property of a display object, such as a sprite or the main timeline, to use as the target for vector-based drawing.

Solution

Store a reference to the graphics property in a variable.

Discussion

To draw with vectors, you must first reference the graphics property of a display object. Then you can access the methods and properties of the Graphics class, which is responsible for dynamic vector drawing. You can reduce code length and improve performance by storing a reference to the property in a variable, and using that variable thereafter where you would otherwise have referenced the property directly.

If you want to draw into a display object, for example, such as the sprite sp you created in 12.1 Creating a Display Object Dynamically, you can reference its graphics property like so:

var g:Graphics = sp.graphics;

Thereafter, you need only refer to g when using a method or property of the Graphics class. You can also reference the graphics property of the main timeline with the following:

var g:Graphics = this.graphics;

This line of code assumes you are writing the script in the main timeline, so the this keyword refers to the correct display object: the main timeline itself.

Note

While it’s a matter of personal coding preference, drawing directly on the main timeline isn’t usually as useful as being able to contain your drawing in a display object you can manipulate later.

12.3 Defining a Line Style

Problem

You want to specify the appearance of a line you wish to draw.

Solution

Use the lineStyle() method of the Graphics class to define line attributes.

Discussion

The last line of the following code block sets any lines subsequently drawn into the display object sp to be of 1-pixel thickness and blue in color. The line settings for the Graphics object g remain in effect until changed or reset.

var sp:Sprite = new Sprite();
addChild(sp);
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);

The first parameter is thickness, a pixel value, with 0 being a hairline. The second parameter is the color of the line.

You also have additional optional parameters that closely mimic the Property inspector settings. These parameters include alpha, pixelHinting (also called stroke hinting, a Boolean to determine if the lines are drawn on the whole pixel), scaleMode (to determine if and how lines are scaled when the parent container is scaled), caps (specifying type of line end cap used), joints (specifying type of joint type used), and miterLimit (determining how sharp corners appear).

If you want to clear everything previously drawn into the display object, you can use the clear() method of the Graphics class. However, because a line isn’t drawn when a pixel thickness isn’t specified, you can also clear just the line attributes by passing no parameters to the lineStyle() method.

g.lineStyle();

See Also

12.6 Defining a Fill Style for a demonstration of using multiple lineTo() commands to create a triangle.

12.4 Drawing a Line

Problem

You want to draw a line from point A to point B.

Solution

Use the moveTo() and lineTo() methods of the Graphics class.

Discussion

Drawing a line works the same way it does with pen and paper. First, you place your pen at the first point of the line. If you don’t take this first step in Flash, the line begins at the origin (0, 0) of the display object, such as the registration point of a parent sprite or even the top-left corner of the Flash stage. Also like pen and paper, combining moveTo() and lineTo() method calls in your scripts lets you draw complex shapes without creating one continuous line.

To draw a line on the stage from point (100, 100), to point (300, 100), use the following code:

var g:Graphics = this.graphics;
g.lineStyle(1, 0x0000FF);
g.moveTo(100,100);
g.lineTo(300,100);

In most cases, you have greater control if you draw into a container display object so you can easily manipulate your sprite or movie clip as a whole. When drawing onto the sprite or movie clip, the registration point of assets drawn into the display object is (0, 0), regardless of the display object’s x and y coordinates on the stage, because the drawn assets inside are relative to the display object’s coordinate space, not that of the stage. So, you’re better off achieving the previous goal of drawing a line on the stage from (100, 100) to (300, 100) by drawing a line from (0, 0) to (200, 0), and positioning the display object.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);
g.lineTo(200,0);

Note

In this case, because you want the line to begin from the relative origin point of (0, 0), you can omit the moveTo() method call.

To demonstrate the ease with which you can now manipulate the drawn assets as a whole, the following line will rotate the container sprite 45 degrees, thus rotating the line inside.

sp.rotation = 45;

See Also

12.6 Defining a Fill Style for a demonstration of using multiple lineTo() commands to create a triangle.

12.5 Drawing a Curve

Problem

You want to draw a curve from point A to point B.

Solution

Use the curveTo() method of the Graphics class.

Discussion

Drawing a curve is similar to drawing a line, in that a curve is drawn from the current drawing point to a new point. However, the curveTo() method adds a third point, called a control point or handle, to shape the curve. ActionScript creates curves that use one control point for two points. This trait is in contrast to many drawing applications, such as Adobe Illustrator, which uses one or more control points for every point.

Placing the control point is an important part of determining the shape of your curve. For example, consider turning the line from 12.4 Drawing a Line into a concave arc, resembling a smile. To pull the curve down in the middle, you might select a control point halfway between and below the two end points. Therefore, if the line spans from (0, 0) to (200, 0), one possible choice for a control point is (100, 100). These x and y coordinates are passed to the curveTo() method as the first and second arguments, while the destination or end point x and y coordinates are the last two arguments of the method.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);
g.curveTo(100, 100, 200, 0);

Omitting moveTo() in the code sets the first point at (0, 0), or the sprites registration point. The final result is a curve that starts at (0, 0), ends at (200, 0), but is curved through a control point of (100, 100).

12.6 Defining a Fill Style

Problem

You want to fill a drawn shape with a color.

Solution

Use the beginFill() and endFill() methods of the Graphics class.

Discussion

In 12.3 Defining a Line Style, you learned how to define a line style, but even a closed line has no fill without specifying a fill style. The beginFill() method specifies a color and opacity to fill any shapes drawn until you call the endFill() method.

When you use a fill, if drawn shapes aren’t closed (meaning the line does not end at its starting point), the shapes are closed for you. To demonstrate this effect, the following code draws a complete triangle even though the instruction that draws the last side of the triangle is commented out (to prevent it from drawing). If you remove the beginFill() and endFill() instructions from this code, then you see only the two lines specified, because the fill process is no longer auto-closing the shape.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(2, 0xFF0000);
g.beginFill(0x0000FF, 1);
g.moveTo(0, -50);
g.lineTo(50, 50);
g.lineTo(-50, 50);
//g.lineTo(0, -50);
g.endFill();

12.7 Drawing a Rectangle

Problem

You want to draw a rectangle.

Solution

Use the drawRect() method of the Graphics class.

Discussion

ActionScript 3.0 has added a few methods to draw geometric shapes, removing the need to build them with multiple line segments. Creating a rectangle is as easy as calling drawRect(), passing in the rectangle’s x and y location, followed by height and width.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);
g.drawRect(0, 0, 100, 60);

All primitives in this chapter, except for a circle, are drawn down and to the right of the x and y coordinates specified. When drawing into a parent container, as in this example, this trait results in a registration point at the upper-left corner of the shape. To draw this rectangle around its center point, offset the x and y values passed into drawRect() by half the width and half the height, respectively. The following substitute line makes the rectangle center-aligned within its parent container, sp.

g.drawRect(−50, −30, 100, 60);

12.8 Drawing a Rectangle with Rounded Corners

Problem

You want to draw a rectangle with rounded corners.

Solution

Use the drawRoundRect() method of the Graphics class.

Discussion

This variance on drawRect() requires a fifth parameter that represents the diameter of a circle used to round off the rectangle’s corners. The last line of the following code creates a 100 × 60 pixel rectangle with rounded corners that have a radius of 15.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);
g.drawRoundRect(0, 0, 100, 60, 15);

The fifth parameter requires a diameter, rather than a potentially more intuitive radius, to easily support the optional sixth parameter. Instead of specifying only a diameter to build the rectangle’s corners, you can also specify a height and width, constructing your corner from an ellipse. This method gives you more granular control over the corner shapes. The following substitute line, for example, uses an ellipse with a width of 30 and a height of 50 to create its corners.

g.drawRoundRect(0, 0, 100, 60, 30, 50);

You can also use the under-documented method drawRoundRectComplex() that requires eight parameters. The first four are, again, the x, y, width, and height of the rectangle. The last four, however, are the diameters of each corner circle, in the order of upper-left, upper-right, lower-left, lower-right. The following substitute line for the previous example would create a tab shape, with a straight bottom edge, and two rounded top corners:

g.drawRoundRectComplex(0, 0, 100, 40, 20, 20, 0, 0);

See Also

12.7 Drawing a Rectangle for drawing a rectangle and 12.9 Drawing a Circle for drawing a circle.

12.9 Drawing a Circle

Problem

You want to draw a circle.

Solution

Use the drawCircle() method of the Graphics class.

Discussion

In addition to x and y starting coordinates, drawing a circle requires only one additional argument value: the radius of the circle (half its diameter, or width/height). The following code creates a circle that is 40 × 40 pixels by using a radius of 20.

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x0000FF);
g.drawCircle(0, 0, 20);

The circle is drawn differently. Instead of drawing with its origin at the upper-left corner of the shape, as is true with other primitives, the circle’s origin is its center. To draw a circle that’s below and to the right of its parent container’s registration point, you must offset the x and y coordinates by the amount used for the circle’s radius. The following substitute line aligns the circle to the top-left corner of the sprite.

g.drawCircle(20, 20, 20);

You can also draw an ellipse using the drawEllipse() method. Rather than accepting a radius as its third parameter, it accepts a width and height as parameters three and four, just like drawRect(). Also like drawing a rectangle, the default registration point of the ellipse is the upper-left corner (in contrast to the drawCircle() method’s default center registration point). Substituting the following for the drawCircle() instruction in the prior example draws an ellipse that is 100 pixels wide and 50 pixels tall.

g.drawEllipse(0, 0, 100, 50);

See Also

12.7 Drawing a Rectangle for drawing a rectangle and 12.8 Drawing a Rectangle with Rounded Corners for drawing rectangle with rounded corners.

12.10 Creating a Gradient Fill

Problem

You want to create a gradient fill to replace solid colors when filling drawn shapes.

Solution

Use the beginGradientFill() and endFill() methods of the Graphics class.

Discussion

To create a gradient fill, you must first understand its component parts. First is the type of gradient: linear or radial. Next is a set of parallel arrays (arrays with an equal number of objects in the same order) that represent up to 15 colors in the gradient. These arrays contain the color values themselves, their alpha values, and the amount of the total gradient each color is meant to occupy, respectively. Finally, an optional matrix can be used to rotate, scale, skew, or offset the location of the gradient. Here is the gradient fill creation segment:

var gradType:String = GradientType.LINEAR;
var colors:Array = [0x000000, 0x000000];
var alphas:Array = [1, 0];
var ratios:Array = [0, 255];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(100, 100, 0, 0, 0);

The color values use standard hexadecimal notation. The alpha values are expressed in decimal notation of percent values between 0 and 1. However, the ratio of color quantity, is expressed as an increasing set of numbers between 0 and 255, representing their position along the gradient.

An equally spaced two-color gradient, such as this example uses, is created using the two extreme values of 0 and 255. Both colors are full at the ends of the gradient, and mix together in the middle. Adding another equally spaced third color would require a ratio array of 0, 127, and 255. Finally, to skew a gradient toward one dominant color, you might use a ratio array of 0 and 127. This would show the first color in one third of the gradient and mix over to the second color that occupied two thirds of the gradient.

The last part of creating a gradient is the optional matrix to manipulate its position, scale, and angle. You create the matrix with the standard use of the new keyword, but editing it can get complex. Fortunately, you have a special method of the Matrix class specifically for this purpose, createGradientBox(). The eponymous method accepts a width, height, rotation, horizontal translation, and vertical translation to manipulate the gradient for you.

This example uses a width and height that matches the size of the rectangle itself, and no horizontal or vertical translation is specified, so the full gradient is visible. With a rotation of 0, the gradient moves from first color to last color, in a left to right direction.

Finally, these values are passed into the beginGradientFill() method (used in place of the beginFill() method of prior recipes) in the order discussed: gradient type, color array, alpha array, ratio array, and matrix. The result is a smooth gradient from opaque black to transparent.

var sp:Sprite = new Sprite();
addChild(spMask);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.beginGradientFill(gradType, colors, alphas, ratios, matrix);
g.drawRect(0, 0, 100, 100);
g.endFill();

12.11 Using a Drawn Shape as a Dynamic Mask

Problem

You want to mask a display object with another display object created at runtime.

Solution

Draw the mask using the Graphics class, and then use the mask property of shape, sprite, and movie clip display objects.

Discussion

This recipe adds only one significant new line to material covered in previous recipes. However, due to subtle changes (variable names, size, and fill style), the complete code has been collected here. This example combines two sprites, one with a rectangle with a solid fill, and another with a rectangle with a gradient fill. The solid rectangle is on the bottom and is 300 pixels wide by 300 pixels tall. The top rectangle contains a gradient of 100 percent alpha black to 0 percent alpha black (transparent), and measures only 100 × 100 pixels.

The last line of this recipe is the important one. To use one display object to dynamically mask another, set the mask property of the maskee, to the mask.

var sp:Sprite = new Sprite();
addChild(sp);
var g:Graphics = sp.graphics;
g.lineStyle();
g.beginFill(0x000099, 1);
g.drawRect(0, 0, 300, 300);
g.endFill();

var gradType:String = GradientType.LINEAR;
var colors:Array = [0x000000, 0x000000];
var alphas:Array = [1, 0];
var ratios:Array = [0, 255];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(100, 100, 0, 0, 0);

var spMask:Sprite = new Sprite();
addChild(spMask);
spMask.x = spMask.y = 100;
var gMask:Graphics = spMask.graphics;
gMask.beginGradientFill(gradType, colors, alphas, ratios, matrix);
gMask.drawRect(0, 0, 100, 100);
gMask.endFill();

sp.mask = spMask;

Note that this minimal approach supports only 1-bit masks. That is, any non-transparent pixel, no matter what the alpha value of that pixel, is considered opaque and part of the mask. This example uses a gradient that changes from 100 percent alpha to 0 percent alpha to emphasize that the alpha data has no effect, by default, on the mask.

However, 8-bit masks, or masks with varying degrees of alpha values, are supported when using bitmap caching, as seen in the next recipe.

See Also

12.6 Defining a Fill Style and 12.10 Creating a Gradient Fill for how to create a solid or gradient fill, and see 12.12 Caching Vector as Bitmap for how to cache a bitmap.

12.12 Caching Vector as Bitmap

Problem

You want to use pixel-based effects on vector assets and/or attempt to increase performance of vector rendering.

Solution

Temporarily work with a visual asset as a bitmap, by temporarily storing bitmap representation of the asset.

Discussion

You need significant CPU processing power to composite and render moving vectors many times a second. In some cases, you can improve performance by treating the vector asset as a bitmap, behind the scenes. In simple terms, Flash Player takes a screenshot of the vector any time a major transformation, such as changes to rotation, alpha, or scale, occurs and composites the bitmap version rather than the vector asset itself.

Because the bitmap version is cached, you have no degradation of visual quality any time a major transformation occurs. However, the need to maintain an equivalent bitmap also means performance can actually worsen if you use this feature injudiciously. Therefore, bitmap caching is not recommended if the asset is rotating, scaling, or changing opacity frequently.

More directly, sometimes you need to temporarily convert a vector to bitmap to apply pixel-based effects, like filters and 8-bit masks. In some cases, this process is automatic, such as when you’re applying a simple filter like a drop shadow. In other cases, you must explicitly enable the feature.

This recipe adds only two lines of code to the very end of the prior 12.11 Using a Drawn Shape as a Dynamic Mask to use a drawn shape as a dynamic mask. The repeated code has been omitted.

sp.cacheAsBitmap = true;
spMask.cacheAsBitmap = true;

By enabling bitmap caching for both the mask and maskee, both can be composited as bitmaps. Then you can use the gradient alpha values in the mask when displaying the underlying content.

See Also

12.10 Creating a Gradient Fill for creating a gradient fill and 12.11 Using a Drawn Shape as a Dynamic Mask to use a drawn shape as a dynamic mask.

12.13 Applying a Simple Bitmap Filter

Problem

You want to apply simple bitmap filter effects to a display object, such as drop shadow, bevel, or blur.

Solution

Use one of the simple filter classes, including DropShadowFilter(), BevelFilter() or BlurFilter().

Discussion

Applying simple filters to display objects is very easy for two reasons. First, the filter classes are easy to use and work similarly to the way the same filters are applied in the Flash authoring environment. Second, there is a built-in filters property in any display object that can have a filter applied, making it straightforward to set the property to the filters you wish to add.

Creating a filter follows the same pattern as most other instantiations in ActionScript 3.0, using the new keyword. Conveniently, the parameters for setting the values of the filter are all optional, and use default values if none are specified. This means that, at minimum, you can create filters using the following example format, which demonstrates the drop shadow and bevel filters:

var dsFilter:DropShadowFilter = new DropShadowFilter();
var bvFilter:BevelFilter = new BevelFilter();

You can apply the filters just as easily by setting the filters property of your display object. The filters property requires an array to let multiple filters be applied at the same time (such as applying both a bevel and a drop shadow). When applying only one filter, you need only pass a single-item array to the property. The following new script creates a filled rectangle, and applies a drop shadow effect:

var sp:Sprite = new Sprite();
addChild(sp);
sp.x = sp.y = 100;
var g:Graphics = sp.graphics;
g.lineStyle(1, 0x000099);
g.beginFill(0x0000FF, 1);
g.drawRect(0, 0, 100, 60);
g.endFill();

var ds:DropShadowFilter = new DropShadowFilter();
sp.filters = [ds];

If you wanted to apply both a drop shadow filter and a bevel filter, the last block of code would read:

var ds:DropShadowFilter = new DropShadowFilter();
var bv:BevelFilter = new BevelFilter();
sp.filters = [ds, bv];

To manipulate a filter’s settings, you can use different optional settings for each filter. The first batch of settings for the drop shadow filter are relatively intuitive numerical values, in this order: distance offset (pixels), angle (degrees), color (in hexadecimal notation), alpha (decimal percent range, 0–1), extent of blur in the x direction, extent of blur in the y direction, strength (the amount of color applied and degree of spread), and quality (how many times the filter’s applied). The last batch of options are three Booleans that represent the special setting of this filter—whether or not the shadow is cast inside the shape (to represent a “hole” in the canvas, for example), whether or not the underlying shadow is knocked out (revealing the canvas), and whether or not the object casting the shadow is hidden (leaving only the shadow).

So, if you want to create a lighter, softer shadow, cast down and to the left, you might substitute the previous filter instantiation with the following:

var ds:DropShadowFilter = new DropShadowFilter(5, 135, 0x000099, .5, 10, 10);

You can also change values after creating a setting by manipulating the desired property directly. You just have to remember to reapply the filters to the display object after any change. For example, revisit the filter setup at the beginning of this recipe:

var ds:DropShadowFilter = new DropShadowFilter();
sp.filters = [ds];

You could then change a specific setting, such as the angle, in the filter, and then reapply the filter to see your change in action. The following code changes the angle from the default 45 to 135:

ds.angle = 135;
sp.filters = [ds];
..................Content has been hidden....................

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