After doing the animator script I realized (actually someone told me) that it would be nice with a description of how to create your own paths. I'll show you how this is done creating a sinus wave path.

Sinus Path

If you don't know what a sinus wave is you should probably take a course in basic math but you don't need to know this to understand how to create a path.

sinus wave

Path Objects

Like I wrote in the animator article a path is a Javascript object that has a few well defined properties and methods. I'll first create a basic template of the object that we will add functions to later.

function SinusPath() {
	this.x = 0;
	this.y = 0;
	this.step = int_step;
	this.reset = int_reset;
	
	//...
}

Initialization

First we need to design what in parameters that the constructor should have (the function SinusPath() is the constructor for all SinusPath objects). We also need to decide how the wave is going to be represented. The easiest way is just to let the path be a specified length and let it alternate around the x axis. For the wave to be general we also need to set the amplitude, the period and the phase angle. We'll start with the length and then modify the path object for the rest.

We also need to set the number of steps the path needs before it is complete. The function now looks like this:

function SinusPath(length, steps) {
//...
}

Now we add the actual code for the initialization. This is added inside the constructor and will be executed upon creation of the objects. (I'll use a JScript style coding using local variables inside the object and then convert it so it will work in Netscape later.) Since this object start in (0,0) and the sinus wave also start in (0,0) there is no calculations needed. I only copy the arguments to local variables for later use.

function SinusPath(length, steps) {
	this.x = 0;
	this.y = 0;
	this.step = int_step;
	this.reset = int_reset;
	
	//Private code :-)
	var currentStep = 0;
	if (steps <= 0) {		// When steps are less than one there shouldn't
		var totalSteps = 0;	// be any animation. Just set the position to
		this.x = length;	// the end position
	}
	else
		var totalSteps  = steps;

step() method

This method is the important part of all path objects (sometimes the initialization can be important as well). When called it should update the x and y values along the path one step. It should also check whether the update was successful. It will fail if the whole path has been traversed already. Here we use the Javascript standard object Math for the sinus function and the pi constant.

function int_step() {
	if (currentStep <= totalSteps && totalSteps > 0) {
		this.x = currentStep * length / totalSteps;
		this.y = Math.sin(2 * Math.PI / totalSteps * currentStep);
		currentStep++;
		return true;
	}
	else
		return false;
}

Note that one full period is 2*pi radians and thus we get the relative angle.

reset() method

Here we need to reset the counter currentStep and reset the positions to the initial position. Once again we check the totalSteps to see where the initial position is.

function int_reset() {
	currentStep = 0;
	if (totalSteps > 0) {
		this.x = 0;
		this.y = 0;
	}
	else {
		this.x = length;
		this.y = 0;
	}
}

Adding the inheritance

The path is actually done, although it doesn't behave like a sinus wave now. More about this later. Now we want this path to inherit the add, concat and rotate from the generic Path object and it is done by setting the prototype like this:

SinusPath.prototype = new Path()

Adding functionality

Below is an image with of a sinus wave with some terms added.

Descriptive sinus wave

Those that know the sinus function know that it returns values in the interval [1,-1] so we should multiply this value with the amplitude. We should also set the period so that the user can have more than one wave top in their SinusPath. The phase shift property is used for starting the wave at a different point than (0,0) (actually a different angle than 0). Below is the modify SinusPath.

function SinusPath(inLength, inAmplitude, inPeriod, inPhase, steps) {
	this.x = 0;
	this.y = 0;	//This will be set later
	this.step = int_step;
	this.reset = int_reset;
	
	//Private code :-)
	var length = inLength;
	var currentStep = 0;
	var amplitude = inAmplitude;
	if (inPeriod > 0)
		var period = inPeriod;
	else
		var period = length;
	var phase = inPhase;
	
	if (steps <= 0) {		// When steps are less than one there shouldn't
		var totalSteps = 0;	// be any animation. Just set the position to
		this.x = length;	// the end position
	}
	else
		var totalSteps  = steps;
	this.y = getY();
	
	function int_step() {
		if (currentStep <= totalSteps && totalSteps > 0) {
			this.x = currentStep / totalSteps * length;
			this.y = getY();
			currentStep++;
			return true;
		}
		else
			return false;
	}
	
	function int_reset() {
		currentStep = 0;
		if (totalSteps > 0)
			this.x = 0;
		else
			this.x = length;
		this.y = getY();
	}
	
	function getY() {
		var q;
		if (totalSteps > 0)
			q = currentStep / totalSteps;
		else
			q = 0;
		return amplitude * Math.sin(2 * Math.PI * length/period * q  - phase/length*2*Math.PI);
	}
}
SinusPath.prototype = new Path();

Netscape Issue

Netscape Navigator has a bug (or maybe it's a feature) that makes all the local variables common to all objects of the same type. So for example if I have two SinusPaths they share the variable currentStep. The way to solve this is to convert all the local variables to properties of the object.

// Before
var myValue = x;
// After
this.myValue = x;

This makes the code a bit less easy to read (that's why I waited to do this until now) but you can view the source here.

Sample

Follow this link to see an animation (and declaration) of this path.

Author: Erik Arvidsson