Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Pages: 1 | 2 | 3 | 4 | 5

Creating Adobe Fireworks Auto Shapes

by senocular - 12/17/2004

 

Introduction

Fireworks Auto Shapes were introduced in Fireworks MX 2004. They are unique shapes that can be placed in a Fireworks document, either through drag and drop from the Shapes panel or through an Auto Shape tool within the rectangle tool in the Tools bar, that can at any time be visually altered directly within that document using their control points. These control points are small interactive buttons (usually looking like a yellow diamond) that the user can interact with and thereby cause a change of appearance in the shape. For example, in the star Auto Shape, you can drag a control point up and down to control how many points that star has.

Auto Shapes provide a great and easy way to create customizable shapes that can be altered based on user needs. But, best of all, custom auto shapes can be created and distributed by Fireworks users. The big question is how? If you'd like to know, keep reading.

 

The Making Of An Auto Shape

When an Auto Shape is placed in a Fireworks document, Fireworks dynamically creates the shape based on a JavaScript file that contains code defining it. This code describes how that shape looks and how users can interact with the shape once its created.

In fact, that JavaScript file is the only thing defining an Auto Shape, at least in terms of how it looks and reacts on the screen. Auto Shapes, though, also include a preview graphic file that is used to represent the Auto Shape in the Shapes panel or Tools menu.

So, given that Auto Shapes are created in JavaScript, making your own does require at least a basic understanding in programming. Luckily, however, JavaScript is the same language used in programming web pages. As such, many Fireworks users/web developers may be familiar enough with it to get around just fine (or one would hope). So chances are you'll have little trouble getting around programming your own shapes. Right?

Of course knowledge of JavaScript alone will only get you so far. Since shapes are created entirely with code, it doesn't hurt to know a good deal of math as well, especially that pertaining to the bezier paths used to create your shapes. Trigonometry, the math pertaining to angles and triangles, can be especially useful as well. Then again, not all Auto Shapes require a lot of math. Having that knowledge is just useful. Depending on how complicated an Auto Shape is, it may be a necessity at all.

 

Before doing anything else with Auto Shapes, you'll need to be well equipped to handle and understand Firework's DOM (Document Object Model) and the API (Application Programming Interface) provided that allows you to use JavaScript commands to interact with the Fireworks application. For that you will need to get Macromedia's documentation on extending fireworks. It will be your bible for creating Auto Shapes as well as any other command or extension for Fireworks you plan to tackle. Like all of Macromedia's documentation, it's not without its faults, but it will certainly provide you with a helpful guide and reference to using JavaScript in Fireworks (and Auto Shapes).

Extending Fireworks 2004 (zipped PDF)

 

 

Limitations Of Auto Shapes

Auto Shapes are great tools, but keep in mind, especially when deciding to make one of your own, that they do have their limitations.

• At present (Fireworks MX 2004), Auto Shapes graphically only support Paths and Text. You cannot have a Bitmap within an Auto Shape, at least not directly. This can be somewhat overcome using a pattern for a shape fill. Doing so, however, would mean using an installed pattern (more on this will be discussed later).

• Auto Shapes cannot be nested. In other words, Auto Shapes cannot contain other Auto Shapes.

• Auto Shapes may, at times, experience issues when interacting with other elements within your document. The primary intent of Auto Shape interaction is to provide a way for the user to interact with the Auto Shape itself. However, since Auto Shapes use Fireworks JavaScript, they are completely capable of interacting with other elements within a Fireworks document as well. This usually isn't a problem, but in certain cases, however, doing so can cause unexpected and disastrous results (register move functions on other objects, for example, are a no-no).

• Auto Shape interaction can be broken if the user alters internal elements. Auto Shapes are really just enhanced Group elements in Fireworks. Users can select path points and other elements within an Auto Shape using the sub-select tool. If they delete or move points or elements unexpectedly, the Auto Shape might break or cause an error since something it expected to be there is there no longer. Usually this isn't a huge concern since typically users understand this, but it may be something you need to deal with when creating your shape.

 

Your First Auto Shape

I think that starting from the very basics is key to ultimately obtaining a full understanding of what you're going to be dealing with. So without further delay, lets create what will be your very first Auto Shape.

1. First, using a text editor, create a new file called First Shape.jsf and save it in your Configuration\Auto Shapes folder located in your Fireworks MX 2004 install directory.

2. Next, open Fireworks.

3. Open the Shapes panel (Window > Auto Shapes). In the Shapes panel, you should see a new Auto Shape with an empty (white) preview with the name "First Shape".

4. Drag that shape into a Fireworks document.

Congratulations. You've just created your first Auto Shape! Ok, so it's not really a shape at all, just an empty element, but that's all you get with an empty JavaScript (JSF) file. Did you really expect anything more? Just having that file was all it took to get your "shape" in the Shapes panel and to be able to drag that into a Fireworks document. As a result a new Auto Shape element became visible within the layers panel. Now it's just a matter of including a script in that JavaScript file that actually creates your shape.

Note: though Auto Shapes are also available as tools from the Tools panel, it's a little easier to create drag and drop shapes created from the Shapes panel. Tools shapes will be covered later, but for now, we'll stick to using Shapes panel Auto Shapes.

Certainly, before doing any coding, it's always a good idea to plan out an Auto Shape before making it. Sketches and schematics will help collect thoughts for both determining how the shape will look and how you might go about actually coding it. What a shape looks like is usually the easy part. Figuring out the code to generate that for you can be a little more difficult. More difficult still can be adding interaction that lets that shape change. Some preconception will be addressed The following shows an example of a sketch (created from within Fireworks) that help outline a heart Auto Shape I've created.

One nice thing about the JavaScript files in your Auto Shapes directory is that when you edit or update them, those changes will take effect next time you create that shape. This means you can easily test your Auto Shape scripts in Fireworks as you work on them. Existing Auto Shapes of that type will not update, though, just new ones. Existing Auto Shapes retain the script used to create them internally.

Though nothing you need to consider right away when making an Auto Shape, preview images can be useful to help with distinguishing the Auto Shape you're working on from others. Eventually, one should be included. The earlier the better. And if you are in the process of making an Auto Shape, your preview can actually help you indicate that the shape is "under construction" so to speak.

Preview images like this for the Shapes panel are simply 60x60 PNG or GIF images representing what your Auto Shape looks like (GIF files seem to be the preferred format for the Shapes panel) that reside with the shape's JSF file in the Auto Shapes directory.

 

The Basics Of Fireworks JavaScript

JavaScript in Fireworks provides a means to perform most application operations through an easily accessible and widely understood scripting language. For Fireworks, JavaScript runs almost identically to that of a browser, only it does so using a different DOM or Document Object Model. This means that the elements that you access within a "document" via scripts in Fireworks are different from those you would access from within a browser, as you might expect. The aforementioned Extending Fireworks PDF gives you the lowdown of JavaScript in Fireworks. It will help you better understand all the Fireworks JavaScript DOM and the API that goes along with it.

Auto Shapes, however, don't make full use of all that Fireworks JavaScript has to offer. A lot of the functions provided for Fireworks interaction are based on selections within a Fireworks document. In creating Auto Shapes, you really aren't working so much with a selection as you would be otherwise (i.e. as you would from within a Fireworks command or panel), so a lot of those functions just don't apply. Instead, Auto Shapes stick to using the few JavaScript structures or objects that relate to them; some of which are common for other uses in Fireworks and others that are specific to Auto Shapes.

The primary JavaScript object for Auto Shapes is the smartShape object. This object is passed to the Auto Shape script when run and provides information within its properties that is necessary to control how that script is handled. And yes, you read right. The object is named smartShape and not Auto Shape This, as I understand it, is primarily a result of the name of this Fireworks feature changing during the development process but just not being reflected in code. It can take a while to get used to.

 

JavaScript Overview

JavaScript is a key part in the creation of Auto Shapes. Without knowing how to program in JavaScript, you may not get very far in creating an Auto Shape. The following section will briefly cover the fundamentals for anyone needing a refresher. I don't think you should expect to learn JavaScript by only the following though. If you need more help, try looking for some more resources online. The following covers the basic concepts in JavaScript that are used pretty extensively in the Auto Shape examples given later on.

Variables

Variables are keywords in a JavaScript script that represent values. Variables can be provided for you (through whatever application may be running the script) or can be created explicitly by you. When creating new variables in JavaScript for the first time, you would want to use the var keyword (though it's not always needed). Here is an example of a variable definition.

var variableName = 5;

This creates a variable with the name "variableName" that has a value of 5. You of course aren't limited to using"variableName" for any variable name; it can be most anything you want. You mainly need to just make sure you aren't using a name that's used by something else and stick to the common format of variable names which require that they are (mostly) alpha-numeric and only starting with a letter, _, or $.

Objects

Objects are special kinds of variables that contain other variables. Object variables can be created using new Object() or using the short-hand syntax of {}. The following shows the creation of objects.

var objectName = new Object();
var objectName = {};

The two above statements are identical, creating a new basic or generic object variable with the name "objectName." Note that when new is used to create an object, the term following the new keyword is known as the constructor. Above, the first objectName variable was created with the "Object" constructor - the method used to create or "construct" new objects.

Once an object is created, more variables can be assigned to it. Separation between an object and its own variables are handled through a dot .. This is called using "dot syntax." Note: var is not used for creating variables within other objects where dot syntax is used.

var objectName = new Object();
objectName.variableName = 5;

This creates an object named "objectName" and then creates a variable within that object named "variableName." One of the advantages of the short-hand method of creating objects is that allows you to add variables or properties like this in the same line the object is defined.

var objectName = {variableName: 5};

The above is identical to the script before it. Notice how the equals sign = has become a colon : when used within the shorthand syntax of object creation.

Arrays

Arrays are a type of object that let you hold values in a form of 0-based list (0-based in that the first item in an array is at its 0th position). Variables in arrays are stored in numeric values. These values start at 0 and count up from there depending on how many values are assigned in the array. Like generic objects, arrays can be created with the new keyword using the Array constructor or by using a short-hand syntax using []. The following shows the creation of arrays.

var arrayName = new Array();
var arrayName = [];

The two above statements are identical, creating a new array variable with the name "arrayName." The short-hand syntax also lets you add values to the array created within the same line much like object, only with arrays, you do not have to supply names for the values since they are automatically placed in the array and assigned to a number based on the order in which they were written.

var arrayName = [5, 15, 25];

Here, the array arrayName is created with 5 in its 0 position, 15 in it's 1 position and 25 in its 2 position.

Since, technically, these values assigned to an array are assigned as numeric variables within that array, and, technically, variables shouldn't start with numbers, dot syntax will not let you access these values directly. Instead, you have to use the bracket operators [] in place of dot syntax. The following represents the value 15 in the previously defined array.

arrayName[1];

To find out how many items are in an array, you can check its length property. This value is an automatic variable that arrays have to help you keep track of how many values they have.

arrayName.length;

Functions

Functions are blocks of script that you can call to be executed or run at your desire. They are created using the function keyword. When the function is called, all script within its function block {} will be executed. Here is an example of a function.

function functionName(){
	variableName = 5;
}

This function has the name "functionName." When called, it assigns the value 5 to the variable variableName.

To call a script, you use its name in conjunction with parenthesis ().

functionName();

This runs functionName which in turn runs all the script within its code block {}. As a result, is variableName assigned to be 5.

There are a few ways to write functions. The above and the following are the most common.

functionName = function(){
	variableName = 5;
}

In this form, its easy to see that function names themselves are variables as well. As such, they too can be added to objects as object properties.

var functionObject = new Object();
functionObject.functionName = function(){
	variableName = 5;
}
functionObject.functionName();

Functions also let you pass in values to a function call (a parameter list in ()) as well as receive values back as a result of that call (a return value). The following is a function that does both.

functionAdd = function(num1, num2){
	return num1 + num2;
}
var total = functionAdd(1, 2);

Here, functionAdd takes two parameters num1 and num2 and uses return to return the value of those numbers when added. When called, the return value is assigned to the variable total which becomes 3 (1 + 2).

Conditionals

Conditional statements are what help you decide on what to do in a script. These are typically handled with if or if-else conditions. These statements let you check a value or compare more than one value and then decide, based on the result, what actions to perform next, if any. The following condition checks to see if if variableName equals 5. If so, it calls the function functionName.

if (variableName == 5){
	functionName();
}

The next condition uses an if-else statement to run functionName if variableName is 5 but, alternatively, will run functionName2 if it is not.

if (variableName == 5){
	functionName();
}else{
	functionName2();
}

Looping

When you want to repeat something over and over again in a script, a loop will allow you to do so. Probably the most common form of loop is the for loop. It is created using the following structure.

for (var i=0; i<count; i++){
	functionName();
}

Assuming there is a variable defined called count, this for loop will repeat the code within its function block {} (in this case functionName()) for count times. Each time the loop repeats, it increments the variable i it created by 1 (i++). A common use of loops like this is to cycle through all the elements of an array as seen in the following.

for (var i=0; i<arrayName.length; i++){
	functionName(arrayName[i]);
}

For each loop, this takes the array value of position i in arrayName and passes it to functionName.

Comments
When you want text in a script to go ignored by the application running it, you place it in a comment. Comments let you dictate your script to make it more readable and easier to understand (though I will usually leave out comments in many of the scripts you see here for brevity). Comments can be created by placing text between /* and */ or by placing text after // as long as it does not consist of more than one line.

/*
multiline
comment
*/
// single line comment

Comments are not only good for describing code, but they can also be used as a debugging tool by letting you temporarily prevent scripts from running by placing them in comments.

 

 

The Anatomy Of An Auto Shape

There are three "stages," if you will, of an Auto Shape you need to consider when creating one. The first and foremost, and most obvious, is drawing the shape, defining what that shape looks like. Next you have defining user interaction. This typically revolves around creating control points and deciding how the user interacts with them (and what happens to the shape when that happens). The third is the preview. What this is, is the visible preview outline seen by the shape as the user interacts with it. Often, depending on the complexity of the shape, this can very well be the same as the actual drawing of the shape. The preview, however, can also be used to provide extra information to aid in user interaction (such as guide lines) or provide a more simple preview of the shape to prevent slowdown that might result in interaction with complex shapes.

 

Drawing Vector Paths With JavaScript

Most Auto Shapes comprise of a vector path or a combination of multiple paths. A path represents a vector element much like what you would draw with the pen tool. Paths themselves consist one or more lines or contours. Each contour is comprised of path points or nodes. And, (yes, there's more) each node consists of 3 points that define it. When generating your Auto Shape, you will need define each these for your shape.

Each one of these are represented in an Auto Shape's code with a respective object. Each object, in turn, has a respective constructor used to create it. These constructors are Path, Contour, and ContourNode respectively. When scripted for your Auto Shape in code, they are created for the actual Auto Shape in the Fireworks document. In order for the paths you create to actually be created for the Auto Shape, they have to be defined in a specific location. This location is within an object inside the smartShape object.

When an Auto Shape script is run by Fireworks, that script has defined for it a smartShape object. In this smartShape object there are many different variable definitions, or properties (for a complete list see the Extending Fireworks PDF). One property of particular importance here is the elem property which is the object that represents the Auto Shape element itself. Since Auto Shapes are technically just enhanced groups, elem represents that Auto Shape group. Group objects have an array where they keep all elements that are grouped within that group. This array is the elements array. So, for Auto Shapes, all paths you wish to create for your shape need to be defined in the smartShape.elem.elements array.

You would start a path by first creating a path object directly in smartShape.elem.elements. Once you have that path, you can then add to that path new contours. Contours for paths that reside in a path's contours array. Contours then need nodes. Creating nodes for contours means creating nodes in a contour's nodes array. When it all comes down to it, to create a single point for a path in an Auto Shape, you would need something like the following.

smartShape.elem.elements[0] = new Path();
smartShape.elem.elements[0].contours[0] = new Contour();
smartShape.elem.elements[0].contours[0].nodes[0] = new ContourNode();

This creates a new path for the Auto Shape as that Auto Shape's first element. That path has one contour which has one node.

If you decide to, you can also create an empty values within either an elements, contours or nodes array by directly altering its length, to create default objects for those elements. Adding to the elements array will create a new path with one contour containing 4 nodes. Adding to the contours array creates one contour with no nodes. And adding to the nodes array simply creates new nodes for each addition. With the elements array, however, you can only create one element like this at a time. Otherwise an error will occur. Because of this, more often than not, paths are usually created with the constructor as opposed to this method. Its not uncommon to see contours or nodes created in this way though. Here is an example.

smartShape.elem.elements.length++; // new path with 1 contour with 4 nodes
smartShape.elem.elements[1] = new Path(); // 1 path
smartShape.elem.elements[1].contours.length = 2; // 2 new contours with no nodes
smartShape.elem.elements[1].contours[0].nodes.length = 4; // 4 nodes

It's also important to note that paths created this way have default properties (or rather no default properties), while those created manually take on properties of the document (properties such as stroke color and fill type).

When you create nodes for a path, they are always created in the upper left hand corner of the document. Once they are created, it's up to you to move them where you need them to be in order to produce your shape. When moving nodes, you'll have to remember that they are defined by 3 control points, the predecessor, the main control point and the successor. These are the same points you move around with the pen tool to manage vector curves. Even if your line does not include curves, these points still exist; they just all share the same location.

The location of a node's control points are defined by properties within the node object. These properties are:

Predecessor predX
  predY
Main control point x
  y
Successor succX
  succY

In moving a point, each one of these 6 properties would need to be have assigned for it the appropriate location value (in pixels) to move the node fully. That having been said, let's actually get to some code that will produce a real (real simple) Auto Shape. At this point in time, it will be nothing impressive, just something that can be thrown into that First Shape file created earlier that will give you something more than nothing. This example will be a simple curve; a path with one contour consisting of two nodes.

Auto Shape: Curve [ Show/Hide Code | Download ]

Save this to a file within your Auto Shapes directory and open Fireworks if it isn't already. Drag the shape into an open document and you should get something that looks like the following:

Note: if you see nothing, make sure you have a line and/or fill applied.

When you drag the shape onto your Fireworks document, the script is run and the new path is created within the Auto Shape group. As you can see, the resulting shape is a single path with one contour containing 2 nodes (added by altering the nodes array's length property). The first node, a hard corner (no curve) was moved to location (25, 25). The second node, a curve, was placed at (100,100) with control points at (150, 50) and (50, 150).

Looking at the script you can see that the positioning of these two nodes has taken up a good number of lines of code and require quite a bit of typing. I have made a couple of functions that can ease the pain of such node positioning. They are as follows:

SetNodePosition = function(node, pt){
	SetBezierNodePosition(node, pt,pt,pt);
}
SetBezierNodePosition = function(node, ptp, pt, pts){
	node.predX	= ptp.x;	node.predY	= ptp.y;
	node.x		= pt.x;		node.y		= pt.y;
	node.succX	= pts.x;	node.succY	= pts.y;
}

Using these, the above script can instead be transformed to:

Auto Shape: Curve 2 [ Show/Hide Code | Download ]

Both SetNodePosition and SetBezierNodePosition are used here in combination with a space-saving variable nods used to store the value of the nodes array for the path's first (and only) contour. Though it may not look so much like a big difference now, when you start defining more nodes, it will really lighten up the load. For example, what if we were to draw a hollow box?

Try this Auto Shape code:

Auto Shape: Hollow Box [ Show/Hide Code | Download ]

This Box shape consists of two contours, each with 4 nodes. They were also each specified as being closed from setting isClosed to be true. This tells the square to finish itself off once it's reached its fourth and final point by connecting back to the first point. Because these 2 contours exist in the same path, they share the same fill. As such, the inner contour will act as a hole to the outer-most contour. If these were made to each be in separate paths, they would both be fully solid squares, one on top of the other.

 

Using The Mouse

The two prior examples, the Curve and the Box, each have been created in the upper left of the Fireworks document as a result of the location values provided for their location. When a user drags an Auto Shape onto a Fireworks document, it is expected that shapes be created where they are added--where the mouse is when it releases the shape into the document. To do this, you will need to base all node positioning not on static numeric values alone, but on the mouse position when the shape is created. You can obtain the mouse position using smartShape.currentMousePos. currentMousePos is a property in the smartShape object which supplies a Point object with x and y variables representing the x and y position of the mouse.

As a point, currentMousePos, too, can cause a bit of code clutter, especially when constantly adding it's position properties to other point objects you've created to control the location of your nodes. As with the node positioning, similar steps can be utilized to help lessen the effects; namely using a smaller variable to represent the value and a function to manage addition operations with points called AddPoints.

var mouse = smartShape.currentMousePos;
// and
AddPoints = function(pt1, pt2){
	return {x:pt1.x + pt2.x, y:pt1.y + pt2.y};
}

With these additions to the script, we can alter the Box example to be created where the mouse is when the shape is inserted into the active document.

Auto Shape: Hollow Box 2 [ Show/Hide Code | Download ]

AddPoints is used to take a pre-defined coordinate (that you would specify) and adds it to the mouse position defined in the mouse variable. The resulting location is one which is relative to the mouse position meaning the entire shape is created around the mouse position. For virtually every shape you create, the mouse location will be a very prominent aspect in determining placement of your Auto Shape elements.

 

Adding Interactive Control Points

Generating scripted vector shapes is good, but that alone isn't what Auto Shapes are all about. Auto Shapes really shine with user interaction. This interaction is based upon interaction with an Auto Shape's control points (not to be confused with the control points of a contour node).

Control points, much like elements, contours, and nodes, exist within an array. This array can be found in the smartShape.elem object and is appropriately called controlPoints. You can create control points with the ControlPoint constructor or, like with contours and nodes, just modify the length of the controlPoints array and have default control points created for you.

smartShape.elem.controlPoints[0] = new ControlPoint(); // one new control point
smartShape.elem.controlPoints.length = 4; // add 3 more default control points

Also like nodes, control points are created in the upper left of the document so they will need to be positioned accordingly using their x and y (position) values. Again, to ease this process, a function can be created to handle this. But, control points aren't all just about position. They have some other properties that might be useful to set as well. These properties may include:

name
type
toolTip
toolTipTracksDrag
visible
hiliteDragOverObject
For more details on what they represent, see Extending Fireworks PDF.

Based on the kind of shape I'm creating, those which I include in the control point positioning (or setting) function vary greatly. For this example, I'll include the additional name and toolTip properties. These properties along with x and y will be set for each control point using a function called SetControlPoint.

SetControlPoint = function(cp, pt, name, toolTip){
	cp.x = pt.x;
	cp.y = pt.y;
	cp.name = name;
	cp.toolTip = toolTip;
}

This, combined with a short variable name to represent the control points array (cps), can make adding control points a breeze. Here's how one can be added using the previous Box shape code.

Auto Shape: Hollow Box 3 [ Show/Hide Code | Download ]

Here, a control point is added to the center of the box with a name of "center" and a tool tip that displays "Click Me" when the mouse has hovered over it. No problem right? Creating control points is a snap. What's left is programming the what happens when the user interacts with it. For that, you will need to get a better understanding of Auto Shape events.

 

Auto Shape Events

The code in JavaScript files that define Auto Shape gets executed whenever an Auto Shape is created as well as whenever there is interaction involving the shape. This interaction is defined by the following events.

InsertSmartShapeAt
BeginDragInsert
DragInsert
EndDragInsert
BeginDragControlPoint
DragControlPoint
EndDragControlPoint
SmartShapeEdited
For more details on what they represent, see Extending Fireworks PDF.

The first four listed are events associated with when the Auto Shape is created. The remaining four pertain to user interaction with the Auto Shape after it has been created. Whenever one of these events occur, the Auto Shape script, in it's entirety, is run. For example, dragging an Auto Shape from the shapes panel to your document is interpreted by Fireworks as being the InsertSmartShapeAt event. The Auto Shape script is then run and your shape is created. Whenever a control point is pressed, Fireworks runs the Auto Shape script as a result of the BeginDragControlPoint event. If the control point is moved, the script is run as a result from the DragControlPoint event and, finally, when the control point is released, the script is run in response to the EndDragControlPoint event. SmartShapeEdited is an event resulting from the user editing the Auto Shape with the Subselection tool.

The important thing to understand here is that with events, the entire script for the Auto Shape is run every time one of these events occur. It is up to you to decide what happens in that script based on which one if these events caused it to run. For example, you wouldn't want to re-create your shape during a EndDragControlPoint (or would you?). Shape drawing should be handled only in a shape creation event like InsertSmartShapeAt. How you are able to determine which event caused your script to run is through the smartShape.operation property.

The operation property in the smartShape object provides a string representation of one of the events listed above--the event that caused the script to run at that particular moment in time. So if you drag and drop your Auto Shape from the Shapes panel into your Fireworks document, your Auto Shape script will run and smartShape.operation within that script will be defined as "InsertSmartShapeAt." You can then check this value and take the necessary steps to do what you need to do when the user does this. Similarly, when the user invokes an "EndDragControlPoint" event by grabbing and releasing one of the Auto Shape's control points, you will need to take the appropriate steps to have your script do what it needs to do to react to that interaction.

Macromedia suggests using a switch statement to check the value of smartShape.operation and then call a corresponding function related to that event. For example, when Macromedia creates Auto Shapes, you'll see something like the following in the JavaScript code for that shape.

switch(smartShape.operation) {
	case "InsertSmartShapeAt":
		InsertSmartShapeAt();
		break;
	case "EndDragControlPoint":
		EndDragControlPoint();
		break;
	case "SmartShapeEdited":
		PlaceControlPoints();
		break;
}
function InsertSmartShapeAt(){
	// code...
}
function EndDragControlPoint(){
	// code...
}
function SmartShapeEdited(){
	// code...
}

I personally find this a little redundant so I tend use another method. First I create a new object called operation then add event functions to that object for each event I wish to address. Then, at the bottom of the script, I check the operation object to see if a method for the current event exists. If so, call it. For example:

var operation = new Object();
operation.InsertSmartShapeAt = function(){
	// code...
}
operation.EndDragControlPoint = function(){
	// code...
}
operation.SmartShapeEdited = function(){
	// code...
}
if (operation[smartShape.operation]) operation[smartShape.operation]();

This skips the whole switch comparison step condensing it into one if check (and an additional object definition for the operation variable). For me, it also helps distinguish my event functions with other functions that I may write and use for my shape since event functions are defined in the operation object.

Using either of the above methods, or even your own method, you get a way to distinguish between Auto Shape events and be able to run different commands based on what the current event calls for. For all further examples here, I will use my method (the second) since I feel most comfortable with it and it will save me, and hopefully you, some typing.

Once you figure out which event is calling the script, you need to take the appropriate actions to react to that event. Shapes from the Shapes panel, those which we have been working with so far (Auto Shapes can also be created from Auto Shape tools), are always created with the InsertSmartShapeAt event. After they are created, the only other events they will be affected by are BeginDragControlPoint, DragControlPoint, EndDragControlPoint, and SmartShapeEdited, most of which are a result of interacting with control points. The only event not control point related there is SmartShapeEdited. This event is often overlooked because of its limitations and how, if the shape is edited, it's a good chance by doing so that the user has broken the shape anyway. The main events to worry about are InsertSmartShapeAt, BeginDragControlPoint, DragControlPoint, and EndDragControlPoint. Lets get back to the Box example and apply to it event detection.

Auto Shape: Hollow Box 4 [ Show/Hide Code | Download ]

Since the only event the Box shape really uses is InsertSmartShapeAt, it's the only one defined for the shape. Though a control point exists, it does nothing so there's no point in adding function handlers for those events that are associated with control points.

If you tried moving the control point in the Box example when it was first added (before event handling), you probably noticed that the control point and the box moved along with the mouse. The reason for this was that as you dragged that control point, the Auto Shape script was being continuously run by Fireworks as a result of the DragControlPoint event. This meant that the entire shape was being redrawn at the position of the mouse every time the mouse was moved, replacing the older version of itself as it went along. These results are kind of haphazard of the the current design of the shape as no checks were implemented to make sure the proper actions were being taken as a result of the current event. The shape only knew to redraw itself in the first element any time the script was run and did so as the control point was dragged. Once checks for event handling were applied and the shape knew better than to draw itself for every event, the control point did nothing. Of course, control points should do something.

 

Controlling User Interaction

When the user clicks and moves around the control points of your Auto Shape, you need to make sure that Auto Shape behaves appropriately. This means not only moving the control point itself, but also handling the shape for whatever reaction it causes.

Fireworks provides you with special JavaScript functions that let you specify how control points and contour nodes move as the mouse is moved while dragging a control point (DragControlPoint event). These functions are:

RegisterMove
RegisterLinearMove
RegisterCircularMove
RegisterPolygonMove
RegisterInsertBBoxMove
For more details on what they represent, see Extending Fireworks PDF.

Each of these are automatically defined in and called from a control point or contour node. Each are also used in combination with a RegisterMoveParms object that outlines the details of the movement. A RegisterMoveParms object, from what has been seen so far and contrary to what you might suspect, is not created using the new keyword with a constructor, but is actually obtained from the smartShape object. The smartShape object provides a RegisterMoveParms object by means of a function it has called GetDefaultMoveParms. Calling it will return a basic RegisterMoveParms object that can be customized with the properties of your choice then used in one of the register move functions listed above to detail just how a user interacts with your control points and/or nodes. A simple example of how one might enable a control point to be dragged using this method would be:

var parms = smartShape.GetDefaultMoveParms();
smartShape.elem.controlPoints[0].RegisterMove(parms);
smartShape.elem.elements[0].contours[0].nodes[0].RegisterMove(parms);

Passing the RegisterMoveParms object (parms) into RegisterMove tells that control point and node to move based on the properties specified by the defaults provided by GetDefaultMoveParms. Those defaults tell the them to move the same direction and distance the mouse moves as the user is dragging a control point. Note that this does not necessarily mean that these points have to be at the same location of the mouse. They just move with the mouse, like the mouse, as it is moved around on the screen.

One thing you might want to know about RegisterMoveParms objects and is that when passed into a register move function, their properties are "burned" into or copied for the actual internal behavior that will be handling movement. This means that after you call a register move function with a RegisterMoveParms object, you can alter that object and use it again without worrying about the new changes affecting the original register move call.

All register move functions except RegisterInsertBBoxMove would be called for control points and nodes within the BeginDragControlPoint event. This sets them up to move accordingly directly when the user begins to interact with the shape. They are automatically "unregistered" by Fireworks when the mouse is released (EndDragControlPoint event). All that happens during the DragControlPoint event as the mouse is moved is taken care of automatically as a result of using one or more of the various register move functions. RegisterInsertBBoxMove is associated with Auto Shape tools and would be used in conjunction with the BeginDragInsert event (it moves points based on the bounding box created from dragging the mouse when the Auto Shape tool is used to create the shape in the document). It will be covered later.

You could, if you wanted, not use any of the register move functions at all and manually dictate movement within the DragControlPoint event on your own. This is like what was happening with the box shape example from before when you moved its control point. Positions for the control point and all the nodes were being set within each event. The result was movement that would be similar to calling RegisterMove for each control point and nodes using the default parameters from GetDefaultMoveParms once during the BeginDragControlPoint event. Register move functions are there to make things easier, though, so generally it's a good idea to make use of them.

With what has been covered so far, we can now create a simple interactive shape. Stepping back from the Box that has had a lot of attention up until now, we'll simplify things a bit and work on a triangle instead.

Here's the code.

Auto Shape: Play Button Triangle [ Show/Hide Code | Download ]

In examining the code, you can break up the script into about 3 parts. The first is the variable definition consisting of the mouse and cps variables. Next, you have a collection of event handler functions that are defined in the operation object. These functions represent the bulk of the goings-on for the shape--how its created and how interaction behaves. Those functions are followed by other custom functions that are used by the event handlers to make things a little easier or to handle other complex operations. Last but not least is the if check that invokes the appropriate event function for the event that caused the script to run as indicated by smartShape.operation.

operation.InsertSmartShapeAt creates the shape and control point based on the position of the mouse. This is called only once when the shape is placed within the Fireworks document. operation.BeginDragControlPoint is called each time the control point is clicked. It sets up movement of the control point and one of the shape's nodes--the node the control point sits on top of or, more specifically, the tip of the triangle. It uses RegisterMove with a default RegisterMoveParms object from smartShape so both the control point and node will follow the movement mouse. When you insert this shape into a Fireworks document, you can see this in action. As you move the control point, both the control point and the node move with the mouse and the Auto Shape automatically updates itself to reflect that.

This example only contains one control point so, when you click on any control point in the shape, you're sure it's going to be that one control point. When you have more than one control point, however, you will need to determine which control point is clicked since clicking any and every control point will invoke a BeginDragControlPoint operation. You can do this checking smartShape.currentControlPoint, smartShape.currentControlPointIndex, and/or smartShape.currentControlPointName.

Each currentControlPoint, currentControlPointIndex, and currentControlPointName property represent pretty much what their name says. currentControlPoint is a reference to the actual control point that was clicked while currentControlPointIndex is its position in the controlPoints array and currentControlPointName is the control point's name (as defined in the SetControlPoint function). Using any or all of these, you can easily delegate actions based on control point clicked, not unlike that which is done with smartShape.operation. Which to use usually depends on what your Auto Shape calls for and how interaction is handled.

Let's re-address the triangle shape and add another control point. This control point can be used to control the position of the remaining nodes in the shape.

Auto Shape: Play Button Triangle 2 [ Show/Hide Code | Download ]

The new control point is placed between nodes 0 and 2 (the very nodes it affects). In the operation.BeginDragControlPoint function, the distinction between the two control points, the original "Tip" point and the new "End" point, is made using smartShape.currentControlPointName. This, of course, not before assigning it to a more manageable cpname variable (programming is one case where bigger is not always better). Note that this variable was assigned within the BeginDragControlPoint function and not at the top of the script. This is because it's really only needed in the BeginDragControlPoint function and not anywhere else whereas with mouse and cps, they are used throughout all functions. There's not point in defining it at the top of the script if it's only going to be used in that one place.

An if statement checks the value of the control point's name comparing it to "Tip" and "End," the names given to the two control points when they were created with SetControlPoint. If the name is "Tip," the first control point and the tip node are registered to move as was the case before. If the name is "End," the new control point and the two other nodes are registered to move with the mouse. Testing and interacting with the shape will demonstrate the results. Each control point and node or nodes moves with mouse movement based on which control point was clicked.

Continue reading »

Pages: 1 | 2 | 3 | 4 | 5