# JavaScript Animations, Part II: Real-Time Animations - Doc JavaScript

## Real-Time Animations

First of all, let's get rid of the `step()`

and `animate()`

methods from our previous library. We won't be needing them anymore, because we're developing a real-time animation engine. `step()`

and `animate()`

process a given set of coordinates, which must be provided beforehand. Let's take another look at our set of sliding methods:

```
function slideBy(dx, dy, steps, interval, statement) {
var fx = this.left();
var fy = this.top();
var tx = fx + dx;
var ty = fy + dy;
this.slideTo(tx, ty, steps, interval, statement);
}
function slideTo(tx, ty, steps, interval, statement) {
var fx = this.left();
var fy = this.top();
var dx = tx - fx;
var dy = ty - fy;
var sx = dx / steps;
var sy = dy / steps;
var ar = new Array();
for (var i = 0; i < steps; i++) {
fx += sx;
fy += sy;
ar[i] = new pos(fx, fy);
}
this.path = ar;
this.statement = (statement) ? statement : null;
this.animate(interval);
}
```

We'll replace the `steps`

parameter with `increment`

-- the number of pixels to advance on each frame. In fact, that's the only change we need to apply to the `slideBy()`

method, because `slideBy()`

simply invokes `slideTo()`

. Here's the new version of our `slideBy()`

method:

```
function slideBy(dx, dy, increment, interval, statement) {
var fx = this.left();
var fy = this.top();
var tx = fx + dx;
var ty = fy + dy;
this.slideTo(tx, ty, increment, interval, statement);
}
```

Now let's take a look at the new version of our `slideTo()`

method:

```
function slideTo(tx, ty, increment, interval, statement) {
var fx = this.left();
var fy = this.top();
var dx = tx - fx;
var dy = ty - fy;
var steps = Math.round(Math.sqrt(dx * dx + dy * dy) / increment);
var sx = dx / steps;
var sy = dy / steps;
if (this.active) return;
this.active = 1;
this.lineSlide(0, fx, fy, tx, ty, sx, sy, interval);
}
```

As you can see, the function's first two statements are identical to those of the `slideBy()`

method. Notice that the number of steps in the animation is required in order to calculate the horizontal and vertical distance per step (`sx`

and `sy`

). The number of steps (before rounding) is equal to the total distance (`Math.sqrt(dx * dx + dy * dy)`

, based on Pythagoras) divided by the distance per step (`increment`

). The `Math.round()`

method rounds a number. Since we want a smooth animation, we must make sure the number of frames is an integer.

Notice that the function is terminated if the `active`

property has a `true`

value. We cannot animate the element if it is already in action. If the element isn't moving, we can start animating it, so the `active`

property is set to 1 (which is equivalent to `true`

). After we're sure the element isn't moving, we can start the animation by calling the `lineSlide()`

method:

```
function lineSlide(num, fx, fy, tx, ty, sx, sy, interval) {
num++;
this.moveTo(fx + sx * num, fy + sy * num);
if (this.left() == tx) {
this.active = 0;
if (this.statement) eval(this.statement);
return;
}
this.timer = setTimeout(this.name + ".lineSlide(" + num + -->
", " + fx + ", " + fy + ", " + tx + ", " + ty + ", " + sx + -->
", " + sy + ", " + interval + ")", interval);
}
```

The function calls itself after `interval`

milliseconds. When the element's horizontal coordinate (`this.left()`

) is equal to the target's horizontal coordinate (`tx`

), the recursion is terminated -- the `active`

property is set to 0, the specified statement is evaluated, and the function is terminated.

Notice that we aren't using the `pos()`

function in our new animation engine. Therefore, we'll slightly modify `moveTo()`

so it rounds the coordinates before moving the element (this isn't required, but it's good style):

```
function moveTo(x, y) {
this.element.left = Math.round(x);
this.element.top = Math.round(y);
}
```

In Column 18, JavaScript Animations, Part I, we discussed straight lines and circles. Now that we've built a new set of functions for real-time straight line animations, it's time to do the same for circular paths. For your reference, here's the old version of our `circle()`

method:

```
function circle(radius, angle0, angle1, steps, interval, -->
statement) {
var dangle = angle1 - angle0;
var sangle = dangle / steps;
var x = this.left();
var y = this.top();
var cx = x - radius * Math.cos(angle0 * Math.PI / 180);
var cy = y + radius * Math.sin(angle0 * Math.PI / 180);
var ar = new Array();
for (var i = 0; i < steps; i++) {
angle0 += sangle;
x = cx + radius * Math.cos(angle0 * Math.PI / 180);
y = cy - radius * Math.sin(angle0 * Math.PI / 180);
ar[i] = new pos(x, y);
}
this.path = ar;
this.statement = (statement) ? statement : null;
this.animate(interval);
}
```

Again, we'll replace the `steps`

parameter with `increment`

, specifying the angle incrementation (per step). Since the new version of the method will support endless loops, we must add that option to the method's interface. By specifying an ending angle of `null`

, the method will animate the element in an endless loop (be sure not to specify a statement, because the animation lasts forever). Here's the new version of the function:

```
function circle(radius, angle0, angle1, full, increment, -->
interval, statement) {
angle0 = getAngle(angle0);
if (angle1 != null) {
angle1 = getAngle(angle1);
var dangle = 360 * full + ((increment < 0) ? 360 - -->
Math.abs(angle1 - angle0) : Math.abs(angle1 - angle0));
increment = dangle / Math.round(dangle / increment);
angle1 = (increment < 0) ? angle0 - dangle : angle0 + dangle;
}
var cx = x - radius * Math.cos(angle0 * Math.PI / 180);
var cy = y + radius * Math.sin(angle0 * Math.PI / 180);
this.circleSlide(cx, cy, angle0, angle1, increment, -->
interval, statement);
}
```

Before we discuss this method, take a look at the `getAngle()`

function:

```
function getAngle(angle) {
var small = angle % 360;
if (small
```

It converts any angle to a 0-360 degree scale. For example, it converts 540 to 180, and -90 to 270. Note that the `%`

operator returns the remainder of a division operation.

Back to the `circle()`

method. We must make sure the animation is evenly spread, in case the user specifies an angle incrementation that does not equally divide the total angular distance. Remember that if `angle1`

is `null`

, the animation is an endless loop, so we do not need to calculate its value. However, if it's not `null`

, `angle1`

represents the ending angle. The statement:

```
var dangle = 360 * full + ((increment < 0) ? 360 - -->
Math.abs(angle1 - angle0) : Math.abs(angle1 - angle0));
```

computes the total angular distance, as a positive integer. Since one full circle is equivalent to 360 degrees, we multiply the value of `full`

by 360, and add the remaining distance. In a clockwise rotation (`increment < 0`

) the remaining distance is equal to:

`360 - Math.abs(angle1 - angle0)`

Likewise, in a counterclockwise rotation (`increment > 0`

) the remaining distance is equal to the following expression:

`Math.abs(angle1 - angle0)`

The statement:

`increment = dangle / Math.round(dangle / increment);`

adjusts the value of `increment`

so that it equally divides the entire angular distance. We then set `angle1`

to the ending angle, accounting for the number of full rotations. For example, it could be 720, -999, or any other integer value. The remaining portion of the function is explained in Column 18, JavaScript Animations, Part I, so let's take a look at the `circleSlide()`

method, which actually moves the element:

```
function circleSlide(cx, cy, angle0, angle1, increment, -->
interval, statement) {
angle0 += increment;
var x = cx + radius * Math.cos(angle0 * Math.PI / 180);
var y = cy - radius * Math.sin(angle0 * Math.PI / 180);
this.moveTo(x, y);
if ((angle1 != null) && (Math.abs(angle1 - angle0) < 0.001)) {
this.active = 0;
if (statement) eval(statement);
return;
}
this.timer = setTimeout(this.name + ".circleSlide(" + cx + -->
", " + cy + ", " + angle0 + ", " + angle1 + ", " + increment + -->
", " + interval + ", '" + statement + "')", interval);
}
```

First, we add the angle incrementation to the `angle0`

parameter. We then use two trigonometric functions to calculate the new x and y coordinates, and call our `moveTo()`

method to move the element to that position

The animation ends if `angle1`

isn't `null`

(`null`

specifies an endless loop), and when `angle0`

is equal to `angle1`

. However, notice that we cannot use the standard `==`

operator, because we are dealing with floating-point (real) numbers. This isn't the place to discuss binary representation of decimal numbers, but you should remember that floating-point calculations are not 100% accurate. Therefore, we must determine if `angle0`

and `angle1`

are **nearly** equal:

```
if ((angle1 != null) && (Math.abs(angle1 - angle0) < 0.001)) {
.
.
.
}
```

Feel free to replace 0.001 with any other relatively small number, such as 0.01 and 0.005, as long as it's not 0. If angle0 is (almost) equal to `angle1`

, the animation is terminated, and the specified statement is evaluated. Otherwise, the method's last statement invokes `setTimeout()`

to call itself recursively after `interval`

milliseconds.

Created: May 21, 1998

Revised: May 21, 1998

URL: http://www.webreference.com/js/column19/realtime.html