Chapter 14. Extending ActionScript

CONTENTS
  •  Callback Functions
  •  Registering Custom Classes
  •  Listeners and Watchers
  •  Overriding Built-In Methods
  •  Setting Scripts to Trigger on Schedule
  •  Summary

This chapter covers ways to both extend and re-define ActionScript. Although there is always more than one way to solve a problem, this chapter shows you other ways to use ActionScript. You'll not only get a closer look at the inner workings of Flash, but you'll see how you can adjust the way ActionScript behaves so that it's more under your control. I know that's pretty vague but think of this chapter as one of those car manuals for people who like rebuilding their engines.

Nearly everything in this chapter is new for Flash MX. Despite the fact that every other chapter includes lots of new Flash MX features, there are still many more. While organizing this book, I would often come across an exciting new feature. If I didn't know exactly where to put it, I added it to this chapter! As a result, you'll see that this chapter covers quite a bit of material.

Some of the material is rather advanced. So, instead of spending a lot time studying each topic, I recommend getting the basic idea here and then practicing when you get to the workshop chapters. I'll reference workshop exercises, as appropriate.

In this chapter, you will:

  • Write callback functions for buttons and clip events so that you don't have to place scripts right on the instances.

  • Make clips act like buttons.

  • Register homemade classes and create complex properties based on Movie Clips.

  • Fully explore how listeners and watchers can trigger scripts automatically.

  • Override built-in methods using the prototype property.

  • Trigger functions on schedule using the setInterval() command.

The good news about extending ActionScript is that you should be familiar with almost every example in this chapter. ActionScript is very consistent, so you will be able to apply knowledge that you gathered from previous chapters. Things should really start to click!

Callback Functions

The traditional places to write scripts in Flash are in keyframes and on button or clip instances. In the case of buttons and clips, the main instructions would go within a specific mouse event (for buttons) or clip event (for clips). Placing code right on objects this way might seem intuitive because scripts are tied to the objects to which they pertain. However, this technique is limiting even if it is a good way to learn.

The technique is limiting for several reasons. First, all your code is scattered in a million places. Code is easier to manage when it's centralized. Second, some events (for which you want a script to respond) won't fit neatly into the mouse event or clip event interface. For example, a clip's data event is not nearly as clear and concrete as a button's press event. It has gotten worse with all the new event triggers added to Flash MX. For example, Movie Clips now have 18 different clip events. Finally, many other types of objects also have events (for example, the TextField object's onChanged event). There's no interface to specify text events, however. Fortunately, by using callback functions, you can write scripts to respond to any built-in event and the same technique works for any object (not just clips and buttons).

Centralizing Code

This "new way" of working means that you can put almost all the code in a keyframe. First let's look at how this differs from the traditional way of placing code right on buttons or clips, and then we can concentrate on the benefits, limits, and applications.

Basically, the form is always:

instanceName.eventName=someFunction

That is, you're saying for a particular instance that you want a certain event name to trigger the specified function. The function you specify can either be defined separately or appear inline (that is, in one line also called a function literal). I'll show both techniques by way of example. This first example shows the myFunction's definition followed by how the button instance my_btn will trigger that function (when it's pressed):

1 function myFunction(){ 2  trace("You pressed!"); 3 } 4 my_btn.onPress=myFunction;

The onPress event is listed under Object, Movie, Button, Events in the Actions panel (that is, it's built-in). Notice that we're saying the my_btn instance's onPress callback is the function named myFunction. We're not triggering the function in line 4; rather, we're just assigning the value of the button's onPress event to equal that function. In the preceding example, the function is defined separately, so it's accessible like any function. This means that you can invoke it by saying myFunction(), and you could have another button's onPress event (or any other event for that matter) point to the same function.

In the following example, the function tied to the button's onPress event will be defined all in one line:

my_btn.onPress=function(){ trace("You pressed!"); }

The effective result is the same as the first example. This time, however, myFunction is not available from anywhere else. Actually, it doesn't exist! That brings up something worth noticing: the definition of the function (everything on the right side of the equals sign) looks almost the same as a regular function definition but no function name is given. When the contents of such a function get longer, it makes sense to space things out and use extra carriage returns. This example shows the same code as the preceding example but with different spacing:

my_btn.onPress=function(){   trace("You pressed!"); }

As long as you follow the same form, it doesn't matter how you space out things. Spacing out things in this manner makes a lot of sense when the function includes more than one line of code. (You'll see some examples where this becomes obvious shortly.)

Note

It's a matter of choice whether you define your functions inline or separately. However, when you define them separately, the order is important. You always want the function declaration to appear before the callback points to it. It's actually a logical flow. If you say, "When the event happens, I want you to do this function," one would hope that the function is defined before the event occurs. Take the onData event, for example. You don't want the instructions to follow this order: 1) "When the data loads, I want you to execute the jumper function;" 2) "The jumper function is ." A problem arises if data has loaded while you're still explaining how the jumper function works. Imagine these baking instructions: "Pour the cup of water into the flour. When half a cup of water is still remaining, stop pouring and wait 4 minutes before continuing." You know you should always read all the instructions before you start, of course, but Flash doesn't know that.

Defining the function separately had the advantage that the function could be shared with other parts of your movie. This inline fashion has a few advantages. The main difference is that if you don't expect or need other parts of your movie to trigger the same function, you should certainly use the inline technique. A separate function declaration takes up RAM because it effectively gets copied when identified by an event. In addition, the inline technique enables you to do some fancy stuff with the keyword this. That is, the this in a normal function is the function itself, whereas an inline function's this is the button instance whose event is being defined. Check out this example of a button that moves every time it's pressed:

my_btn.onPress=function(){this._x+=10;}

It's pretty simple, really: When the press event occurs, the button's _x is increased by 10. This example actually brings out a potentially confusing point. That is, normally, the this of a button is the timeline in which the button is placed. That is, the following code (placed right on the button instance) will have an entirely different result:

on(press){   this._x+=10; }

The result of this code is that the clip in which the button is present (or main timeline) will move to the right. Only when you use callbacks does a button's this mean the button itself. On the one hand, this can be very confusing; however, it's actually rather logical. You can identify callback functions for clip instances (and all the other objects), and this is always the clip (or object) itself.

Now that you know the form, the following examples should give you an idea of what's possible.

First, let's look at an example that uses the mouseMove clip event:

1 my_mc.onMouseMove=function(){ 2   this._x=_root._xmouse; 3   updateAfterEvent(); 4 }

This example moves the my_mc clip's "x" to match the location of the mouse in the main (_root) timeline. When working with callback functions, it's almost required that you use this. That is, you can't change line 2 to _x=_root._xmouse; if you do, Flash will think the _x is the main timeline's _x (because this code is in a keyframe of the main timeline). Nor can you change line 2 to this._x=_parent._xmouse. Both changes would have worked fine if the code were attached to the clip itself (inside a clip event); however, here you must be more explicit. By the way, if you wanted to address the _parent timeline, you could change line 2 to this._x=this._parent._xmouse.

Here's an abbreviated version of some code you'll write in Workshop Chapter 4, "Building a Slide Show":

1 my_mc.change=-10; 2 my_mc.onEnterFrame=function(){ 3   this._alpha+=this.change; 4   if(this._alpha<0 || this._alpha>100){ 5     this.change=this.change*-1; 6   } 7 }

Basically, the enterFrame script (which executes 12 times a second if the frame rate is 12 fps), changes the clip's _alpha by an amount equal to the value of the change variable (initialized at -10). Then, if the _alpha gets too low (less than 0) or too high (greater than 100), the change variable changes sign (gets multiplied by -1). You might wonder why I didn't use an onLoad callback in this example that is, change line to read:

my_mc.onLoad=function(){this.change=-10;}

Although this makes total sense, it doesn't work! In fact, the instance of my_mc that I placed on the Stage will trigger its own load event before this script executes. That is, this script (which defines what's supposed to happen when the clip's onLoad event triggers) will never trigger because it's too late the clip will have already loaded. The onLoad event is usable. You just have to place the script in a place where it will be invoked early enough. In the "Registering Custom Classes" section later in this chapter, you'll see how code placed on a frame inside a Movie Clip symbol and inside the #initclip and #endinitclip keywords will do exactly that: execute before the clip instance loads.

There's more to know about defining callbacks for clips. Because the main timeline is really a Movie Clip too (_root), we can identify a callback function for _root, as follows:

_root.onEnterFrame=function(){my_thing._x++;}

Assuming there's a clip, button, or text field with the instance name my_thing, this script will move it every 1/12 of a second.

Finally, here's an easy example that helps to introduce the next few sections:

my_btn.onRollOver=function(){this._alpha=50;} my_btn.onRollOut=function(){this._alpha=100;}

In this example, the button instance called my_btn will drop to _alpha 50 when you roll over, and come back to 100 when you roll out. This example eliminates the need to edit the contents of the button (to make an Over-state). Interestingly, if the instance called my_btn were actually a clip, it would still work. Compare this to editing scripts for clips and buttons using the traditional method (that is, selecting the object and opening the Actions panel). Only mouse events are available for buttons, and only clip events are available for Movie Clips. However, the list of events that can have callbacks identified is the same for buttons and clips (see Figure 14.1).

Figure 14.1. Traditional button and clip events (right) are all available for callbacks functions (left).

graphics/14fig01.gif

In my opinion, it's easiest to simply create Movie Clips and treat them as buttons by identifying callbacks for appropriate states (such as onPress). As you'll see in the following section, however, other issues arise.

Note

By now you should be familiar with how the keyword this refers to the current timeline or, when used inside a function, refers to the function itself. That poses an interesting and confusing issue when defining callbacks. Specifically, the this of a callback function refers to the clip or button to which it's assigned, but normally this refers to the current timeline. That is, in the callback form myButton.onPress=function(){ trace(this);}, you'll see the button instance (myButton) in the Output window when it's pressed. However, the equivalent conventional code attached right to a button, on(press){trace(this);}, will display the timeline where the button resides.

This issue means that you can't always add this in front of a variable or clip name to explicitly refer to the current timeline. For example, these two code snippets are different:

myButton.onPress=function(){   this.myVariable=100;   this._alpha=50; } myButton.onPress=function(){   myVariable=100;   _alpha=50; }

In the first case, the variable and property affected (myVariable and _alpha) are both that of the myButton instance. But in the second case the variable and property affected are those of whatever timeline where this code resides. It's really not a huge deal when you think about it, but it often messes me up. It's not a bug after all, you need ways to reference the current timeline or the instance generically. You just can't assume this always means "the current timeline."

How a Movie Clip Becomes a Button

Assigning callback functions has several benefits. On the surface, assigning callbacks is mainly just a different approach than putting code right on buttons and clips. It's more than that, however, because other objects have no other alternative (text field instances, sounds, and even data that is loaded externally to name a few). Even more than serving as an alternative to the traditional way of using buttons and clips, assigning callback functions can make a clip behave like a button. Quite simply, the range of events available to clips includes those once reserved only for buttons. Buttons are (or were) great because they include such events as press and release, whereas clips have only mouseDown and mouseUp and these trigger regardless of where the user clicks. Anyway, it's nice to skip buttons all together by assigning callback functions for whichever event you choose.

Like most things, a few issues crop up. In the preceding section, I showed how you could make a clip act like a button with this code:

myClip.onRollOver=function(){this._alpha=50;} myClip.onRollOut=function(){this._alpha=100;}

I'll show you other ways besides an alpha change to treat the rollover effect in a minute. Notice that with just one more line of code, the clip can respond to a mouse press:

myClip.onPress=function(){trace("you pressed");}

(Naturally, you could replace the trace command with some meaningful code.) That works great. What's really interesting is that by simply assigning a callback on a clip to any of those "button-like" events, Flash automatically displays the hand cursor when the user rolls over (just like buttons). It's simple to turn off (for clips or buttons) by setting the showHandCursor property to false, as the following code shows:

myClip.showHandCursor=false;

By the way, there's also a similar property, called enabled, which not only turns off the cursor, but also causes the button (or clip acting like a button) to be inactive (for example, my_btn.enabled=false).

At this point, I've shown you the basics of how a clip can act like a button. But there's more namely, how to make the clip look like a button. You should know that traditional button symbols automatically have an Up-, Over-, Down-, and Hit-state (see Figure 14.2). Naturally, if a clip is to replace a button, it will need to have all these states as well.

Figure 14.2. Traditional buttons symbols have four states: Up, Over, Down, and Hit.

graphics/14fig02.gif

Unless you're happy with button states that simply change a clip property (like _alpha above), you'll want to tap into those four states. To have your clip use the button states, you just need to create frame labels that read (exactly): _up, _over, and _down. (We'll get to the Hit-state in a minute.) That's pretty easy. You'll find out pretty fast that (like any clip with multiple frames) you'll need a stop() script in the first frame. But, as long as your symbol has those three frame labels, and somewhere you identify a callback (for at least one mouse event), your clip will do what a button can. What's cool is that you're not limited to one frame per state, although you'll have to place a play() script in the labeled frame if you want them to play. Figure 14.3 shows the insides of such a clip.

Figure 14.3. A Movie Clip symbol treated like a button will need the labels _up, _over, and _down to appear like a button.

graphics/14fig03.gif

Finally, the Hit-state: Inside a button, the graphics in the Hit frame define the shape that's clickable. In clips turned buttons, you can't simply create a label called _hit (which certainly was the first thing I tried). Rather, you have to assign the Hit-state area with a script. The hitArea property defines the clip that will serve as the clickable shape. The form is buttonClip.hitArea=otherClip, where otherClip is an instance name of a clip. (And that other clip doesn't even have to be visible.) For example, suppose that your clip instance in the main timeline (myClip) contains only some text, and you also have a nice big box shape instance (box) placed underneath myClip. The following code (placed in a keyframe) will make the clip act like a button, use the box as the Hit-state, and then make the box invisible:

myClip.onPress=function(){trace("it works");} myClip.hitArea=box; box._visible=false;

The first line just makes the clip behave like a button; that is, the hand cursor and any frames named _up, _over, and _down will work properly. The second line specifies that box will define the clickable area. Then, the third line hides the box.

Note

The process for defining the hitArea clip is fairly straightforward, but I have a couple of warnings. First, you should definitely consider making such a clip even though it's optional. Without it, the default hitArea that gets used is whichever state is currently visible. If the area occupied by the Over-state is significantly different than the Up-state, the result could be that the button will blink (that is, roll over the button see the Over-state). If that Over-state means that the graphic has moved and is no longer under the button, however, you've really rolled off and you're back to the Up-state (where you roll again) hence, the blink. This never happens with regular buttons because the last keyframe is always used if no Hit-state is created.

Second, it's likely that you'll want to hide the hitArea clip that's okay. It's also alright to nest that clip inside your "button clip" symbol. However, regardless of whether that hitArea clip is visible, it must be present in all the state frames (_up, _over, and _down). Just put it in its own layer. Setting the hitArea points to a clip, not just to the shape of that clip; therefore, if it moves or changes, the current shape is used for each state.

Although making your clips act like buttons might look like a little extra work, once you get the hang of it, I'm sure you'll find it convenient. We'll come back to such "clip buttons" in the "Registering Custom Classes" section later in this chapter. For now, let's review and look at a few of the limits to callback functions.

Limits to Callback Functions

Callback functions have a particular limit in that only one function can be defined for any one event on a particular instance. That is, if your button instance named my_btn has an onPress event assigned to a function that displays "hi," and then you decide to assign a new callback to the onPress event (for example, to display "hello"), the old function will be wiped away.

In addition to having only one function per instance, each instance must have its event specified individually. It's not as though two instances can't trigger the same function (that's like the first set of examples where the function name was specified instead of all the code being placed "in line"). However, every instance must have an instance name, and you have to account for each one individually. The name can be verbose (if you have a lot of buttons), but you can always refer to clips dynamically (by using the familiar path[stringName] form). For example, the following code assigns a callback function to the buttons my_btn_1 through my_btn_10:

for(i=1;i<11;i++){   this["my_btn_"+i].onPress=function(){_root.play();} }

This looping technique can be particularly useful when you want to disable or enable a bunch of buttons at once just change the line inside the loop to this["my_btn"].enabled=false;.

There's not much more to it than to understand these two limits: Instances can have only one callback per event at a time, and the callbacks of each instance have to be defined individually. If you find yourself wanting an instance to trigger more than one function every time a particular event occurs, you must find another way. The callback function can simply trigger more than one standard function if you want. Another way is to use "listeners." You might recall from Chapter 10, "Keyboard Access and Modifying Onscreen Text," that listeners give you a way to mix and match the response to a particular event. (Listeners are reviewed later in this chapter, too.) The big catch is that most events (to which you can define a callback function) don't work as listeners but some of them do. (More later.)

In many ways, the limits of callbacks make some tricky code very simple. For example, say you want a toggle button that pauses and plays alternatively. Check out this concise code:

1 function stopTime(){ 2   stop(); 3   my_btn.onPress=pauseTime; 4 } 5 function pauseTime(){ 6   play(); 7   my_btn.onPress=stopTime; 8 } 9 my_btn.onPress=stopTime;

The stopTime() function stops the play head and then reassigns the my_btn instance's onPress event to the other function (pauseTime). In line 5, you see the declaration for the pauseTime() function, which basically does the opposite. Finally, line 9 gets things started by assigning the my_btn instance's onPress event to the stopTime function.

There's no problem in overriding the events in this way. That brings up the fact you can effectively "turn off" a callback by assigning the event to equal null,as in my_btn.onPress=null.

Finally, there is a way to automatically assign the same callback function for each and every instance of a particular object type (even if they're not yet on the Stage). We'll discuss this technique more in the "Overriding Built-In Methods" section later, but it's worth showing briefly now.

The form to override an event (or method) is:

theObjectType.prototype.theEvent=theFunction

The only part used verbatim is prototype; everything else is replaced. For example, if you want every button to change its alpha when the user's cursor goes over it, use the following code (but read the follow-up text for warnings):

Button.prototype.onRollOver=function(){this._alpha=50;}

This should look somewhat familiar to adding methods to the prototype of homemade objects, the way you did in the preceding chapter. However, here we're saying that we want to affect the Button class (that is, the master definition of all buttons). Every button (or any object) has all its methods stuffed in the prototype property. Initially, the values for onPress, onRollOver, and so on are simply undefined. By executing a line of code like the preceding, you're saying that you want every button's initial value for onRollOver to be assigned the value of that function (which sets the individual button's _alpha to 50). The only warning is that executing this code will wipe away any other functions previously stored in a particular instance. Similarly, any subsequent callbacks assigned to this event will wipe away this one. Think of the preceding code as providing a default value for the onRollOver event. (More about this overriding later.)

Registering Custom Classes

Although we looked at a lot of theory and practical uses for homemade objects in the last chapter, the topic of registering custom classes fits well in this chapter. In fact, any time that you create a constructor function, you're really defining a class. However, homemade objects (despite being a lot of fun) are never visualized unless you take steps to affect onscreen clips and text. That is, an object is simply stored in a variable but you don't see anything unless you (by hand) tie it to something onscreen. In fact, no instances of your homemade object (class) exist unless you instantiate them.

The new registerClass() method makes it possible to create a class (or definition of your homemade object) that automatically inherits all the features of the built-in Movie Clip class. At the same time, you associate a particular symbol with your class so that every time that symbol is instantiated (dragged out of the Library), you'll be instantiating an object of the homemade class. This means you can design a homemade object that is automatically linked to something visual and it will behave just like the parent object but with additional methods and properties that you define. For example, you can make a Button class that is just like any other button clip, but which automatically displays a checkmark that toggles on and off. We'll do just that. Not only will the onPress event trigger a callback to display or remove the checkmark graphic, but onPress will also trigger any other function you want and that function can be different for each instance of the button. That is, we'll effectively get around the fact that one button can have only one onPress event callback. If it sounds to you like the "super button" we're going to create is sort of like a component, you're right. The point is, the registerClass() method is an integral part of how components can supplement your movies without conflicting what's already present.

Basic Form

Even though registerClass() is used extensively in the creation of components, you don't have to be making a component to use it. (And, for that matter, you don't have to use registerClass() if you're making a component.) We won't get into components until it makes sense to do so both later in this chapter and then again in Chapter 15, "Components."

The three steps to register a custom class are as follows:

  1. Define the constructor function for the class.

  2. Establish the class is a child of the built-in Movie Clip class (that is, inherit everything from it).

  3. Register the class.

Here's a starter script:

1 function MyClass () { } 2 MyClass.prototype = new MovieClip(); 3 Object.registerClass("symbolID",MyClass);

Line 1 is the constructor. You can place initialization scripts in the constructor, but you don't have to (you still need this minimum). (By the way, as a convention, programmers usually capitalize their class names.) Line 2 says that MyClass's prototype property is assigned the value of a new Movie Clip (which is really saying that it's a child of the Movie Clip class). Finally, line 3 registers the new class called MyClass (the one we've been fashioning) and ties it to a symbol with identifier symbolID. Remember, an identifier is different than the symbol's name it's specified from the symbol's linkage setting (accessible from the Library's option menu).

Note

Notice how I defined the constructor for a class:

function MyClass () { }

This is just like how we made constructors in the preceding chapter. I think it's clearest in this form. However, you may also see programmers use the following code instead:

MyClass = function () { }

Although it's not exactly the same, it performs the same. It's worth knowing because you'll probably see it both ways.

In practice you'll likely include more code than the minimum. Specifically, you'll define callback functions for any event you desire. The callbacks will be tied to the prototype property so that they will be common to all instances of the object you design. Such definitions must appear after the prototype is set equal to a new MovieClip() (line 2 above) and before the class is registered (line 3 above) For example, notice the new line of code (line 6) that will cause all instances to move when the enterFrame event occurs:

1 //constructor: 2 function MyClass () { } 3 //inherit MovieClip class attributes 4 MyClass.prototype = new MovieClip(); 5 //override the enterFrame event 6 MyClass.prototype.onEnterFrame=function(){this._x++;} 7 //register the class 8 Object.registerClass("symbolID",MyClass);

The form is always "class dot prototype dot event equals function," and prototype is used verbatim. You actually don't need to define the callbacks before registering the class. If you define callbacks for the prototype of your object in a later script, it still works (it just won't start working until you execute the script). What's really wild is that once the class is registered, you can address it from anywhere in your movie. It's not like how you address a variable name by using the path to the variable. It's just like how you can always say MovieClip.prototype.onEnterFrame=function(){} to affect every instance of Movie Clips (and descendants, including instances of the MyClass class). Every instance of the symbol with the identifier "symbolID" will automatically be an instance of the MyClass class and, thus, be affected by anything you do to that class.

Before I show a practical example, I need to emphasize the importance of the sequence of scripts. If you intend for clip instances to be instances of your custom class, you must register the class before any instances are on the Stage. For example, say you place an instance of the symbol with the identifier, "symbolID," on the Stage and give it an instance name of myInstance. If in the same frame you execute the three minimum lines of code (from the preceding starter script), you will see that the expression (myInstance instanceof MyClass) is false and that (myInstance instanceof MovieClip) is true. That is, the instance is part of the MovieClip class (not the MyClass class). The problem is that if an instance is already on the Stage when you execute the code, that instance's class has already been decided. Imagine you are born in the U.S. to American parents. If later, your parents change their citizenship to another country, you're still an American citizen.

All you need to do is to register the class before any instances are on the Stage. One way is to simply place at least those three lines of code in the first keyframe and then place instances on the Stage in the second frame or later. (To verify that it works, use the instanceof operator, as in trace(myInstance instanceof MyClass), which should display true.)

The other way to register a class before the symbol is instantiated is by using the #initclip event. It's not really an event, but it enables you to execute scripts before the symbol is instantiated. The way it works is you place all your code between #initclip and #endinitclip (sort of like putting code between the opening and closing curly braces). You must place this code in the first frame of a symbol. So, just go inside the master symbol (the one with the identifier "symbolID") and place the following code in the first frame:

#initclip function MyClass() { } MyClass.prototype = new MovieClip(); Object.registerClass("symbolID",MyClass); #endiniticlip

Now if you place an instance of this symbol on the Stage and give it an instance name myInstance, the expression myInstance instanceof MyClass will be true (and myInstance instanceof MovieClip will be false). Even more exciting than seeing true or false in the Output window is the fact that you can execute a line of code like the following to affect every instance of this class (without affecting any regular clips that happen to be present):

MyClass.prototype.onEnterFrame=function(){this._x++;}

Because I know seeing clips move across the screen might still be less than thrilling, I've created the following practical example of what we've seen so far.

Practical Example: Registering a Class

This example shows a button with an automatic checkmark. (You can download the source files for this example from www.phillipkerman.com/actionscripting.) First I make a new Movie Clip symbol and give it a linkage identifier of "myButton." Then I make three named frames (_up, _over, and _down) where I draw the different states for my button. Still inside the symbol, but in a separate layer (that spans the whole timeline), I draw a shape to become the hit area, convert it to a Movie Clip symbol, and give it an instance name of hitShape. Then I draw a checkmark, convert it to a symbol, and name the instance checkMark. Figure 14.4 shows the clip structure.

Figure 14.4. Here are the insides of the clip that will act like a button.

graphics/14fig04.gif

Finally, I place the following code in the first frame of the button symbol:

 1 stop();  2 #initclip  3 function MyButtonClass () { }  4 MyButtonClass.prototype = new MovieClip();  5  6 MyButtonClass.prototype.onLoad = function(){  7   this.hitArea = this.hitShape;  8   this.hitShape._visible = false;  9   this.checked = false; 10   this.checkMark._visible=false; 11 } 12 13 MyButtonClass.prototype.onRelease = function(){ 14   this.checked = !this.checked; 15   this.checkMark._visible= this.checked; 16 } 17 18 Object.registerClass("buttonClip",MyButtonClass); 19 #endinitclip

Line 1 stops the timeline from continuing to the other frames (for the other states). Once this clip has a mouse event (onRelease), it automatically will display those frames. Line 2 begins the script that executes before the symbol loads. Line 3 is the constructor function (not much there, eh?). Line 4 says that this symbol will inherit all the built-in properties that clips have. Lines 6 and 13 define a couple of callbacks for the prototype property of this class (onLoad and onRelease). Again, the callbacks are attached to the prototype property because I want every instance of this class to share them. In onLoad, I do four things: Line 7 specifies the clip to be used for the hitArea (the clip I named hitShape), line 8 makes that clip invisible, line 9 sets the homemade variable checked to false, and line 10 makes the checkMark instance invisible (at the start).

The onRelease callback function first changes the checked variable from false to true (or true to false) in line 14. Then it sets the _visible property of the clip checkMark to false or true. Basically, onRelease is just a toggle. Finally, line 18 registers the class. At this point, I can drag as many instances of the clip on the Stage, and they all will have an automatic checkmark.

There's nothing wrong with the MyButtonClass. However, there's nothing all that special about it, either. In fact, it would be easy to pull off the same effect without defining a class. I could put an invisible button inside a clip and do the same basic thing without much trouble. This was just a start; once you have the class designed, adding to it is easy. The first thing we should do to this class is to make it so that the onRelease event can do more than just display or hide a checkmark. If you have an instance of this clip on the Stage and place code right on the clip, it won't interfere with the onRelease callback inside the class definition. That is, the following code works without a problem:

on(release){   trace("do something"); }

However, that's the old way (of putting code right on an instance). If the instance name is myInstance, the following code will actually override the onRelease event in the class for just that instance (and the checkmark will fail):

myInstance.onRelease=function(){trace("do something");}

Instead of worrying about overwriting the class's onRelease event, we can just add to the code in the class definition. Specifically, we'll make the onRelease event both display the checkmark and invoke a function. However, if we just add a line of code such as _root.myFunction(), every instance will trigger the same function. In order to have multiple instances that do different things, we need to turn the clip into a component. The following chapter explores the process in greater detail, but I'll show you how to apply it here. The idea is that after you drag a particular instance of the symbol onto the Stage, you specify the name of the function you want to be triggered. Here is an extra line of code that goes at the end of the onRelease callback function in the class definition (after line 15, shown earlier):

_root[this.handler](this.checked)

Although this might not look traditional, it's actually quite simple. The idea is that the name of the function will actually be a string (stored in the variable this.handler). Because we can't just invoke a function with a string name, we use the familiar technique of placing the string in brackets and preceding it with the path to the function. Also, when invoking a function, we have to follow the function name with parentheses. Just to make it more interesting, I'm passing as a parameter the value of the checked variable. Now all we need to do is to define a function in the main timeline of the movie, and then figure out a way to let the author using this symbol specify a unique value for the handler variable. Here's the code you can put in the main timeline:

function toggleTime(whichWay){   if(whichWay==true){     play();   }else{     stop();   } }

This way toggleTime(true) will make the movie play and toggleTime(false) will make it pause. Again, we could have easily made the main movie stop or play from inside the class definition, but we're going to have only one instance of the button triggering the toggleTime() function. Other buttons can trigger other functions that we declare. All we need to do now is to give the using author a way to specify a different function for each instance of the button. Select the symbol in the Library and choose Component Definition from the options menu. Then, in the dialog box, click the plus button and replace varName with handler. Click OK and the clip becomes a component. At this point, you can select an instance of this symbol on the Stage and, through the Parameters tab in the Properties panel, set the value toggle time for handler (see Figure 14.5).

Figure 14.5. An instance of the component we created can have its handler variable set via the Properties panel's Parameters tab.

graphics/14fig05.gif

This was actually a pretty fancy component but you'll learn more later. The features we add to the button in the next section will work regardless of whether the symbol is actually a component.

Note

Before we move on to adding properties to our custom classes, I should probably note that you can do more than define callback functions for standard events such as onPress. When designing your classes, you'll likely want to create methods. These can be accessible from outside scripts (so called public methods) or they might be only for use inside the class. Whether they're public or private is just a matter of how they're used. The syntax for methods is the same as that for defining a callback (but you come up with your own name) something like: MyClass.prototype.someMethod=function(){//the code}. The point I'm trying to make is that you're not limited to just extending the built-in features; you can make your own methods. All the knowledge from the preceding chapter can be easily applied here.

Adding "Getter/Setter" Properties

Even though I recommend thinking of homemade variables as homemade properties, they aren't quite the same thing. The syntax to refer to or change them is the same, but they differ in that built-in properties have an immediate (and usually visual) result when they change. That is, change the _alpha property and you see something. That's not the case if you change a homemade variable. All this leads to the fact that when you define classes, you can also define true properties.

These properties are called getter/setter properties. You can get them (to see their values) and set them (to change their values). Flash does not constantly monitor the getter/setter properties. Actually, they are not calculated unless a script tries to set or get them. Anyway, when you make your own properties, you need to define both what should happen when a script sets them and how the value is derived when a script tries to get them. For example, if someone changes the value for the checked variable, aren't you supposed to see something happen? (In the Button class above, if you change an instance's value of checked to anInstance.checked=true, you wouldn't "see" anything happen.)

The basic form of creating a property is:

Class.addProperty("property", getFunction, setFunction);

The only part that is used verbatim is addProperty. Basically, you name the property you want to create (in quotes) and identify both a function that executes when someone tries to get the value and a function for when he sets the value. You can actually replace getFunction and setFunction with full function definitions "in-line" (but I thought that would be too messy as an example). The form of those two functions is standard, but their jobs are very specific. Namely, getFunction should end with a line that returns the new value of the property. setFunction automatically comes with a parameter (the value of which is the new value for the property at hand). It's probably easiest to see this in a practical example. Suppose that you want to create a new property called _left that contains the left side of any Movie Clip. In this case (and in order to skip all the registerClass() stuff), we'll just extend the built-in Movie Clip class, as follows:

1 function getLeft(){ 2   var theLeft = this._x - (this._width/2); 3   return theLeft; 4 } 5 function setLeft(toWhat){ 6   this._x = toWhat + (this._width/2); 7 } 8 MovieClip.prototype.addProperty("_left",getLeft,setLeft);

Notice that line 3 returns the calculated value. That is, if there's a clip instance called myClip and some script says trace(myClip._left), the value returned (after the word return) is what appears in the Output window. Also notice that the setLeft() function (line 5) expects to receive a parameter (given the name toWhat in this case). This is just like a freebie that you get with setter functions. After all, if a script later tries to set the _left property (for example, myClip._left=100), we need a way to know what value the property is to become (100). Finally, the last line uses addProperty() to specify that the property name will be _left, the getter function will be getLeft, and the setter will be setLeft. We're using the addProperty() method on the Movie Clip's prototype to affect all instances of clips. This is a really useful example. After entering the preceding code, you can set or get any clip's _left property. MyClip._left=100 will move the clip so that its left is at 100. This example should give you a few ideas of what's possible. (Note that this particular example assumes that the clip's default registration point is the center.)

Here's another practical example. Take the preceding MyButtonClass example. It's easy enough to tint the checkMark instance, but it will always take two steps (create the Color object, and then use the setRGB() method). Let's make a new property called shade. You can place the following code right before the registerClass() method:

MyButtonClass.prototype.setColor=function(toWhat){   this.c=new Color(this.checkMark);   this.c.setRGB(toWhat); } MyButtonClass.prototype.getColor=function(){   return this.shade; } MyButtonClass.prototype.addProperty("shade", MyButtonClass.prototype.getColor, graphics/ccc.gifMyButtonClass.prototype.setColor );

By adding the functions setColor and getColor to the MyButtonClass's prototype (as well as using addProperty on the class's prototype), you give every instance of this button clip a shade property. If you have an instance called myInstance, you can say myInstance.shade=0xFF0000; to turn the checkmark red.

Adding properties is a bit more involved than just making up homemade variables. You have to script everything that you expect to happen when the property changes. Once you set things up, however, it can be really fun.

Note

I think this is a good time to demystify the term polymorphism. Polymorphism is nothing more than two different classes responding to the same named property or method with different responses. For example, both a circle and a square can have an area property. Calculating area for a circle (PI r2) is different than calculating area for a square (side2) but they both have an "area." A simple definition of polymorphism might be "the same result, but derived differently."

One way to apply polymorphism to what we've seen so far would be to create a getter/setter property called shade for an entirely different class. Instead of changing the color of the checkmark, you could have it tint the clip itself. The point is that you would just modify the other class's idea of what "shade" means. Polymorphism really is an easy concept. Applying it to real projects is a little more work.

Inheritance with Classes

We've seen that when creating a custom class, you can make your class automatically inherit the standard methods and properties of a plain Movie Clip. You can take this even further. For example, you can design one class (that inherits Movie Clip attributes), but then make other classes that inherit from the first class (and, thus, they'll inherit from the Movie Clip class as well). That is, you might want several classes that are unique in many ways, but similar in a few ways too. You can define all the ways they're similar in your "parent" class and then let all the different "children" inherit from the parent. Of course, if the parent inherits everything from the Movie Clip class, all the children will do so as well.

To make inheritance work, the parent class is defined first so that when the children say, "Inherit from the parent," the parent already has been defined. All you really need to know is that you can change #initclip to read #initclip 0 and #initclip 1. The lower numbers execute first. To make the children inherit from a homemade class (not from the Movie Clip class), change the part that reads

MyClass.prototype=new MovieClip();

to

MyClass.prototype=new MyParentClass();

It's important that MyParentClass has been defined when this executes. (And the #initclip numbers control that.)

Here's a relatively simple example in which the parent clip (with a linkage identifier of "parent") contains the following code in its first frame:

#initclip 0 function ParentClass (){} ParentClass.prototype=new MovieClip(); ParentClass.prototype.setColor=function(toWhat){     this.c=new Color(this);     this.c.setRGB(toWhat); } ParentClass.prototype.getColor=function(){     return this.shade; } ParentClass.prototype.addProperty("shade",ParentClass.prototype.getColor, ParentClass. graphics/ccc.gifprototype.setColor); Object.registerClass("parent", ParentClass); #endinitclip 0

There's nothing new here except the #initclip 0 (to make sure this code happens first at least before #initclip 1). You might notice that this example has no methods or callbacks, just a getter/setter property. You can certainly include more than this. Anyway, the code for the child class (which will automatically include the shade property, as well as anything else that you define) looks like this:

#initclip 1 function ChildClass (){} ParentClass.prototype=new ParentClass (); Object.registerClass("child", ChildClass); #endinitclip 1

The things to notice here are that everything is in initclip 1 (not 0). Therefore, when line 3 executes, the ParentClass has already been defined (so this class has no trouble being a descendant). Finally, this clip's identifier is child. Naturally, you can add more methods, callbacks, or properties to this definition. You don't need to define the shade property, however, because it was automatically inherited from ParentClass in line 3!

Instead of droning on with another example, just wait until Workshop Chapter 9, "Creating a ToolTip Component," where we put some of this to practical use.

Listeners and Watchers

The next couple of sections cover topics not directly related to callbacks and classes. However, both listeners and watchers fit here nicely because they provide things that are either difficult or impossible to achieve with callbacks or classes. Specifically, the fact that you can have only one callback function defined per event is effectively circumvented with listeners. Although they're not the same as events, listeners are cumulative; therefore, you can have several listeners listening for the same event. Similarly, watchers get around a frustrating fact about getter/setter properties. Specifically, properties are not continuously updated. Only when a script says, "Hey, change this property" or "What's that property's value?" does Flash go and set or get that property. Although this way is more efficient, it's frustrating because you might want to monitor one property's value. If the value ever goes too high, for example, you might want to respond. It's not totally efficient to be continuously checking a property's value but watchers do that in an efficient way. The truth be told, you can't actually use watchers on getter/setter properties, and you can't use listeners on most events; nonetheless, they're useful and certainly worth studying as a comparison.

Listeners Basics

Listeners enable you to define which function will trigger when a particular event occurs. It's almost the same as callback functions, although the list of events that you can use as listeners does not include every event imaginable. For example, no button events (such as onPress) can be used as a listener. The advantage of listeners is that they're easy to add and remove and they don't conflict with each other. That is, one event (say a text field instance's onChanged event) can be listened for and trigger a particular function. You can easily add more listeners or other events without affecting existing listeners.

To create listeners, you always follow the same basic form: store a generic object in a variable; identify callback functions to events attached to that variable; and then add the listener to the instance to which you want to listen. Here's an example with dummy names:

myListener=new Object(); myListener.eventName=function(){trace("event happened");} someInstance.addListener(myListener);

You can use any legitimate variable name in place of myListener. Also, you must replace eventName with one of the event names that may be used for the someInstance object type. Because there are different events for text fields than, say, the Key object, you must use an appropriate event name. Finally, in the last line, you replace someInstance with the instance name you want to listen to. Alternatively, you also can add listeners to classes, such as the Key object class.

In such cases, you just replace someInstance with the class name (for example, Key.addListener(myListener)).

We did a few nice practical examples of listeners in Chapter 10 that applied to the Selection, Key, and TextField objects. Here's an example of using a listener on the Mouse object that requires that you have a clip instance (named "clip") in the main timeline. The following code goes in a frame script:

myListener=new Object(); myListener.onMouseMove=function(){   _root.clip._x=_root._xmouse;   _root.clip._y=_root._ymouse;   updateAfterEvent();   } Mouse.addListener(myListener);

In practice, this example would probably be easier if you typed the following (equivalent) instead:

clip.onMouseMove=function(){   this._x=_root._xmouse;   this._y=_root._ymouse;   updateAfterEvent(); }

Using listeners always requires at least three steps (creating the generic object, assigning a callback, and then adding the listener). Once you've created the listener, you can modify it in a few ways. If you can add more callback functions to the listener object, the updates appear immediately. For example, using the example above (not the alternative just shown), you could use the following code to make the clip rotate every time the user clicks:

myListener.onMouseDown=function(){_root.clip._rotation++;}

There are two things to notice. First, adding a new callback to the myListener variable can happen at any time even after the addListener method. Also, the following code will not work because this will be the function itself, not the clip:

myListener.onMouseDown=function(){this._rotation++;}

Finally, you can also override any callback currently stored in an event of the myListener variable. That is, another line that begins myListener.onMouseDown= will replace the function already in that event.

Removing a listener is easy, you just say:

Mouse.removeListener(myListener);

(Replace Mouse with the object or instance to which you added the listener in the first place.) I should mention that in addition to supplementing, overriding, and removing listeners, you can add additional listeners that respond to the same event. For example, even after you have added the myListener listener to the Mouse object, you can fashion a new one and have it respond to the onMouseMove event as well. All you have to do is use a new variable name for your listener object, as follows:

myOtherListener=new Object(); myOtherListener.onMouseMove=function(){_root.clip2._x=_root._xmouse;} Mouse.addListener(myOtherListener);

In this case, the other clip instance, clip2, will track the mouse's x coordinate. That you are able to turn listeners on and off at will makes them very convenient. The only trouble with listeners is that there aren't enough of them. (Like I say, you won't find any for clips or buttons.)

Watchers

Setting up a watch on a variable is a way to identify which function you want to execute every time that variable changes. It works only on variables or properties in generic objects, not getter/setter properties but it's still powerful.

The form gets a little weird, so let's look at it one step at a time. The basic form is as follows:

this.watch("myVariable", responseFunction);

You can replace this with another address if the variable you're watching is in a different timeline. The variable you're watching (myVariable) appears between quotes (which is weird because you'd normally expect variables to appear without quotes). Finally, responseFunction is replaced with either the name of a function declared elsewhere or (preferably) with an inline version. responseFunction (which can be named anything you want) is interesting because it automatically includes up to four parameters. Here's a sample version (that would have needed to appear above the line of code containing the watch method above):

function responseFunction(id, oldVal, newVal, userData){   trace("var"+id+" changed from "+oldVal+"to"+newVal);   return newVal; }

Before I explain the parameters, let me point out something important. Namely, the last line within this function should always be something equivalent to return newVal (or whatever you use for the third parameter name). That is, if you're watching the variable variableName and a script executes myVariable=10, you probably want myVariable to have the value 10. If you leave out that last line (return newVal), the value of myVariable will be undefined. If you do something weird like return newVal/2, the code myVariable=10 will actually assign its value to 5.

Regarding the parameters, think of these as freebies. Any function identified as a "response function" to a watch will always have up to four parameters. You can name them whatever you want, but they always appear in this order: The first parameter (id) is simply a string version of the variable being watched. The second parameter (oldVal) is the value of the variable before the imminent change. That is, if myVariable were currently 4 and the code myVariable=10 executed, the value for oldVal in the response function would be 4. It's kind of weird, but realize that the response function triggers the instant any code tries to change the variable being watched. The third parameter (newVal) is the value the variable is trying to become. You can think of this as the value that the variable is about to become, but remember that last line of code can effectively change destiny. Finally, the fourth parameter (userData) isn't very important. It's just an optional feature that comes into play only if, when setting the watch, you include an additional value as a third parameter of the watch method. For example, you could change the initial watch method to read:

this.watch("myVariable", responseFunction, 123);

Then, once inside the responseFunction, the value 123 would be received as the value for the fourth parameter. A practical use for this might be to tell you who set up the watch. That is, you could have two different buttons that set up the same watch (on the same variable). When executing the response function, however, you might want to know who set it up in the first place. (This will show up in the following example.)

Before we look at a practical example, let me mention the unwatch() method. The form is pretty simple:

this.unwatch("myVariable");

Just remember to keep the variable name in quotes.

Applied Example Using Watch

Here's an example of controlling text input. I picked this example having just completed a project in Flash 5 that would have been infinitely easier in Flash MX. In that project, users could enter values into several Input Text fields, each of which limited the range of values the users were allowed to enter. Any time that they entered too high or too low of a value, they were immediately instructed to fix their mistake.

This example will cause a clip to jump to frame 2 when the entered value is above 100, or to frame 3 when the value is below 10. If the value is between 10 and 100, the clip will stay on frame 1. Start by making a clip that has a green circle in frame 1, text that reads "too high" in frame 2, and "too low" in frame 3. Also, place a stop() in the first frame of this clip and give it an instance name of clip. Next, make an Input Text field and associate it with the variable name myField. (Normally, I would recommend using a field's text property rather than the "Var:" option in the Properties panel.) Finally, put the following code in the first frame of the main timeline:

 1 function checkField(id, oldVal, newVal){  2   _root.clip.gotoAndStop(1);  3   if (newVal>100){  4     _root.clip.gotoAndStop(2);  5   }  6   if (newVal<10){  7     _root.clip.gotoAndStop(3);  8   }  9   return newVal; 10 } 11 this.watch("myText", checkField); 12 myText=10;

The first line of interest is line 11, which says that any time the variable myText changes, you want the checkField function to execute. In checkField, line 2 assumes that the value is within range and makes the clip jump to frame 1. Then the two if statements (lines 3 and 6) check whether the new value is above or below the range (and then make the clip jump to the appropriate frame number).

You might think that a simple callback function (for example, the Input Text field's onChanged event) would be enough to pull off the same effect you'd be right. However, consider that you might give the user other ways to change the variable myText. For example, you might have two buttons: one that increases the myText variable and one that decreases it. (It turns out that the onChanged event would still work, but only because we're using the Var: option with Input Text field.) The point I'm trying to make is that watchers are good for when you don't want to worry about all the different ways a variable can change you just want to make sure that you know about it when it does.

One thing about the preceding example is that it's sort of hard-wired to the range of 10 100. To show a use for the fourth parameter (userdata), I've made the following modification to the code:

 1 function checkField(id, oldVal, newVal, range){  2   _root.clip.gotoAndStop(1);  3   if (newVal>range[1]){  4     _root.clip.gotoAndStop(2);  5   }  6   if (newVal<range[0]){  7     _root.clip.gotoAndStop(3);  8   }  9   return newVal; 10 } 11 this.watch("myText", checkField, [10,100]); 12 myText=10;

Notice first that an additional value is passed (in line 11) specifically, an array [10,100]. (Because this option allows for only one extra value to be passed, I cheated and put two values in one!) Then, in line 1, the checkField function refers to that extra parameter as range. Finally, in lines 3 and 6, I extract the values in the first index and zero index to determine the range. You can pass any kind of data you want for that extra parameter a generic object, for example.

This example should give you some ideas. Keep in mind that watchers are useful for many variables, not just ones that the user types. Remember, also, that you can only watch homemade variables, not getter/setter properties.

Overriding Built-In Methods

We had so much fun earlier writing our own callback functions that I failed to mention that you can also override many of the methods in the built-in objects (such as Array and Movie Clip). It works much the same way as assigning callback functions on the prototype property of homemade classes. The form is still:

Class.prototype.methodName=function(){}

For example, if you had the twisted thought of reassigning the standard play() command, you could use the following:

MovieClip.prototype.play=function(){this.stop();}

This works! Not that you'd want to make play() result in the same thing as stop(), but once the preceding code was executed, it would affect your whole movie. More practical situations arise, however. Suppose that you don't like the way charAt() always counts the beginning of the string with 0. You could override the built-in charAt() method (of the String object) with this code:

String.prototype.charAt=function(n){   return this.substring(n-1,n); }

Because the charAt() method accepts a parameter, the function declaration includes the parameter named n. And because charAt() returns a value, you see that the insides of my function return a value. Finally, the keyword this takes the place of whichever string is having its charAt() found. That is, you always precede the function with a particular string, such as "Phillip".charAt(1). If you put the code trace("Phillip".charAt(1)) before the overriding code, you'll see h in the Output window. However, if you place it anywhere after the code, you'll see P instead.

I don't recommend overriding the built-in methods in either of the ways I've shown, but you should understand that you can override them. Flash 5 included several particularly slow-working methods, which have all been improved in Flash MX. However, people in the Flash community often rewrite the code to improve one of these slow-performing methods. In addition, you can override built-in methods to make them perform differently or even fix an inherent bug (of which I don't believe there are any). In these cases, it makes sense to override those built-in methods. Also, if some quirky bug arises, it may be worth fixing it in this way.

Keep in mind that perhaps even more powerful than overriding the built-in methods is the fact you can add to the built-in methods. That is, reassigning a method in the object's prototype property wipes away the old method in that place, but adding new methods to the prototype property is no problem at all. For example, say you want all Movie Clips to have a scaleTo() method. You can create such a method with the following code:

MovieClip.prototype.scaleTo=function(n){   this._xscale=n;   this._yscale=n; }

From this point forward, you could say myClip.scaleTo(120) or myOtherClip.scaleTo(50). Actually, perhaps a better idea would be to simply add a property called _scale (currently there's only _xscale and _yscale) using the techniques discussed earlier. That would be good practice (and is something I've included in the source files online). My point here is not to say that these are the specific things you should do; rather, now that you have the foundation skills under your belt, you should feel free to come up with solutions that fit your needs.

Setting Scripts to Trigger on Schedule

All the wild stuff covered in this chapter should have you pretty excited. There's just one more topic that fits here even though it isn't really an "extension" of Flash. The setInterval() command enables you to identify a function that is to be executed automatically, at whatever frequency you desire. Normally, your scripts have to wait until an event triggers them. It's obvious that a script waiting for a mouse press may take a long time before it executes (especially if the user doesn't click), but even events, such as enterFrame and mouseMove, or simply reaching script in a keyframe are all limited by either how fast the movie's framerate is or how fast the user can move his mouse. That is, an enterFrame script that executes every 1/12 of a second might not be often enough. Conversely, if you only want a script to trigger every 4 seconds, it's somewhat difficult to do inside an enterFrame script.

For all the reasons listed previously, setInterval() is a great new feature. It's super easy to use. Here's the form:

setInterval(functionToTrigger, 1000);

Replace functionToTrigger with either a function's name or an inline version. Replace the second parameter (1000 in this case) with the interval in milliseconds between each execution. That is, this example will trigger once every second. If you want something to trigger faster, say once every tenth of a second, use 100. Here's a simple example:

setInterval(function(){trace("all's well")},1000);

In this case, the function declaration (the first parameter) appears inline. And, just like clockwork, you'll see "all's well" once every second in the Output window. For something a bit more practical, here's the code to make an instance named "clip" blink on and off every half-second:

function blink(){   clip._visible = !clip._visible; } setInterval(blink, 500);

In this case, I just moved the function declaration out on its own and referred to it by name in the setInterval() command. An optional feature of setInterval() is to pass values as parameters to the function that gets triggered. For example, you might want to recycle the blink() function and make it work for other clips too. You can pass parameters to the function being triggered by including a third parameter in the setInterval() command. Here is a modified example that blinks the circle instance once a second and the box instance once every other second:

function blink(what){   what._visible= !what._visible; } setInterval(blink, 1000, circle); setInterval(blink, 2000, box);

Notice that the blink() function now changes the visible property of whatever is passed in as a parameter. Then I set up two intervals that both trigger the same function but at different frequencies and with a different third parameter. The value of what becomes either circle or box.

Finally, although this is cool, you might want to stop the blinking (or whatever interval you design). You can turn off an interval by using the clearInterval() command. It's slightly tricky because Flash needs to know which interval you intend to clear. To identify which interval is which, you must store each inside a variable. Instead of saying setInterval(func,10), you say myInterval=setInterval(func,10). Then, you can later say clearInterval(myInterval). So, in the preceding example, I could change the last two lines to read:

circleInterval=setInterval(blink, 1000, circle); boxInterval=setInterval(blink, 2000, box);

Then, in a later script, you could say clearInterval(boxInterval) to stop the box from blinking. You could even get tricky and set up a third interval, which might not execute for 10 seconds, but whose job is to clear the other two intervals. For example:

function stopBlinking(){   clearInterval(circleInterval);   clearInterval(boxInterval);   clearInterval(stopper); } stopper=setInterval(stopBlinking,10000);

(Note that this code is added to the previous example.) You can test this script pretty easily. You might want to do some other "clean up" work in the stopBlinking() function. For example, you might want to make sure that both the circle and box clips are visible (or not depending on what you want).

There are two practical situations for using setInterval(): either you have a script that you want to execute very infrequently, or you have a script that you want to execute very frequently but bringing out a Movie Clip just to use the enterFrame event is too messy. Also, using setInterval() along with clearInterval() is sort of like cleaning up as you work.

Summary

This chapter covered some cool stuff. Nearly every feature showed you a different way of doing something that could be achieved other ways. Hopefully, you don't feel that the more traditional techniques covered earlier are all wasted now that you know "better" ways to do things. For one thing, these techniques aren't better unless you can make sense of them. Also, they're not better if you have nothing to which to compare them. In any event, I hope you're encouraged to try some out.

It might be overwhelming to put down the book and try all the different features shown in this chapter. My recommendation is that you try to repeat or expand upon the examples in this chapter. Also, if something comes to mind while you're reading this book, I suggest that you try your idea out using more than one technique. For example, if you thought, "Hey, I can make a clip that blinks every time the user types," you should try to achieve that effect using more than one technique. You could try assigning a callback function and then, regardless of whether it worked, go on to try the same exercise using a custom class. I think the hardest thing to learn is how to apply this information. When the material really starts to gel, you'll be able to pick the most appropriate feature. Until then, try as many as you can. Even if you discover a better way, time spent learning is time well spent.

CONTENTS


ActionScripting in MacromediaR FlashT MX
ActionScripting in MacromediaR FlashT MX
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 41

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net