AS Reference  :  Notes Index  :  Resources  :  About/Contact  :  Downloads

The Flash Drawing API


Beginning with Flash MX, Macromedia has provided the ability for coders to programmatically create their own movieclips within a Flash movie at runtime, and give them a defined shape using the Drawing API (Application Programmer Interface). This consists of a set of commands (MovieClip methods) to create movieclips (and movieclips within movieclips) that contain shapes made of strokes and fills. Those commands are:

Above are four movieclips drawn with the drawing API. Because they are the same as any other instance of the MovieClip class, any of the methods of the MovieClip class may be applied to them, and they have all the properties of that class. The code to make them is below. You can try it yourself by creating a new blank 500x200 Flash document with #666666 backround and pasting the code below into frame 1.

// make a 100-pixel grid movieclip at depth 1
createEmptyMovieClip("grid", 1);
grid.lineStyle(1, 0xcccccc, 100);
grid.moveTo(0, 0);
grid.lineTo(500, 0);
grid.lineTo(500, 200);
grid.lineTo(0, 200);
grid.lineTo(0, 0);
grid.moveTo(100, 0);
grid.lineTo(100, 200);
grid.moveTo(200, 0);
grid.lineTo(200, 200);
grid.moveTo(300, 0);
grid.lineTo(300, 200);
grid.moveTo(400, 0);
grid.lineTo(400, 200);
grid.moveTo(0, 100);
grid.lineTo(500, 100);

// create a movieclip of two rectangles at depth 2
// create a variable r to reference this movieclip
// (which we can do because createEmptyMovieClip returns a 
//  reference to itself)
var r:MovieClip = createEmptyMovieClip("rectangles", 2);
r.beginFill(0xcc00cc, 100);
r.moveTo(20, 20);
r.lineTo(140, 20);
r.lineTo(140, 140);
r.lineTo(20, 140);
// notice that the shape will be filled automatically
// without having to draw the last line
r.endFill();
// start a new rectangle of a different color in the same clip
r.beginFill(0x0000cc, 50);
r.lineStyle(1, 0x000033, 100);
r.moveTo(80, 80);
r.lineTo(180, 80);
r.lineTo(180, 180);
r.lineTo(80, 180);
r.endFill();

// create a triangle movieclip at centered on 0,0. then move it so
// it's centered at 300, 100 by referencing its _x and _y properties.
// then make it rotate every frame using the onEnterFrame method.
var t:MovieClip = createEmptyMovieClip("triangle", 3);
t.beginFill(0x000099);
t.lineStyle(2, 0x000033, 100);
t.moveTo(-50, 50);
t.lineTo(0, -50);
t.lineTo(50, 50);
t.endFill();
t._x = 300;
t._y = 100;
t.onEnterFrame = function() {
   this._rotation += 5;
}

// make a circle movieclip c, centered at 400, 100 with radius=80
// apply a radial gradient fill to the circle
createEmptyMovieClip("c", 4);
// center point and radius of circle
var r:Number = 80;
var x:Number = 400;
var y:Number = 100;
// constant used in calculation
var A:Number = Math.tan(22.5 * Math.PI/180);
// variables for each of 8 segments
var endx:Number;
var endy:Number;
var cx:Number;
var cy:Number;

c.beginGradientFill("radial", [0x0000cc, 0xcc00cc], [80, 100], [128, 255], 
   {matrixType:"box", x:0, y:0, w:430, h:150, r:0});
c.moveTo(x+r, y);
for (var angle:Number = 45; angle<=360; angle += 45) {
   // endpoint
   endx = r*Math.cos(angle*Math.PI/180);
   endy = r*Math.sin(angle*Math.PI/180);
   // control:
   // (angle-90 is used to give the correct sign)
   cx =endx + r* A *Math.cos((angle-90)*Math.PI/180);
   cy =endy + r* A *Math.sin((angle-90)*Math.PI/180);
   c.curveTo(cx+x, cy+y, endx+x, endy+y);
}
c.endFill();

Drawing curves and circles

As you can see in the example above, drawing can be done either with lines (using lineTo with 2 parameters, the x and y point to draw the line to) or with curves. To draw a curve with the Flash drawing API, you use the curveTo method, which requires a control point and an anchor (endpoint). The control point is the "handle" in a quadratic bezier curve which bends the curve to the desired shape. Because there is only one handle per curve instead of two (as in a cubic bezier), some manipulating must be done to correctly draw arcs that are greater than about 45 degrees.

For angles of 45 degrees (an arbitrary point based on experimentation and the fact that it's a multiple of 360) and less, the control point which provides the best arc for a given angle theta is x=r, y=r * tan(theta/2). Using that control point for a 45-degree arc, we can connect eight such arcs to form a circle (45 * 8 = 360), as was done in the code above.

A note about registration point

An easy mistake to make when using the drawing API is to assume that the movieclip's registration point (the point around which it will rotate, if you want to rotate it, eg) is at the point where you issue the first moveTo to, but it isn't. It's actually always at (0,0). So if you want the registration point to be at, say, (50,100), you need to draw the clip at or around (0,0) and then move it so that the old (0,0) point is now sitting at (50,100), using the clip's _x and _y properties. In the example above, I wanted the triangle to rotate around the (300,100) point, so I drew it first around (0,0) and then changed the _x and _y of the triangle so it would be in the position I wanted it to be for rotating.

Creating a method that can be used by all movieclips in a movie

As of Flash MX, the prototype property of the MovieClip class can be used to assign additional functions to the class, which makes them available to all movieclips. Here's an example of creating and using a drawCircle method:

// r = radius of circle
// x, y = center of circle
MovieClip.prototype.drawCircle = function (r, x, y) {
   var TO_RADIANS:Number = Math.PI/180;
   // begin circle at 0, 0 (its registration point) -- move it when done
   this.moveTo(0, 0);
   this.lineTo(r, 0);

   // draw 12 30-degree segments 
   // (could do more efficiently with 8 45-degree segments)
   var a:Number = 0.268;  // tan(15)
   for (var i=0; i < 12; i++) {
      var endx = r*Math.cos((i+1)*30*TO_RADIANS);
      var endy = r*Math.sin((i+1)*30*TO_RADIANS);
      var ax = endx+r*a*Math.cos(((i+1)*30-90)*TO_RADIANS);
      var ay = endy+r*a*Math.sin(((i+1)*30-90)*TO_RADIANS);
      this.curveTo(ax, ay, endx, endy);	
   }
   this._x = x;
   this._y = y;   
}

// use the method to draw a circle in movieclip c 
// at x=100, y=100 with a 70-pixel radius
createEmptyMovieClip("c", 1);
c.beginFill(0x000055, 60);
c.drawCircle(70, 100, 100);
c.endFill();

Drawing a shape with a cutout

To draw a shape with a part of it cut out, like a donut eg, simply put all the commands to draw both the initial object (big circle) and the cutout (smaller circle) between the beginFill and endFill statements. Here's how the donut on the left (ok, more like a bagel but still delicious) was created:

// r1 = radius of outer circle
// r2 = radius of inner circle (cutout)
// x, y = center of donut
// This creates a donut shape (circle with a cutout circle)
MovieClip.prototype.drawDonut1 = function (r1, r2, x, y) {
   var TO_RADIANS:Number = Math.PI/180;
   this.moveTo(0, 0);
   this.lineTo(r1, 0);

   // draw the 30-degree segments
   var a:Number = 0.268;  // tan(15)
   for (var i=0; i < 12; i++) {
      var endx = r1*Math.cos((i+1)*30*TO_RADIANS);
      var endy = r1*Math.sin((i+1)*30*TO_RADIANS);
      var ax = endx+r1*a*Math.cos(((i+1)*30-90)*TO_RADIANS);
      var ay = endy+r1*a*Math.sin(((i+1)*30-90)*TO_RADIANS);
      this.curveTo(ax, ay, endx, endy);	
   }
  
   // cut out middle (draw another circle before endFill applied)
   this.moveTo(0, 0);
   this.lineTo(r2, 0);

   for (var i=0; i < 12; i++) {
      var endx = r2*Math.cos((i+1)*30*TO_RADIANS);
      var endy = r2*Math.sin((i+1)*30*TO_RADIANS);
      var ax = endx+r2*a*Math.cos(((i+1)*30-90)*TO_RADIANS);
      var ay = endy+r2*a*Math.sin(((i+1)*30-90)*TO_RADIANS);
      this.curveTo(ax, ay, endx, endy);	
   }

   this._x = x;
   this._y = y;
}
createEmptyMovieClip("d1", 1);
var colors:Array = [0, 0xCB9454, 0x8D6433];
var alphas:Array = [100, 100, 100];
var ratios:Array = [0, 110, 255];
var matrix:Object = {a:300, b:0, c:50, d:0, e:300, f:0, g:-3, h:3, i:1};
d1.beginGradientFill("radial", colors, alphas, ratios, matrix);
d1.drawDonut1(86, 36, 100, 94);
d1.endFill();

Using a shape with a cutout as a mask

If a shape containing a cutout is to be used as a mask, as the donut on the right above, care must be taken to draw the cutout in the opposite direction from that in which the original shape was drawn. (If you don't, there will be no cutout in the mask). Here's the code for the donut mask on the right, in which a big circle is drawn in a clockwise direction and a smaller circle drawn in the opposite direction (all before the endFill is applied). pic is a movieclip containing the flower graphic.

// r1 = radius of outer circle
// r2 = radius of inner circle (cutout)
// x, y = center of donut
// This creates a donut shape that can be used as a mask
MovieClip.prototype.drawDonut2 = function (r1, r2, x, y) {
   var TO_RADIANS:Number = Math.PI/180;
   this.moveTo(0, 0);
   this.lineTo(r1, 0);

   // draw the 30-degree segments
   var a:Number = 0.268;  // tan(15)
   for (var i=0; i < 12; i++) {
      var endx = r1*Math.cos((i+1)*30*TO_RADIANS);
      var endy = r1*Math.sin((i+1)*30*TO_RADIANS);
      var ax = endx+r1*a*Math.cos(((i+1)*30-90)*TO_RADIANS);
      var ay = endy+r1*a*Math.sin(((i+1)*30-90)*TO_RADIANS);
      this.curveTo(ax, ay, endx, endy);	
   }
  
   // cut out middle (go in reverse)
   this.moveTo(0, 0);
   this.lineTo(r2, 0);

   for (var i=12; i > 0; i--) {
     var endx = r2*Math.cos((i-1)*30*TO_RADIANS);
     var endy = r2*Math.sin((i-1)*30*TO_RADIANS);
     var ax = endx+r2*(0-a)*Math.cos(((i-1)*30-90)*TO_RADIANS);
     var ay = endy+r2*(0-a)*Math.sin(((i-1)*30-90)*TO_RADIANS);
     this.curveTo(ax, ay, endx, endy);     
   }

   this._x = x;
   this._y = y;
}
createEmptyMovieClip("d2", 2);
d2.beginFill(0xaa0000, 60);
d2.drawDonut2(86, 36, 300, 94);
d2.endFill();
pic.setMask(d2);

Intro
Flash: What & How
Example Sites
Create
Draw, Edit Shapes
Gradients
More Drawing Tips
Import
A Sample
Animate
Frames, Keyframes
Motion Tweens
More Motion Tweens
Shape Tweens
Masks
Control
Stop/Replay
Movieclips Intro
Movieclip Reference
Site Structure 1
Slideshow Movieclip
Contact Form
Scroll Resume
Preloader
Site Structure 2
Publish
Display Options
Player Detection
Optimize
AS 2.0 Basics
Intro to Syntax
Playhead Commands
Playhead Cmds 2
Coded Tween
onEnterFrame
Intro to Classes
Declare/Assign
Comments, Trace
Simple Data Types
Arrays & Objects
Code Blocks
Operators
Beyond Buttons
Code Structure
Toggle Controls
Group of Buttons
Drag and Hit
Distort Magnifier
Scroll Text
Bee Game
Dart Shooter
Sound Control
Easing Slider
Easing Slider 2
Components Intro
Timers & Delays
Dynamic Content
Intro
Drawing API
Create Text
Attach Movieclips
Easing Slider 3
Easing Slider 4
Load jpg/swf
Sliding Viewer
Preload swf
XML
Easing Slider 5
Server Comm
LoadVars (w/ PHP)
AS - PHP Lookup
Text File
Database 1:LoadVars
Database 2:Remoting
Read from directory
AS 2.0 Classes
Intro
Math
Key
Date
Color
EventDispatcher
New Samples
Pie Chart
Event-model Emailer
Tween Sequence
Fuse Sequence
SVG in Flash
Bitmap Topo
SWF as Data Holder
Two-level Menu
Yahoo! Flash Maps
Class-based Game
ASTB Samples
Disclaimer
3D Outlines
Bounce Collide
Address Book
Save Drawings
Home  :  Notes Index  :  Resources  :  About/Contact  :  Downloads