Pages: 1, 2, 3
Smoothing Out Those Complicated Paths
Using the array method shown above can lead to jerky movement. The number of points in your array will determine the jerkiness of the path; the more points, the smoother the movement. But putting lots of points into the array can be a hassle as it may take a long time to calculate each point. A quicker and more elegant solution is to use the points in the array as anchors, and then use
setTimeout() to smoothly move between the anchor points. This won't always give exactly the path you want, but often it's good enough. This technique is demonstrated in the busy bee example.
If you take a look at the source of the example you'll see it involves a bit more code than the others. There are two main functions:
getAnchors() function looks up the beginning and ending points of each segment of the path and feeds these numbers to the
moveDiv() function. The
moveDiv() function moves the image using the
setTimeout() trick discussed earlier.
moveDiv() also checks that the image doesn't move past the ending anchor point. If the image is about to move past the end of the segment,
moveDiv() moves the image to the ending anchor point and then calls
getAnchors() to get the next pair of anchors.
getAnchors() function is simple. It takes the current segment of the path as a parameter, and retrieves the appropriate beginning and ending points (the anchors). If the
array_position is zero, it looks up the first anchor (
the_coords) and the second anchor (
var first_anchor = the_coords[array_position];
var second_anchor = the_coords[array_position+1];
Then the function adds one to the
array_position variable and makes sure it hasn't reached the end of the array. If it is the end of the array, the function resets the position to 0, which means the animation will start over:
if (array_position == the_coords.length-1)
array_position = 0;
After determining the next segment, the function calls
moveDiv() to actually move the DIV:
moveDiv(array_position, first_anchor, second_anchor, 0, 0);
The first three variables sent to
moveDiv() indicate, respectively, the current array element, the coordinates of the first anchor, and the coordinates of the second anchor. The final two parameters keep track of how far to move the DIV horizontally and vertically. Initially, you won't know what those values are, so you can just put zeros there. It's up to the
moveDiv() function to figure out how much to move the DIV each time.
moveDiv() function is more complicated than the
getAnchors() function. First, it gets the stylesheet information using
getStyleObject(). The next lines get the
top coordinates of each of the anchors. Here's the code for the first anchor:
var first_points = anchor_one.split(":");
var first_left = parseInt(first_points);
var first_top = parseInt(first_points);
Here's split() again, dividing the anchor, which looks like "100:120" into two numbers.
moveDiv() calculates the horizontal and vertical step sizes, if they need to be calculated. They only need to be calculated at the beginning of each segment, when
moveDiv() is called by
top step sizes set to zero. The
getStepSize() function returns a number, which determines how much the DIV should move in that direction each time
moveDiv() is called.
getStepSize() takes three parameters: the coordinates of the two anchors and a number which indicates whether we want to know about the horizontal step size or the vertical step size (0 for horizontal and 1 for vertical):
if ((horizontal_step_size == 0) && (vertical_step_size == 0))
getStepSize(anchor_one, anchor_two, 0);
getStepSize(anchor_one, anchor_two, 1);
getStepSize() in greater detail soon. Once the step size has been determined, the new
top coordinates are calculated by adding the step size to the DIV's current location:
var new_left = first_left + horizontal_step_size;
var new_top = first_top + vertical_step_size;
Before the DIV is moved you have to make sure you're not overshooting the ending anchor of the segment. This is actually a bit trickier than you might initially guess. I wrote a separate function called
atEndOfPath() to figure out whether or not the DIV is about to overshoot the ending anchor. The
atEndOfPath() function needs to know the step size, the position of the end of the segment, and where the moveDiv() function wants to move the DIV. Based on these numbers, it returns
true if the DIV is about to move past the end of the segment, or
false if it's not. If it's about to move past the end of the segment, the DIV should instead move to the end anchor.
if (atEndOfPath(left_step_size, second_left, new_left)
||(atEndOfPath(top_step_size, second_top, new_top)))
new_left = second_left;
new_top = second_top;
Now, all you have to do before moving the DIV is to add "px" at the end of the coordinates, unless the page is being rendered by Netscape Navigator 4:
new_left = new_left + "px";
new_top = new_top + "px";
And finally, you can move the DIV:
the_style.left = new_left;
the_style.top = new_top;
But even after the DIV has moved, there's more work to do. You have to figure out what the script should do next. There are two situations. If the script has reached the end of the segment, it should call
getAnchor() to start working on the next segment. If the script hasn't reached the end of the segment, then it should call
moveDiv() again in a short time to move the DIV along the segment a bit.
The first part of this is easy:
if ((parseInt(new_left) == parseInt(second_left)) &&
(parseInt(new_top) == parseInt(second_top)))
If the DIV is the end of the segment, it calls
getAnchors() to get the next set of anchors. The second part is slightly trickier. You have to call
moveDiv() again with the correct parameters. The call to
moveDiv() has five parameters: how far into the path array we are, the position from which movement starts, the place where it ends, the horizontal step size, and the vertical step size. Something like this:
moveDiv(1, '120:100', '140:100', 10, 0);
The last three lines of the function create the string, and then use
setTimeout() to call the function in a hundredth of a second:
var new_anchor_one = new_left + ":" + new_top;
var timeout_string = "moveDiv(" +
array_position + ", '" + new_anchor_one + "', '" +
anchor_two + "', " + left_step_size + "," +
top_step_size + ");";
the_timeout = setTimeout(timeout_string, 10);
Here's the function which determines whether or not the DIV is about to go past the segment boundary:
function atEndOfPath(the_step_size, second_number, new_number)
var the_end = false;
if (((the_step_size > 0) && (new_number > second_number)) ||
((the_step_size < 0) && (new_number < second_number)))
the_end = true;
This function takes parameters representing the amount the DIV should move each time, the end of the segment, and the location the DIV is about to move to. There are two conditions that need to be considered. If
the_step_size is positive, that means the DIV is moving right or down, so the DIV will have gone too far if the
new_number parameter (the next step in the movement) is greater than the
second_number. On the other hand, if
the_step_size is negative, that means the DIV is moving to the left, or up, so the DIV has gone too far if
new_number is less than second_number.
The last function figures out the step size and is pretty simple: it simply gets the relevant coordinate of the first and second point, and divides the distance between them by 10. This means that each segment will be broken up into 10 pieces, and
moveDiv() will be called 10 times per segment.
And that is that. As you can see, it's quite a bit more complicated than any of the examples we saw earlier. However, it's a pretty good solution to the general problem of smoothly moving DIVs along complicated paths.
Time to Get Animated
Animation can take on many forms, but they generally follow a similar model. Create one or more images and play them in succession at a speed that makes it seem that one object is moving smoothly. The techniques you've learned here can apply to many effects, from scrolling menus to making superman fly across your screen. It takes a little practice, but once you get the hang of it, the possibilities are limited only by your imagination.
Return to the Web Development DevCenter.