Es lassen sich Animationen mit beliebigen Bewegungen programmieren, indem man Animatoren programmiert. Um das Konzept der Animatoren zu verstehen, muss man den Datenfluss innerhalb CAnim kennen.
Den Datenfluss innerhalb CAnim zeigt folgenden Bildchen:
Die aktuelle Cursorposition bestimmt, welche Tracks momentan aktiv sind. Für die aktiven Tracks wird je ein Parameterwert zwischen 0 und 1 berechnet, welcher die Cursorposition innerhalb dieses Tracks angibt. Steht der Cursor genau am Anfang eines Tracks, hat der Parameter den Wert 0, steht der Cursor genau am Ende des Tracks, hat er den Wert 1. Dieser Parameterwert wird an den Animator weitergereicht. Der Standard-Animator holt sich vom Track den Start- und Endzustand des Objektes und berechnet anhand des Parameters die aktuelle Position, Grösse und Deckkraft. Diese Werte gibt der Animator an Funktionen des Actors weiter, welcher die zugehörigen HTML-Objekte entsprechend verändert.
Ein Animator hat die Aufgabe, aufgrund der aktuellen Cursorposition innerhalb des Tracks, welche als Parameterwert zwischen 0 und 1 vorliegt, den entsprechenden Objektzustand zu berechnen und diesen an den Actor weiterzureichen.
In CAnim ist ein Standard-Animator eingebaut, der zwischen zwei Objektzuständen interpoliert, die beim Track gespeichert sind. Der Standard-Animator kann für jeden Track oder jeden Actor durch eine eigene Implementation ersetzt werden. Diese Implementation kann bestimmte Funktionen des Standard-Animators überladen. Ein Beispiel eines solchen Animators haben wird bereits kennen gelernt: CPolygon. CPolygon ersetzt die standardmässige Bewegung entlang einer Geraden durch eine Bewegung entlang eines beliebigen Polygons.
Wir wollen nun einen eigenen Animator CParabel programmieren, der einen Ball hüpfen lässt. Der Animator soll die Bewegung eines Objektes zwischen zwei Orten so berechnen, dass eine Parabel von einstellbarer Höhe entsteht.
Den Start- und Endpunkt kann der Animator vom entsprechenden Track erfragen. Die Höhe der Parabel könnte beim Animator gespeichert werden. Flexibler wäre es jedoch, wenn die Höhe ebenfalls beim Track in irgendeiner Form gespeichert werden könnte. Dies ist tatsächlich möglich, indem bei AddTrack() neben den Koordinaten, Deckkraft und Grösse ein weiterer Wert angegeben wird:
myAnim.AddTrack( 'ball', 0, 1, [x1,y1,,,,jumpHeight], [x2,y2], parabelAnimator );
Der noch zu programmierende parabelAnimator kann so für beliebig viele Tracks wiederverwendet werden. Wir werden bei CParabel eine Standard-Höhe speichern, die zum Einsatz kommt, wenn beim Track keine Höhe angegeben wird.
Die Y-Koordinate der Parabel muss nun so berechnet werden, dass beim Parameterwert 0.5 die Sprunghöhe erreicht ist. In X-Richtung wird einfach zwischen Startwert-X und Endwert-X linear interpoliert. In Y-Richtung lautet die Formel für eine Parabel:
y = -4 * parameter * (parameter-1) * jumpHeight
Dieser Wert wird nun dem linear interpolierten Y-Wert zwischen Start-Y und End-Y hinzu addiert. Der Code für CParabel sieht folgendermassen aus:
function CParabel( aDefaultHeight ) { this.DefaultHeight = aDefaultHeight; } CParabel.prototype.OverrideMove = function( aActor, aTrack, aParam ) { // Start- und Endwerte des Tracks erfragen var startState = aTrack.StartState; var endState = aTrack.EndState; var startX = startState.PosX; var startY = startState.PosY; var endX = endState.PosX; var endY = endState.PosY; var height = startState.Any; if (!xNum(height)) height = this.DefaultHeight; // aktuelle Position berechnen var x = Range( aParam, startX, endX ); var y = Range( aParam, startY, endY ) - 4 * aParam * (aParam-1) * height; // Objekt verschieben aActor.MoveTo( x, y ); }
Wird bei einem Animator die Funktion OverrideMove implementiert, so ruft CAnim nicht die Move-Funktion des Standard-Animators, sondern eben OverrideMove des von uns programmierten Animators. In OverrideMove müssen wir dann aActor.MoveTo(x,y) rufen, um die Bewegung schliesslich auszuführen.
Zunächst muss allerdings die Position berechnet werden: startState und endState sind vom Typ CObjState, welcher wiefolgt definiert ist und seine Daten von AddTrack bzw. AddTracks erhält:
class CObjState { PosX, PosY, Opacity, Width, Height, Any; }
In startState.Any haben wir unsere Sprunghöhe gespeichert, oder wenn sie nicht angegeben wurde enthält Any den Wert ''.
Das Berechnen der Objektposition ist einfach. Die von aParam abhängige X-Position wird mit der Funktion Range(aParam,aMin,aMax) interpoliert. Ganz analog die Y-Position, ausser dass diese um die Parabelhöhe korrigiert wird.
Der oben programmierte Animator CParabel wird irgendwo am Anfang in das Javascript der Webseite eingebunden. Die Anwendung ist dann sehr einfach. Wir wollen einen Ball ein paar Sprünge machen lassen:
function BuildAnimation() { var parabel = new CParabel( -50 ); // Negative Y Werte zeigen nach oben! var dt = 1000; myAnim.AddFrames( [dt,dt,dt,dt] ); myAnim.AddActor( 'ball', parabel ); myAnim.AddTracks( 'ball', 0, [200,120], [400,120], [350,60], [250,60], [200,120] ); myAnim.Load(); // initialisiert die Animation }
Da bei AddTracks kein Animator als Argument übergeben werden kann, muss unser Animator dem Actor des Balls mit AddActor() zugeweisen werden.
In Beispiel 1 springt der Ball in einem räumlichen Rechteck von Ecke zu Ecke. Um die Räumlichkeit hervor zu heben, möchten wir die Grösse des Balles verkleinern, wenn der Ball nach hinten springt und wir möchten die Deckkraft verringern, sodass es aussieht, als ob der Ball im Nebel verschwindet. Zudem wollen wir den Ball nicht immer gleich hoch springen lassen. Dazu müssen wir Beispiel 1 nur um ein paar Werte ergänzen:
function BuildAnimation() { var parabel = new CParabel( -50 ); // Negative Y Werte zeigen nach oben! var dt = 1000; myAnim.AddFrames( [dt,dt,dt,dt] ); myAnim.AddActor( 'ball', parabel ); myAnim.AddTracks( 'ball', 0, [200,120,1,24,24,-100], [400,120,1,24,24,-75], [350,60,0.7,18,18,-50], [250,60,0.7,18,18,-75], [200,120,1,24,24] ); myAnim.Load(); // initialisiert die Animation }
Schiesslich wäre es auch noch möglich, den Pfad über eine Funktion zu modellieren, wie in den bisherigen Beispielen gezeigt!