Components 1 : Base Component



The Flash movie at left shows three arrows created from a single component. This component allows Flash developers to drag a movieclip onto the stage and customize it to serve the purposes of the current project.

Download the sourcefile here.

Smart clips were introduced in Flash 5 and allowed Flash developers to create movieclips that you could customize for individual projects based on parameters you set in the authoring environment. In Flash MX, components have taken the place of smart clips and offer more advanced interaction with Actionscript, as well as a Live Preview feature that allows users to see the effects of the altered parameters on the component without having to publish the movie.

There are a number of uses for components. Included with Flash MX are components developed by Macromedia for use as common interface widgets like a scrollbar or push button. By altering skins for these components, you can create a look to suit your project without having to recode the functionality each time. You could also create a component that adds some additional code functionality to your movies. Consider if you contain the Class definition for a Vector class inside a component. By dragging the component into your movie (or attaching it at run-time with code), you have effectively added the Class to your movie and can interact with it through Actionscript. You can also create a component that takes care of the complex coding of animation. For instance, you might create a component that will animate a particle effect (like an explosion of sparks). By altering parameters for the component, you could reuse that particle effect for a different purpose (smoke) without having to recode.

In this series of three tutorials on components, we will create a new component from the ground up, beginning with the base component itself, then building a live preview and finally a custom user interface. This first tutorial will walk you through all you need to create the minimum for the component without all the bells and whistles. The component we will be creating is an arrow movieclip that you can drag onto the stage and alter the appearance of by changing parameter values.


  1. The first things that we need are graphics for our arrowHead and arrowTail. Create two new movieclip symbols (F8) and name them "arrowHead" and "arrowTail". In your library, create a new folder named "arrow assets" and drag both of these new clips into this folder.
  2. The clips currently are empty, so we need to add graphics to each. Inside the arrowHead symbol, draw a single, black arrowhead per frame for the first five or six frames. I actually created seven arrowhead graphics in all, then left the eighth keyframe blank (assuming one option for the arrowhead could be "none"). Each arrowhead should be drawn to the right of the symbol's registration point, as demonstrated in the following illustration, which shows two separate frames in the arrowHead symbol:




  3. The next step is to add the graphics to the arrowTail symbol in a similar. These graphics, however, should be drawn to the left of the arrowTail's registration point, as demonstrated in the following illustration. I added six tails in all, plus a blank keyframe in frame seven.




  4. The final graphic to add is the line between the arrowHead and arrowTail. This is for preview purposes only, as we will draw the actual line at run-time using the drawing API. Create a new movieclip symbol named "line preview" and draw a straight horizontal line with a stroke width of 1 pixel and a length of 75 pixels. Drag this symbol into the "arrow assets" folder in the library.
  5. Now we have all the graphics we will need for our arrows. It's time to code! First, though, we need a symbol that will house all of our code. This will be our arrow component (a component is like a configurable movieclip, so we need a movieclip to start with). Insert a new symbol (F8) and name it "Arrow". Make sure the advanced options are available, and that the symbol is set to be exported in the first frame with the linkage identifier "Arrow". What we are doing is making sure the symbol is exported from the library with a specific name. We will "tie" this name with some code in a later step so that Flash knows we want every instance of this symbol to use that code. This is called "extending a movieclip" and was covered in this earlier tutorial.




  6. Doubleclick the Arrow symbol in the library to enter symbol editing mode (if you are not already in it). Create four layers inside the Arrow symbol (from the bottom up): "line", "tail", "head", "code". Drag the appropriate graphics from your library into the layers (following the labels) and position each to make up a complete arrow (tail-line-head). Position the completed arrow to the right of Arrow's registration point (we will reposition everything later on through code, but the position of each clip will become important in a later step when we implement live preview). Now name each of these instances "line", "arrowTail", and "arrowHead", respectively (I'll leave you to figure out which movieclip is called what!).




  7. We're done with everything but the code! So select the "code" layer of the Arrow timeline and open up your Actionscript editor. Type the following:


  8. #initclip
     
    #endinitclip

    These two lines will be the bookends for our component code. Neither should have a semicolon following it, which is different from most Actionscript commands. These lines do two things for us here. First, they tell Flash that the code that follows is a component Class definition, and that it should only be run once, no matter how many instances are placed on the stage (as opposed to code that you place on the timeline of a movieclip symbol that is run for each instance). Second, the code contained within these bookends will run before the movie begins. This means that any instance of this component placed on the stage in the authoring environment will have had its Class defined before its instantiation. This is extremely important for our components to work properly.

  9. Add the following lines to the code:



  10. #initclip
     
    Arrow = function() {
      this.line._visible = 0;
      this.init();
    }
     
    #endinitclip

    This function looks like any other we might write. The exception is that this will serve as our Class definition for our Arrow component (we capitalize the function name to indicate this). The only things we do when this function is called - which is any time an instance of Arrow is instantiated - is make the preview line movieclip invisible and call the Class's init() method, which we will write in a moment. I contain the initialization code in an init() method purely because of personal preference, not because it is necessary.

  11. Add the following lines to the code in the Actionscript editor:



  12. #initclip
     
    Arrow = function() {
      this.line._visible = 0;
      this.init();
    }
     
    Arrow.prototype = new MovieClip();
     
    Object.registerClass("Arrow", Arrow);
     
    #endinitclip

    Believe it or not, that's all the code you need to define a component - it won't do much (or anything), but that's all you need. The first new line sets up the inheritance for our Class. Basically, we tell it to pick up all the properties and methods of the MovieClip Class so that we can use all the movieclip code that we are familiar with to control each arrow's properties. The second new line tells Flash that every instance of "Arrow" (the identifier name for the symbol we exported from the library) that is placed on the stage will be an instance of the Arrow Class as we are defining it. This is why we exported the "Arrow" symbol with the identifier name "Arrow" back in step 5.

  13. Let's now write our init() method, which will run whenever a new instance of Arrow is placed on the stage. Add this method AFTER the prototype assignment:



  14. Arrow.prototype = new MovieClip();
     
    Arrow.prototype.init = function() {
      this.resetSize();
      this.drawArrow();
    }

    We call two more methods inside the init() method (you could get away with moving these two method calls inside the main function declaration - I just use the init() out of habit). First, we will reset the width and height of our arrow movieclip to its original dimensions if the user has rescaled it, then we will draw the actual arrow, sending the head and tail moveiclips to their proper frames.

    Notice that we are attaching the init() method (as we will with all methods) to the prototype property of Arrow. This makes the method available to all instances of the Arrow without having to duplicate the method to each instance. This is why we must declare the inheritance for Arrow BEFORE assigning methods, since to do so afterwards would mean overwriting all of our work!

  15. Now let's take care of our resize() method. This will rescale our clip to its original dimensions if the user has rescaled the clip in the authoring environment. We will take care of the scaling of the final arrow ourselves through code, so that the arrow and tail aren't distorted. Add this method after the init() method:



  16. Arrow.prototype.resetSize = function() {
      var w = this._width;
      var h = this._height;
      this.lineLength = Math.sqrt(w*w + h*h);
      this._xscale = this._yscale = 100;
    }

    The user will be able to scale and rotate the arrow in the authoring envirmonment, so the first thing we need to do is reset the clip to its default. Before we do that though, we take its current height and width, and use the Pythagorean Theorem to determine the length of the line from corner to corner (this also then accounts for rotation of the clip). Once the lineLength is stored, we reset the _xscale and _yscale to the defaults.

  17. One last method! This is the biggest one, though, so we'll break it into parts. Begin adding these lines after the resize() method:



  18. Arrow.prototype.drawArrow = function() {
      this.lineStyle(this.lineThickness, this.lineColor, 100);
      this.lineTo(this.lineLength, 0);

    So far, so good. Here we start the method by creating a lineStyle based on some parameters that we'll allow the user to set (lineThickness, lineColor), then we draw a line the length of the lineLength variable we set in the resize() method. Now it's time to format the arrowhead.

  19. Add on to the drawArrow() method:



  20. Arrow.prototype.drawArrow = function() {
      this.lineStyle(this.lineThickness, this.lineColor, 100);
      this.lineTo(this.lineLength, 0);
      if (this.arrowHeadStyle > -1) {
      var headFrame = (this.arrowHeadStyle % this.arrowHead._totalframes) + 1;
      } else {
      var headFrame = this.arrowHeadStyle;
      while (headFrame < 0) {
      headFrame += this.arrowHead._totalframes;
      }
      headFrame++;
      }
      this.arrowHead.gotoAndStop(headFrame);
      this.arrowHead._x = this.lineLength;
      this.arrowHead._xscale = this.arrowHead._yscale = this.arrowHeadScale;
      (new Color(this.arrowHead)).setRGB(this.arrowHeadColor);

    These lines might look more complicated than they actually are. The if..else statement just sends the arrowHead movieclip to the proper frame based on what the user has entered for the arrowHeadStyle variable, which should correspond to a frame number. If the number supplied doesn't fall into the range of frames in arrowHead, we just perform some math operations to bring it into the proper range. This is useful since we can change the arrowhead graphics (and how many there are) at any time without having to worry about the code.

    After sending the arrowHead movieclip to the proper frame, we place the arrowHead at the end of our line that we drew, scale it based on another user-defined variable (arrowHeadScale) and color it based on another (arrowHeadColor). That takes care of the head. The tail is pretty much exactly the same thing!

  21. Finish off the drawArrow() method with the following:



  22.   this.arrowHead.gotoAndStop(headFrame);
      this.arrowHead._x = this.lineLength;
      this.arrowHead._xscale = this.arrowHead._yscale = this.arrowHeadScale;
      (new Color(this.arrowHead)).setRGB(this.arrowHeadColor);
      if (this.arrowTailStyle > -1) {
      var tailFrame = (this.arrowTailStyle % this.arrowTail._totalframes) + 1;
      } else {
      var tailFrame = this.arrowTailStyle;
      while (tailFrame < 0) {
      tailFrame += this.arrowTail._totalframes;
      }
      tailFrame++;
      }
      this.arrowTail.gotoAndStop(tailFrame);
      this.arrowTail._x = 0;
      this.arrowTail._xscale = this.arrowTail._yscale = this.arrowTailScale;
      (new Color(this.arrowTail)).setRGB(this.arrowTailColor);

    That's so similar to what we do with the arrowHead movieclip that you could probably make it into a nice little function to take care of the redundancy. I've laid it all out for you here, though. The only difference is that we set the arrowTail movieclip at 0 to place it at the opposite end of the line. And that's our code done!

  23. The last thing we need to do to complete our component is allow the user to alter variables to control the look of the arrow. We do this through the library. Right-click on the "Arrow" symbol and select "Component Definition..." Using the PLUS at the top left of the dialog box, add the following parameters to the component, looking closely at the TYPE and VALUE in addition to the NAME:




  24. That completes the initial construction of our arrow component! Drag an instance onto the stage and use the Properties palette to set its parameters. You won't see the changes until you publish your movie (that's what Live Preview is for - covered in the next lesson). Feel free to rotate and scale the arrow as well to see the effect.


It doesn't take too much code at all to make movieclips into customizable components. Here in a short tutorial we have created one from scratch. I hope this has given you the foundation you need to go out and experiment with more complex and original ideas. Basically, whenever you find you have coded the same effect or tool more than once, you should stop and consider, "Could this be made into a component?" If it can, give it a shot! A little time invested in component development could save considerable time down the line!

The next tutorial in this series will look into adding a Live Preview to the Arrow component so that we can see the effect on the stage. This is easier to accomplish than people think, and we'll go through it step by step so you can start incorporating Live Previews into all of your components. Till then, explore and enjoy!