Publishers of technology books, eBooks, and videos for creative people

Home > Articles > Design > Adobe Creative Suite

This chapter is from the book

Construction

You've got a pretty clear objective here: to create a component that will control other clips on the Stage and enable the end users to control the movement of those clips with their mouse. This clip will not itself be visible to the user of the scrolling list, but will remain behind the scenes.

The gesture-driven component will be a movie clip made up of three frames. The first frame will handle initializing variables and defining functions for the clip. The second frame will contain the code that actually does the work—keeping the different elements in the list aligned properly, getting the mouse position, and making calculations to move the list elements on the screen. The third frame will be your loop frame; it will just tell the clip to loop back to the second frame.

Setting Up the Movie

To start building your scrolling list component in Flash, make a new movie and set its frame rate to 20 fps. Make a new layer and call it scrolling list. Then create a shape on the Stage (it can be anything at this point), select it, and turn it into a movie clip called ScrollingList. Double click the clip you just created (to edit it) and add a new layer. Name this new layer actions. This is where you will be adding your ActionScript. Add two more frames to the movie clip, making it a total of three frames long. Make each of these frames a keyframe. The quickest way to do this is to select all three frames in the actions layer of the timeline and convert them to key frames (Modify>Frames> Convert to Keyframes). Now you're ready to start adding the code.

Figure 5.5. Select your three frames and convert them to keyframes (Modify>Frames>Convert to Keyframes).

Hiding the Clip

Select the first frame in the actions layer and open the Actions panel. Here you will hide the movie clip and define some constants that you will replace with component parameters later. Enter this code:

this._visible = false;

var CLIP_BASE_NAME = "mc_test_clip";
var TOTAL_CLIPS = 3;
var LIST_ORIENTATION = "vertical";
var CLIP_SPACING = 10;
var MOUSE_BUFFER = 20;
var SCROLL_SPEED = 1;
var LOOPING = true;
var SMOOTH = true;
var FRICTION = 5;

Remember, take the extra time to give your variables sensible names so that you and others going through your code can understand what's happening. The MOVIE_WIDTH and MOVIE_HEIGHT variables hold the width and height of your movie. If these values do not match your current movie's dimensions, change them now. CLIP_BASE_NAME holds the base name for the clips you'll be scrolling in the list. You will make some clips later to test the code. TOTAL_CLIPS is the number of clips that you will control in the list. This will be used in conjunction with the CLIP_BASE_NAME constant to actually find the movie clips on the Stage. The LIST_ORIENTATION constant will control whether your scrolling list moves from side to side or up and down. SCROLL_SPEED will hold a number that you will use to determine the speed at which the list should scroll. The LOOPING constant will hold whether the list loops around to the beginning when you reach one end of the list, or whether it stops at one end, forcing the user to scroll in the opposite direction to get to the other side. Both the SMOOTH and FRICTION constants will be used later to help add a nice little touch to the list: having it decelerate slowly when the user moves her mouse away from the list.

Public Functions

When planning your code, especially if other developers are going to use it, you should think in terms of "public or private" when organizing functions and variables. Suppose, for example, that you have a balloon movie clip that contains all the ActionScript logic necessary to float around the screen like a real balloon does. All the calculations for gravity and weight and even the size of the balloon are functions and variables that don't need any help from the outside to work properly. These could be considered private functions and variables. However, you may want to have another movie clip onscreen pop the balloon. In this case, you can provide a public pop() function that can safely be accessed by other code in the main movie.

The advantage to working in this manner is that you would need to explain to the other developers only about how to use the public functions to do what they want. This keeps you from having to explain to everyone using your cool balloon clip how the material_tension variable is handled or how your code might deal with a gGravity variable with a null value. You just provide those developers with a good public set of variables and functions so that they don't need to learn all the intricacies of your code if they don't want to.

For the scrolling list component, you may sometimes need to pause the scrolling or perhaps even reinitialize the list. Add the following public functions in the Actions panel:

function enableScrolling() {
 gScrollingEnabled = true;
}

function disableScrolling() {
 gScrollingEnabled = false;
}

function resetList() {
 gotoAndPlay(1);
}

These are very simple functions that change the value of a variable, gScrollingEnabled, to either true or false. The resetList function takes care of sending the playback head back to the first frame of the component, which will rebuild the list.

Handling the Offset

Instead of enforcing that the movie clip's registration point be in the center, for this module you will use programming. Add this code to your ActionScript panel:

function getClipOffset(clipObj, axis) {
var tempOffset = 0;
if (axis == "x") { tempOffset = clipObj._x Ð clipObj.getBounds(_parent).xMin;
} else if (axis == "y") {
tempOffset = clipObj._y Ð clipObj.getBounds(_parent).yMin;
}
return tempOffset;
}

The getClipOffset function takes a movie clip object and the axis of the offset you want it to return. It then finds the offset of the movie clip object by calculating the difference between the y position of the clip and the coordinate of the clip's left edge returned by Flash's getBounds() function.

Creating a Clip Array

Now you'll define the createClipArray function. It will use the CLIP_BASE_NAME and TOTAL_CLIPS constants to find clips on the Stage and keep them around in an array so that you can easily loop through them later. Add this code next:

function createClipArray(baseName, total) {
var tempArray = new Array(); for (var i = 0; i < total; i++) {
if (null != _parent[baseName + i]) {
tempArray[i] = _parent[baseName + i];
}
}
return tempArray;
}
var gClipArray = createClipArray(CLIP_BASE_NAME, TOTAL_CLIPS);

This is one cool little function! It does some simple stuff, but makes your work much, much easier later on. The createClipArray function takes the base name and a total number of clips as parameters. It then loops through the same number of times as there are clips, checking to see whether a clip with the base name and a number at the end exists in the _parent movie. If it does exist (if it's not null), the function adds a reference to it in an array. The function then returns the newly built array to the caller of the function, the gClipArray variable. Clean, simple, and elegant.

Now make sure the createClipArray function works. By creating the array, you have created references to your movie clips. These references can be used to access and set properties of those movie clips, as follows:

arrayOfClips[0]._x = 100;

Here the x position of the clip is set, referenced by the first element of the array. This would be the same as:

_parent.mc_someClip0._x = 100;

Or:

_parent["mc_someClip0"]._x = 100;

The referencing technique provides one real advantage: If you ever want to change the path to a movie clip from _parent to, perhaps, _level233, you will only have to change it in the createClipArray function; you won't have to search through all your code for where you accessed the movie clip using the _parent methods. Neat, huh?

Organizing Your List Clips

First off, save your movie and then get ready to put that gClipArray variable to use. You are going to position all the clips on the Stage and then try to determine the scrolling list's active area based on the position of all those clips. Enter this code next in the ActionScript panel:

var gTopBounds = 0;
var gBottomBounds = 0; 
var gLeftBounds = 0;
var gRightBounds = 0;
if (gClipArray.length > 0) {
 gTopBounds = gClipArray[0].getBounds(_parent).yMin;
 gBottomBounds = gClipArray[0].getBounds(_parent).yMax; 
 gLeftBounds = gClipArray[0].getBounds(_parent).xMin;
 gRightBounds = gClipArray[0].getBounds(_parent).xMax;
}

These variables are being initialized and will soon hold the outer perimeter, or bounds, of the active area (the area that the mouse can roll over to activate the scrolling) of the scrolling list. Next you verify that the gClipArray contains something; and if it does, you set the bounds variables to the bounds of the first clip in your list.

Now add this code next:

var tempX = 1;
var tempY = 1;
for (var i = 0; i < gClipArray.length; i++) {

  if (LIST_ORIENTATION == "horizontal") {
   gClipArray[i]._x = tempX + getClipOffset(gClipArray[i], "x");
  tempX = gClipArray[i]._x – getClipOffset(gClipArray[i], "x") + 
gClipArray[i]._width + CLIP_SPACING; } else { gClipArray[i]._y = tempY + getClipOffset(gClipArray[i], "y"); tempY = gClipArray[i]._y – getClipOffset(gClipArray[i], "y") +
gClipArray[i]._height + CLIP_SPACING; } tempTop = gClipArray[i].getBounds(_parent).yMin; if (tempTop < gTopBounds) { gTopBounds = tempTop }; tempBottom = gClipArray[i].getBounds(_parent).yMax; if (tempBottom > gBottomBounds) { gBottomBounds = tempBottom }; tempLeft = gClipArray[i].getBounds(_parent).xMin; if (tempLeft < gLeftBounds) { gLeftBounds = tempLeft }; tempRight = gClipArray[i].getBounds(_parent).xMax; if (tempRight > gRightBounds) { gRightBounds = tempRight }; } gTopBounds -= MOUSE_BUFFER; gBottomBounds += MOUSE_BUFFER; gLeftBounds -= MOUSE_BUFFER; gRightBounds += MOUSE_BUFFER;

This chunk of code looks like a lot but is actually pretty simple. After initializing some temporary variables, it loops through gClipArray and, depending on the list's orientation (horizontal or vertical), positions each clip, in order of its name, side by side separated by the distance in pixels specified in the CLIP_ SPACING constant. It also compensates for any clip offset that may exist. After it positions the clip, it updates the temporary variable with a new location for the next clip in the loop. Then the position of every clip is compared to the bounds' variables. The code updates them if it finds a clip that exceeds the bounds. In other words, if you have different sized clips in your list, the biggest clip is used to determine the bounds of the list. Finally, you increase the scrolling list's activation area by the amount specified in the MOUSE_BUFFER constant. This makes it possible for the user to activate the scrolling of the list without having to actually be positioned over the list.

Initializing Your Variables

Now it's time to initialize some more global variables:

var gTotalWidth = tempX;
var gTotalHeight = tempY;

var tempScaleMode = Stage.scaleMode;
Stage.scaleMode = "exactFit";
var MOVIE_WIDTH = 550;
var MOVIE_HEIGHT = 400;
Stage.scaleMode = tempScaleMode;
var gMovieCenterX = MOVIE_WIDTH / 2;
var gMovieCenterY = MOVIE_HEIGHT / 2;

var gMouseDeltaX = 0;
var gMouseDeltaY = 0;

var gOriginX = 0;
var gOriginY = 0;

var gFriction = 1 + (FRICTION / 150);

var gConstX = (Math.pow(gMovieCenterX, 3) + Math.pow(MOVIE_WIDTH, 3) +  
Math.pow(gMovieCenterX, 2) + Math.pow(MOVIE_WIDTH, 2)) / 4; var gConstY = (pow(gMovieCenterY, 3) + Math.pow(MOVIE_HEIGHT, 3) +
Math.pow(gMovieCenterY, 2) + Math.pow(MOVIE_HEIGHT, 2)) / 4; var gScrollingEnabled = true;

The gTotalWidth and gTotalHeight variables hold the width and height of the scrolling list as a whole. These will be used later to reset the entire list position based on the width or height (cumulative) of the clips in the scrolling list.

Next you have the gMovieCenterX and gMovieCenterY variables that hold the center point on the screen for both the width (x) and height (y). These numbers will be used later to calculate the distance between the mouse cursor and the center of the screen. It uses the gMouseDeltaX and gMouseDeltaY variables to store those values.

Figure 5.6. The variables being initialized.

The gOriginX and gOriginY variables will be used as a reference point from which to determine the new location of the clips on the Stage. Initialize them here to 0.

The last set of variables to initialize in this frame has to do with the smoothing effect of the component. When smoothing is set to yes, the scrolling list will slowly decelerate to a stop after the mouse cursor has left the activation area. The gFriction variable is set to a fraction of the FRICTION constant. Adding 1 to it will ensure that it is always greater than zero. This number will be used later when calculating the acceleration and deceleration of the scrolling list.

The gConst variables are math constants, really big ones in this case, that will also be used later to divide into another very large number and finally determine the new x or y position of the scrolling list.

Figure 5.7. So far so good. The list elements are added to the object array and positioned properly!

Finally, you have the gScrollingEnabled global variable, which will be a Boolean value (true or false) that will tell the list whether to scroll at all. This can be useful to temporarily stop the list from scrolling if you want to steal the user's attention for alerts or other elements. This should be changed using the enableScrolling() and disableScrolling() functions defined at the beginning of this frame.

Believe it or not, this is all the code for frame 1! Save your movie again and go to scene 1. Draw a shape on the screen and convert it into a move clip. Be sure to set the registration point of the movie to 0, 0. Now copy that movie clip and paste it in the same layer twice. Position the new clips on top of each other and give the first clip the name of mc_test_clip0, the second clip the instance name of mc_test_clip1, and the third clip the name of mc_test_clip2. Now test your movie. You should see that the three items have been repositioned in a vertical row. Close the test movie and change the LIST_ORIENTATION constant you defined at the top of the first frame to horizontal and then test the movie again. You should now notice that the shapes have been organized horizontally across the Stage. Great work!

Now it's time to move on to frame 2 of the gesture-driven scrolling list component. This is where you make the scrolling list actually scroll.

The Over State

The first code that you will add deals with checking the state of the mouse cursor in relation to the activation area you defined in frame 1. Make sure you have frame 2 selected and add the following code in the ActionScript panel:

if (LIST_ORIENTATION == "horizontal") {
 var isOver = _root._ymouse >= gTopBounds && _root._ymouse <= gBottomBounds;
} else {
 var isOver = _root._xmouse >= gLeftBounds && _root._xmouse <= gRightBounds;
}

Here, depending on what the orientation of the movie is, you test the mouse cursor position and see whether it is within the bounds of the activation area. If so, the isOver variable gets set to true. Now add this code:

if (isOver && gScrollingEnabled) {

} else {

}

This is the if statement that will contain the code to calculate the new position of the gOriginX and gOriginY reference points. It will evaluate to true as long as the isOver and the gScrollingEnabled variables are both true. Add this code now, inside the top portion of the if statement, before the else part:

 var oldMouseDeltaX = gMouseDeltaX;
 var oldMouseDeltaY = gMouseDeltaY;

 if (SMOOTH) {
 gMouseDeltaX = oldMouseDeltaX + ((_root._xmouse – gMovieCenterX - 
oldMouseDeltaX) / (gFriction * 2)) gMouseDeltaY = oldMouseDeltaY + ((_root._ymouse – gMovieCenterY -
oldMouseDeltaY) / (gFriction * 2)) } else { gMouseDeltaX = _root._xmouse - gMovieCenterX; gMouseDeltaY = _root._ymouse - gMovieCenterY; }

The first thing that happens here is you initialize two new variables, oldMouseDeltaX and oldMouseDeltaY, to hold the current values of the gMouseDeltaX and gMouseDeltaY variables you initialized in frame 1. Then, if the SMOOTH constant is set to true, you set the global mouse delta variables to the distance between the center of the screen and the current mouse location, subtract the previous mouse delta (oldMouseDeltaX and oldMouseDeltaY), and then divide the result by the gFraction variable also defined in frame 1. This results in a fraction of the delta that you add to the old delta and assign that to gMouseDeltaX and gMouseDeltaY. This code is accelerating from the last delta to the new delta. This will result in a smooth transition from a completely stopped list to a moving list. If the SMOOTH constant is set to no, you grab the delta and assign it to the gMouseDeltaX and gMouseDeltaY variables without the in-between steps of dealing with the gFriction variable. If you were to move your mouse to one end of the list to start it scrolling, it would start up instantaneously.

In the second part of the if statement (the else), add the following:

 if (SMOOTH) {
 gMouseDeltaX = gMouseDeltaX / gFriction;
 gMouseDeltaY = gMouseDeltaY / gFriction;
 } else {
 gMouseDeltaX = 0;
 gMouseDeltaY = 0;
 }  

This code gets executed if the mouse is not over the activation area. If SMOOTH is set to true, you slowly diminish the mouse delta variables by dividing them by the gFriction variable instead of setting them to 0 as you do if the SMOOTH setting is turned off.

The Heart of the System

You've now arrived at the heart of the gesture-driven scrolling list component. This is where all the code will go that actually moves the movie clips around. For starters, add this code at the very bottom of the Actions panel:

if (LIST_ORIENTATION == "horizontal" && gMouseDeltaX != 0) {
} else if (LIST_ORIENTATION == "vertical" && gMouseDeltaY != 0) {

}

Figure 5.8. The code for the horizontal setting is the same as for the vertical, except the axes are different.

This if statement really does all the work for the scrolling list component. First it checks what orientation was specified, and then checks whether the global mouse delta variables are anything other than 0. The code that actually moves the clips around is contained within this if statement. Therefore, you can deduce that when the global mouse delta is 0 (in other words, the mouse cursor is not within the activation area bounds or the diminishing code has reached 0), the code that lies between these if statements will never get called. Although the two sections of this if statement are testing for different things, the code is identical for both—except that one deals with the x-axis (the horizontal one) and the other the y. You will address the x-axis version, and then replicate it for the y when you are through. Now, in the first if statement you just added, enter this code:

 gOriginX = gOriginX – Math.pow(gMouseDeltaX * SCROLL_SPEED, 3) / gConstX;

As you recall, gOriginX (and its y-axis sibling, gOriginY) act as reference points for you to use to calculate the new positions of the movie clips on the Stage. Here, you do the work of setting gOriginX. First, the global mouse delta (gMouseDeltaX) is multiplied by the SCROLL_SPEED constant you defined in frame 1. You then take this number and cube it, which should result in a very large number. Remember those really big math constants you defined in frame 1? Now you finally get to use them by dividing them into the resulting cubed number. So why, exactly, are you cubing a number and so on? Well, the gOriginX variable defines the feel of the scrolling list with speed, friction, and acceleration. If you were to use straight multiplication here, you would most likely end up with a steady acceleration that would get you the result similar to a linear equation. Instead you effectively create a larger "slow" area in the middle of the scrolling list, which will give the end user much more control over the scrolling list.

Immediately following the code you just added, enter this code:

 var clipOffsetX = 0;
 for (var i = 0; i < gClipArray.length; i++) {

 }

clipOffsetX will be used to calculate the proper distance from the gOriginX position (or reference point) to the new clip position. Next you begin the for loop that will go through each of the objects in the gClipArray variable.

Now add the following:

 if (gOriginX < -gTotalWidth) {
  gOriginX = gOriginX % gTotalWidth;
 } else if (gOriginX > gTotalWidth) {
  gOriginX = gOriginX % gTotalWidth;
 }

 if (!LOOPING) {
 if (gOriginX > 1) {
  gOriginX = 1;
 } else if (gOriginX < MOVIE_WIDTH - gTotalWidth + 1 + CLIP_SPACING) {
  gOriginX = MOVIE_WIDTH - gTotalWidth + 1 + CLIP_SPACING;
  }
 }

Because the gOriginX variable is a reference point for your movie clips in the scrolling list, you need to continuously add to it or subtract from it. This number can become quite large and unruly and will start to create more work for you when you need to offset that big value. The solution here is to limit its movements to the collective width of the movie clips. Here you test to see whether the gOriginX variable is larger or smaller than the total width of all the movie clips in the list. If it is, you just reposition the origin to the remainder of the origin and the total width of the movie clips.

Next the code handles the LOOPING constant. If the developer has selected the scrolling list not to loop, you check the position of gOriginX to see whether it goes past the edges of the screen. If it does, you limit it to that edge.

Now add the code that will do the actual calculation for the position of each of the movie clip elements (still inside the for loop):

 var newX = gOriginX + clipOffsetX;
 if (newX + gClipArray[i]._width < 0) {
  newX = gTotalWidth + newX;
 } else if (newX > MOVIE_WIDTH) {
  newX = newX - gTotalWidth;
 }
 gClipArray[i]._x = newX + getClipOffset(gClipArray[i], "x");
 clipOffsetX += gClipArray[i]._width + CLIP_SPACING;

First you define a new variable named newX, which will hold the new position for the current movie clip (gClipArray[i]) in the array. Then you test that new position to see whether it falls below or above the edges of the main movie. If it does, you add or subtract the total width of all the clips from the new value, essentially putting the movie clip at the very front or the very end of the list.

Next you set the x position of the current clip in the array (gClipArray[i]) to newX and calculate the offset for movie clips with different registration points. After that, you update the clipOffsetX variable to include this clip's width and spacing (CLIP_SPACING). And that's it! The code to handle the vertical situation is nearly identical to this except it handles the y-axis. Add that to the second part of the if statement, where LIST_ORIENTATION is "vertical":

 gOriginY = gOriginY - pow(gMouseDeltaY * SCROLL_SPEED, 3) / gConstY;

 var clipOffsetY = 0;
 for (var i = 0; i < gClipArray.length; i++) {

 if (gOriginY < -gTotalHeight) {
  gOriginY = gOriginY % gTotalHeight;
 } else if (gOriginY > gTotalHeight) {
  gOriginY = gOriginY % gTotalHeight;
 }

 if (!LOOPING) {
  if (gOriginY > 1) {
  gOriginY = 1;
 } else if (gOriginY < MOVIE_HEIGHT - gTotalHeight + 1 + CLIP_SPACING) {
  gOriginY = MOVIE_HEIGHT – gTotalHeight + 1 + CLIP_SPACING;
 }
 }

 var newY = gOriginY + clipOffsetY;
  if (newY + gClipArray[i]._height < 0) {
  newY = gTotalHeight + newY;
  } else if (newY > MOVIE_HEIGHT) {
  newY = newY - gTotalHeight;
  }

 gClipArray[i]._y = newY + getClipOffset(gClipArray[i], "y");
 clipOffsetY += gClipArray[i]._height + CLIP_SPACING;
}

Test Your Work

Now that you've finished off the code for frame 2, take some time to relax—and make sure you've saved this movie! Now add one more line of code and then you can test the movie. Select the third keyframe in this movie clip and open the Actions panel. Add this line:

gotoAndPlay(2);

Now go back to the main movie (scene 1) and copy and paste your clips until you have about nine clips on the Stage. Remember to edit the TOTAL_CLIPS constant in frame 1 of this movie clip to reflect the proper number of clips. Now test the movie. Is everything working? Fiddle around with some of the other constants and keep testing the movie to see how it reacts.

The three listings that follow show all the code you should have in the three frames of your scrolling list component. Check to make sure you are in sync, and then you will move on to making a real component out of the scrolling list movie clip.

Listing 5.1 Frame 1 ActionScript

this._visible = false;

var CLIP_BASE_NAME = "mc_test_clip";
var TOTAL_CLIPS = 3;
var LIST_ORIENTATION = "vertical";
var MOUSE_BUFFER = 20;
var CLIP_SPACING = 10;
var SCROLL_SPEED = 1;
var LOOPING = true;
var SMOOTH = true;
var FRICTION = 5;

function getClipOffset(clipObj, axis) {
 var tempOffset = 0;
 if (axis == "x") {
 tempOffset = clipObj._x – clipObj.getBounds(_parent).xMin;
 } else if (axis == "y") {
 tempOffset = clipObj._y – clipObj.getBounds(_parent).yMin;
 }
 return tempOffset;
}
function createClipArray(baseName, total) {
 var tempArray = new Array();
 for (var i = 0; i < total; i++) {
 if (null != _parent[baseName + i]) {
  tempArray[i] = _parent[baseName + i];
 }
 }
 return tempArray;
}

var gClipArray = createClipArray(CLIP_BASE_NAME, TOTAL_CLIPS);

var gTopBounds = 0;
var gBottomBounds = 0; 
var gLeftBounds = 0;
var gRightBounds = 0;
if (gClipArray.length > 0) {
 gTopBounds = gClipArray[0].getBounds(_parent).yMin;
 gBottomBounds = gClipArray[0].getBounds(_parent).yMax; 
 gLeftBounds = gClipArray[0].getBounds(_parent).xMin;
 gRightBounds = gClipArray[0].getBounds(_parent).xMax;
}

var tempX = 1;
var tempY = 1;
for (var i = 0; i < gClipArray.length; i++) {

 if (LIST_ORIENTATION == "horizontal") {
 gClipArray[i]._x = tempX + getClipOffset(gClipArray[i], "x");
 tempX = gClipArray[i]._x – getClipOffset(gClipArray[i], "x") +  
gClipArray[i]._width + CLIP_SPACING; } else { gClipArray[i]._y = tempY + getClipOffset(gClipArray[i], "y"); tempY = gClipArray[i]._y - getClipOffset(gClipArray[i], "y") +
gClipArray[i]._height + CLIP_SPACING; } tempTop = gClipArray[i].getBounds(_parent).yMin; if (tempTop < gTopBounds) { gTopBounds = tempTop }; tempBottom = gClipArray[i].getBounds(_parent).yMax; if (tempBottom > gBottomBounds) { gBottomBounds = tempBottom }; tempLeft = gClipArray[i].getBounds(_parent).xMin; if (tempLeft < gLeftBounds) { gLeftBounds = tempLeft }; tempRight = gClipArray[i].getBounds(_parent).xMax; if (tempRight > gRightBounds) { gRightBounds = tempRight }; } gTopBounds -= MOUSE_BUFFER; gBottomBounds += MOUSE_BUFFER; gLeftBounds -= MOUSE_BUFFER; gRightBounds += MOUSE_BUFFER; var gTotalWidth = tempX; var gTotalHeight = tempY; var tempScaleMode = Stage.scaleMode; Stage.scaleMode = "exactFit"; var MOVIE_WIDTH = 550; var MOVIE_HEIGHT = 400; Stage.scaleMode = tempScaleMode; var gMovieCenterX = MOVIE_WIDTH / 2; var gMovieCenterY = MOVIE_HEIGHT / 2; var gMouseDeltaX = 0; var gMouseDeltaY = 0; var gOriginX = 0; var gOriginY = 0; var gFriction = 1 + (FRICTION / 150); var gConstX = (Math.pow(gMovieCenterX, 3) + Math.pow(MOVIE_WIDTH, 3) +
Math.pow(gMovieCenterX, 2) + Math.pow(MOVIE_WIDTH, 2)) / 4; var gConstY = (Math.pow(gMovieCenterY, 3) + Math.pow(MOVIE_HEIGHT, 3) +
Math.pow(gMovieCenterY, 2) + Math.pow(MOVIE_HEIGHT, 2)) / 4;

Listing 5.2 Frame 2 ActionScript

if (LIST_ORIENTATION == "horizontal") {
 var isOver = _root._ymouse >= gTopBounds && root._ymouse <= gBottomBounds;
} else {
 var isOver = _root._xmouse >= gLeftBounds && root._xmouse <= gRightBounds;
}

if (isOver) {
 var oldMouseDeltaX = gMouseDeltaX;
 var oldMouseDeltaY = gMouseDeltaY;

 if (SMOOTH) {
 gMouseDeltaX = oldMouseDeltaX + ((_root._xmouse - gMovieCenterX - 
oldMouseDeltaX) / (gFriction * 2)) gMouseDeltaY = oldMouseDeltaY + ((_root._ymouse - gMovieCenterY -
oldMouseDeltaY) / (gFriction * 2)) } else { gMouseDeltaX = _root._xmouse - gMovieCenterX; gMouseDeltaY = _root._ymouse - gMovieCenterY; } } else { if (SMOOTH) { gMouseDeltaX = gMouseDeltaX / gFriction; gMouseDeltaY = gMouseDeltaY / gFriction; } else { gMouseDeltaX = 0; gMouseDeltaY = 0; } } if (LIST_ORIENTATION == "horizontal" && gMouseDeltaX != 0) { gOriginX = gOriginX - Math.pow(gMouseDeltaX * SCROLL_SPEED, 3) / gConstX; var clipOffsetX = 0; for (var i = 0; i < gClipArray.length; i++) { if (gOriginX < -
gTotalWidth) { gOriginX = gOriginX % gTotalWidth; } else if (gOriginX > gTotalWidth) { gOriginX = gOriginX % gTotalWidth; } if (!LOOPING) { if (gOriginX > 1) { gOriginX = 1; } else if (gOriginX < MOVIE_WIDTH - gTotalWidth + 1 + CLIP_SPACING) { gOriginX = MOVIE_WIDTH - gTotalWidth + 1 + CLIP_SPACING; } } var newX = gOriginX + clipOffsetX; if (newX + gClipArray[i]._width < 0) { newX = gTotalWidth + newX; } else if (newX > MOVIE_WIDTH) { newX = newX - gTotalWidth; } gClipArray[i]._x = newX + getClipOffset(gClipArray[i], "x"); clipOffsetX += gClipArray[i]._width + CLIP_SPACING; } } else if (LIST_ORIENTATION == "vertical" && gMouseDeltaY != 0) { gOriginY = gOriginY - Math.pow(gMouseDeltaY * SCROLL_SPEED, 3) /
gConstY; var clipOffsetY = 0; for (var i = 0; i < gClipArray.length; i++) { if (gOriginY < -gTotalHeight) { gOriginY = gOriginY % gTotalHeight; } else if (gOriginY > gTotalHeight) { gOriginY = gOriginY % gTotalHeight; } if (!LOOPING) { if (gOriginY > 1) { gOriginY = 1; } else if (gOriginY < MOVIE_HEIGHT - gTotalHeight + 1 + CLIP_SPACING) { gOriginY = MOVIE_HEIGHT - gTotalHeight + 1 + CLIP_SPACING; } } var newY = gOriginY + clipOffsetY; if (newY + gClipArray[i]._height < 0) { newY = gTotalHeight + newY; } else if (newY > MOVIE_HEIGHT) { newY = newY - gTotalHeight; } gClipArray[i]._y = newY + getClipOffset(gClipArray[i], "y"); clipOffsetY += gClipArray[i]._height + CLIP_SPACING; } }

Listing 5.3 Frame 3 ActionScript

gotoAndPlay(2);

Making the Component

Now that all the hard work of coding this clip is done, take a little extra time to make this clip into a truly reusable component. Open the library for your movie (Window>Library) and find the ScrollingList movie clip. Right-click (Ctrl-click on the Mac) the movie clip and select Component Definition. When the Component Definition panel comes up, use the plus sign (+) button to add new constants to the list of variables. Here is the list that you will add:

  • CLIP_BASE_NAME

  • TOTAL_CLIPS

  • LIST_ORIENTATION

  • MOUSE_BUFFER

  • CLIP_SPACING

  • SCROLL_SPEED

  • LOOPING

  • SMOOTH

  • FRICTION

When adding a variable, you can double-click one of the three fields to edit the name, initial value, and type of variable it is. Make all the variables Number types for now.

Figure 5.9. The Component Definition interface with all the constants.

Change the CLIP_BASE_NAME variable to the Default type.

Now change the type of field for LIST_ORIENTATION to a List type. Then double-click the middle column to bring up the List dialog box. Using the plus sign (+), add "vertical" and "horizontal".

LOOPING and SMOOTH should be changed to Boolean types. Keep both their values at the default of false.

Finally, in frame 1 of your scrolling list component, remove or comment out the variables at the top of the script.

Hooray! You're done! Now you can drag and drop this component into any movie and change all the parameters without ever going into the code. What's more, you not only have a component that can really be useful for you when developing, but a tool that can help you create more usable web sites for your clients.

Figure 5.10. Be sure to comment the constants from the component.

Peachpit Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from Peachpit and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about Peachpit products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites; develop new products and services; conduct educational research; and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email ask@peachpit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by Adobe Press. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.peachpit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020