WaBis

walter.bislins.ch

Datei: Animation: anim.js

Inhalt der Datei: javascript/anim/src/anim.js
// anim.js (C) Walter Bislin, Januar 2005
//
// description and download:
//  http://walter.bislins.ch/doku/animation

// include x.js
//

function Range( aValue, aMin, aMax ) {
  // expands aValue(0..1) to span from aMin to aMax
  return aMin + (aMax-aMin)*aValue;
}

// ------ Globals

var MaxFrameRate = 10; // 40 ms (= 25 fps)

// ------ CPolygon (implements an Animator)

function CPolygon( aCoord, aOffsetX, aOffsetY, aPathModifyFunc )
// aCoord: [[x,y],...]
// aPathModifyFunc(aParam) must return f(aParam) so, that aParam transforms to Values 0..1
{
  if (!xDef(aPathModifyFunc)) aPathModifyFunc = null;
  if (!xNum(aOffsetX)) aOffsetX = 0;
  if (!xNum(aOffsetY)) aOffsetY = 0;
  this.PathX = new Array();
  this.PathY = new Array();
  this.PathP = new Array();
  this.PathModifyFunc = aPathModifyFunc;
  for (var i = 0; i < aCoord.length; i++) {
    this.PathX[i] = aCoord[i][0] + aOffsetX;
    this.PathY[i] = aCoord[i][1] + aOffsetY;
  }
  this.PathP[0] = 0.0;
  for (var i = 1; i < aCoord.length; i++) {
    var dx = this.PathX[i] - this.PathX[i-1];
    var dy = this.PathY[i] - this.PathY[i-1];
    this.PathP[i] = this.PathP[i-1] + Math.sqrt( dx * dx + dy * dy );
  }
  var dp = this.PathP[this.PathP.length-1];
  if (dp > 0) {
    for (var i = 1; i < this.PathP.length; i++) {
      this.PathP[i] /= dp;
    }
  }
}

CPolygon.prototype.FindSegment = function( aParam )
{
  for (var i = 1; i < this.PathP.length; i++) {
    if (this.PathP[i] > aParam) return i-1;
  }
  return this.PathP.length-1;
}

CPolygon.prototype.OverrideMove = function( aActor, aTrack, aParam )
{
  var x, y;
  if (aParam < 0) aParam = 0;
  if (aParam > 1) aParam = 1;
  var pp = (this.PathModifyFunc) ? this.PathModifyFunc(aParam) : aParam;
  if (pp == 0) {
    x = this.PathX[0];
    y = this.PathY[0];
  } else if (pp == 1) {
    x = this.PathX[this.PathX.length-1];
    y = this.PathY[this.PathY.length-1];
  } else {
    var i = this.FindSegment(pp);
    var ps = (pp - this.PathP[i]) / (this.PathP[i+1] - this.PathP[i]);
    x = Range( ps, this.PathX[i], this.PathX[i+1] );
    y = Range( ps, this.PathY[i], this.PathY[i+1] );
  }
  aActor.MoveTo( x, y );
}

// ------ CObjState

function CObjState( aData, aOffsetX, aOffsetY )
// CObjState( aData, aOffsetX=0, aOffsetY=aOffsetX )
// aData = [aPosX=0,aPosY=aPosX,aOpacity=-1,aWidth=-1,aHeight=aWidth,aAny='']
{
  var aPosX = 0, aPosY = 0, aOpacity = -1, aWidth = -1, aHeight = -1, aAny = '';
  if (!xDef(aData) || aData == null) aData = [];
  if (!xNum(aOffsetX)) aOffsetX = 0;
  if (!xNum(aOffsetY)) aOffsetY = aOffsetX;
  if (xNum(aData[0])) aPosX = aPosY = aData[0];
  if (xNum(aData[1])) aPosY = aData[1];
  if (xNum(aData[2])) aOpacity = aData[2];
  if (xNum(aData[3])) aWidth = aHeight = aData[3];
  if (xNum(aData[4])) aHeight = aData[4];
  if (xDef(aData[5])) aAny = aData[5];
  this.PosX = aPosX + aOffsetX;
  this.PosY = aPosY + aOffsetY;
  this.Opacity = aOpacity;
  this.Width = aWidth;
  this.Height = aHeight;
  this.Any = aAny;
}

// ------ CFrame

function CFrame( aTime )
{
  this.ID = -1;
  this.Time = aTime;
  this.Stop = false;
  this.StopTime = 0;  // 0 -> stop; > 0 -> stop n ms then continue
  this.NRepeats = 0;
  this.RepeatFrame = 0;
  this.RepeatCount = 0;
  this.UnloadedTracks = new Array(); // for next animation part
  this.LoadComplete = false;
}

CFrame.prototype.SetRepeat = function( aNRepeats, aRepeatFrame )
// aNRepeats = -1 -> for ever
{
  this.NRepeats = aNRepeats;
  this.RepeatFrame = aRepeatFrame;
  this.RepeatCount = 0;
}

CFrame.prototype.SetStop = function( aStopB, aStopTime )
{
  this.Stop = aStopB;
  this.StopTime = aStopTime;
}

CFrame.prototype.FindUnloadedTrack = function( aTrack )
{
  for (var i = 0; i < this.UnloadedTracks.length; i++) {
    if (this.UnloadedTracks[i] == aTrack) return i;
  }
  return -1;
}

CFrame.prototype.AddUnloadedTrack = function( aTrack )
{
  var i = this.FindUnloadedTrack( aTrack );
  if (i == -1) {
    this.UnloadedTracks[this.UnloadedTracks.length] = aTrack;
  }
}

CFrame.prototype.ClearUnloadedTracks = function()
{
  this.UnloadedTracks = new Array();
}

// ------ CActor

function CActor( aAnim, aDivID, aPathModifyFunc, aObjAnimator )
// CActor( aAnim, aDivID, aPathModifyFunc=null, aObjAnimator=null )
{
  if (!xDef(aPathModifyFunc)) aPathModifyFunc = null;
  if (!xDef(aObjAnimator)) aObjAnimator = null;
  this.Anim = aAnim;
  this.ID = aDivID;
  this.DocObj = xGet( aDivID );
  this.ObjAnimator = aObjAnimator;
  this.CurrAnimator = null;
  this.PathModifyFunc = aPathModifyFunc;
  this.Visible = false;
  this.PendingVisi = false;
  this.OpacityUsed = false;
}

CActor.prototype.Free = function() {
  this.Anim = null;
  this.ObjAnimator = null;
  this.CurrAnimator = null;
}

CActor.prototype.Show = function()
{
  if (!this.Visible) {
    xShow( this.DocObj );
    this.Visible = true;
    if (this.CurrAnimator && xDef(this.CurrAnimator.OnShow)) this.CurrAnimator.OnShow(this);
  }
}

CActor.prototype.Hide = function()
{
  if (this.Visible) {
    xHide( this.DocObj );
    this.Visible = false;
    if (this.CurrAnimator && xDef(this.CurrAnimator.OnHide)) this.CurrAnimator.OnHide(this);
  }
}

CActor.prototype.SetVisi = function( aVisi )
// defer visibility change until UpdateVisi is called for optimization
{
  this.PendingVisi = aVisi;
}

CActor.prototype.UpdateVisi = function()
{
  if (this.PendingVisi != this.Visible) {
    if (this.PendingVisi) {
      this.Show();
    } else {
      this.Hide();
    }
  }
}

CActor.prototype.MoveTo = function( x, y )
{
  xMoveTo( this.DocObj, x, y );
  if (this.CurrAnimator && xDef(this.CurrAnimator.OnMove)) this.CurrAnimator.OnMove( this, x, y );
}

CActor.prototype.ResizeTo = function( w, h )
{
  xResizeTo( this.DocObj, w, h );
  if (this.CurrAnimator && xDef(this.CurrAnimator.OnResize)) this.CurrAnimator.OnResize( this, w, h );
}

CActor.prototype.SetOpacity = function( o )
{
  xOpacity( this.DocObj, o );
  this.OpacityUsed = (o != 1);
}

// ------ CTrack

function CTrack( aAnim, aDivID, aStartFrameIx, aEndFrameIx, aStartState, aEndState, aPathModifyFunc, aObjAnimator )
{
  this.Anim = aAnim;
  this.Actor = aAnim.AddActor( aDivID );
  if (!xDef(aObjAnimator) || aObjAnimator == null) aObjAnimator = this.Actor.ObjAnimator;
  if (!xDef(aPathModifyFunc) || aPathModifyFunc == null) aPathModifyFunc = this.Actor.PathModifyFunc;
  this.ObjAnimator = aObjAnimator;
  this.StartFrameIx = aStartFrameIx;
  this.EndFrameIx = aEndFrameIx;
  this.StartState = aStartState;
  this.EndState = aEndState;
  this.StartTime = aAnim.Frames[aStartFrameIx].Time;
  this.EndTime = aAnim.Frames[aEndFrameIx].Time;
  this.DeltaTime = this.EndTime - this.StartTime;
  this.PathModifyFunc = aPathModifyFunc;
}

CTrack.prototype.Free = function() {
  this.Anim = null;
  this.Actor = null;
  this.ObjAnimator = null;
}

CTrack.prototype.InTime = function( aTime )
{
  if (aTime >= this.StartTime && aTime < this.EndTime) return true;
  return (aTime == this.EndTime && this.EndFrameIx == this.Anim.NFrames-1);
}

CTrack.prototype.CompParam = function( aTime )
{
  var p = (aTime - this.StartTime) / this.DeltaTime;
  if (this.PathModifyFunc) p = this.PathModifyFunc(p);
  return p;
}

CTrack.prototype.InFrame = function( aFrameIx )
{
  if (aFrameIx >= this.StartFrameIx && aFrameIx < this.EndFrameIx) return true;
  return (aFrameIx == this.EndFrameIx && aFrameIx == this.Anim.NFrames-1);
}

CTrack.prototype.GetCurrAnimator = function()
{
  var animator = this.ObjAnimator;
  if (!animator) animator = this.Actor.ObjAnimator;
  return animator;
}

CTrack.prototype.OnFrame = function( )
{
  var a = this.GetCurrAnimator();
  if (a && xDef(a.OnFrame)) a.OnFrame( this.Actor, this );
}

CTrack.prototype.Update = function( aTime, aFrameChangedB )
{
  if (this.InTime(aTime)) {
    var param = this.CompParam(aTime);
    var actor = this.Actor;
    var a = this.GetCurrAnimator();
    actor.CurrAnimator = a;
    if (aFrameChangedB) {
      if (a && xDef(a.OnFrame)) a.OnFrame( actor, this );
    }
    if (a && xDef(a.OverrideUpdate)) {
      a.OverrideUpdate( actor, aTrack, param );
    } else {
      this.ActorMove( param );
      this.ActorResize( param );
      this.ActorOpacity( param );
      if (a && xDef(a.OnUpdate)) a.OnUpdate( actor, aTrack, param );
    }
    actor.SetVisi(true);
  }
}

CTrack.prototype.ActorMove = function( aParam )
{
  var actor = this.Actor;
  var a = this.GetCurrAnimator();
  if (a && xDef(a.OverrideMove)) {
    a.OverrideMove( actor, this, aParam );
  } else {
    var currX = Range( aParam, this.StartState.PosX, this.EndState.PosX );
    var currY = Range( aParam, this.StartState.PosY, this.EndState.PosY );
    actor.MoveTo( currX, currY );
  }
}

CTrack.prototype.ActorOpacity = function( aParam )
{
  var actor = this.Actor;
  var a = this.GetCurrAnimator();
  if (a && xDef(a.OverrideOpacity)) {
    a.OverrideOpacity( actor, this, aParam );
  } else {
    if (this.StartState.Opacity >= 0) {
      var currO = Range( aParam, this.StartState.Opacity, this.EndState.Opacity );
      actor.SetOpacity( currO );
    } else if (actor.OpacityUsed) {
      actor.SetOpacity( 1 );
    }
  }
}

CTrack.prototype.ActorResize = function( aParam )
{
  var actor = this.Actor;
  var a = this.GetCurrAnimator();
  if (a && xDef(a.OverrideResize)) {
    a.OverrideResize( actor, this, aParam );
  } else {
    if (this.StartState.Width >= 0) {
      var currW = Range( aParam, this.StartState.Width, this.EndState.Width );
      var currH = Range( aParam, this.StartState.Height, this.EndState.Height );
      actor.ResizeTo( currW, currH );
    }
  }
}

CTrack.prototype.IsLoaded = function()
{
  var a = this.GetCurrAnimator();
  if (a && xDef(a.IsLoaded)) {
    return a.IsLoaded( this.Actor, this )
  }
  return true;
}

CTrack.prototype.Unload = function()
{
  var a = this.GetCurrAnimator();
  if (a && xDef(a.Unload)) {
    a.Unload( this );
  }
}

CTrack.prototype.Load = function()
{
  var a = this.GetCurrAnimator();
  if (a && xDef(a.Load)) {
    var me = this;
    a.Load( function(){ me.Anim.OnTrackLoad(me); }, this );
  }
}

CTrack.prototype.Preload = function()
{
  var a = this.GetCurrAnimator();
  if (a) {
    var me = this;
    if (xDef(a.Preload)) {
      a.Preload( function(){ me.Anim.OnTrackLoad(me); }, this );
    } else if (xDef(a.Load)) {
      a.Load( function(){ me.Anim.OnTrackLoad(me); }, this );
    }
  }
}

// ---- CAnim

function CAnim( aTimeArray )
// aTimeArray: rel Times between two frames in ms
{
  this.Init();
  if (xDef(aTimeArray)) this.AddFrames( aTimeArray );
  var me = this;
  xOnUnload( function() { me.Unload(); } );
}

CAnim.prototype.Init = function()
{
  // public
  this.TimerInterval = MaxFrameRate;
  this.OffsetX = 0;
  this.OffsetY = 0;
  this.FrameOffset = 0;
  this.AutoPlay = false;
  this.AutoPlayTime = 1000;
  this.Loop = false;
  this.PathModifyFunc = null;
  this.PathModifyForAll = false;
  // private
  this.Frames = new Array();
  this.Frames[0] = new CFrame( 0 );
  this.Frames[0].ID = 0;
  this.Frames[0].Stop = true;
  this.NFrames = 1;
  this.Tracks = new Array();
  this.NTracks = 0;
  this.Actors = new Array();
  this.NActors = 0;
  this.StartTimeClock = 0;
  this.CurrTime = 0;
  this.CurrFrameIx = 0;
  this.LastFrameIx = -1;
  this.CurrFrameID = 1;
  this.Timer = null;
  this.PauseTimer = null;
  this.Running = false;
  this.Loaded = false;
  // callbacks
  this.OnStop = new xCallbackChain();
  this.OnPlay = new xCallbackChain();
  this.OnFrame = new xCallbackChain();
  this.OnUpdate = new xCallbackChain();
  this.OnPartLoaded = new xCallbackChain();
  this.OnReadyState = new xCallbackChain();
  this.ReadyState = -1;
}

CAnim.prototype.GetCurrFrame = function() { return this.CurrFrameIx; }
CAnim.prototype.GetCurrFrameID = function() { return this.Frames[this.CurrFrameIx].ID; }
CAnim.prototype.GetCurrTime = function() { return this.CurrTime; }
CAnim.prototype.GetLastFrame = function() { return this.NFrames-1; }
CAnim.prototype.GetLastFrameID = function() { return this.Frames[this.NFrames-1].ID; }

CAnim.prototype.UsePathModifyFunc = function( aPathModifyFunc, aForAllTracksB )
{
  if (!xDef(aForAllTracksB)) aForAllTracksB = false;
  this.PathModifyFunc = aPathModifyFunc;
  this.PathModifyForAll = aForAllTracksB;
}

CAnim.prototype.AddOnReadyState = function( aOnReadyStateFunc )
// aOnReadyStateFunc( aReadyB )
{
  this.OnReadyState.Add( aOnReadyStateFunc );
}

CAnim.prototype.AddOnStop = function( aOnStopFunc ) { this.OnStop.Add(aOnStopFunc); }
CAnim.prototype.AddOnPlay = function( aOnPlayFunc ) { this.OnPlay.Add(aOnPlayFunc); }

CAnim.prototype.AddOnFrame = function( aOnFrameFunc )
// aOnFrameFunc( aFrameIx )
{
  this.OnFrame.Add(aOnFrameFunc);
}

CAnim.prototype.AddOnUpdate = function( aOnUpdateFunc )
// aOnUpdateFunc( aCurrTime )
{
  this.OnUpdate.Add(aOnUpdateFunc);
}

CAnim.prototype.AddOnPartLoaded = function( aOnPartLoadedFunc, aObj )
// aOnPartLoadedFunc( aStopFrameID )
{
  this.OnPartLoaded.Add(aOnPartLoadedFunc);
}

CAnim.prototype.AddFrames = function( aTimeArray, aResetOffsetB )
// AddFrames( aTimeArray, aResetOffsetB=true )
// aTimeArray: rel Times between two frames in ms
{
  if (!xDef(aResetOffsetB)) aResetOffsetB = true;
  if (aResetOffsetB) this.FrameOffset = this.NFrames-1;
  var absTime = this.Frames[this.NFrames-1].Time;
  for (var i = 0; i < aTimeArray.length; i++) {
    absTime += aTimeArray[i];
    this.Frames[this.Frames.length] = new CFrame( absTime );
  }
  this.NFrames = this.Frames.length;
  this.EndTime = this.Frames[this.NFrames-1].Time;
  var saveFrameOffset = this.FrameOffset;
  this.FrameOffset = 0;
  this.SetFrameStop( this.NFrames-1 );
  this.FrameOffset = saveFrameOffset;
}

CAnim.prototype.AddTrack = function( aDivID, aStartFrameIx, aNFrames, aStartState, aEndState, a1, a2 )
// AddTrack( aDivID, aStartFrameIx, aNFrames, aStartState=[], aEndState=aStartState, a1=null, a2=null )
// aStartState, aEndState: [aPosX=0,aPosY=aPosX,aOpacity=-1,aWidth=-1,aHeight=aWidth,aAny='']
// a1, a2: null | aPathModifyFunc | aAnimator
{
  var aAnimator = null, aPathModifyFunc = this.PathModifyFunc;
  if (xDef(a1)) {
    if (xFunc(a1)) aPathModifyFunc = a1; else aAnimator = a1;
  }
  if (xDef(a2)) {
    if (xFunc(a2)) aPathModifyFunc = a2; else aAnimator = a2;
  }
  var aEndFrameIx;
  aStartFrameIx += this.FrameOffset;
  if (aStartFrameIx < 0 || aStartFrameIx >= this.NFrames-1) {
    alert( 'CAnim.AddTrack: nonexistent Frame '+aStartFrameIx );
    return;
  }
  aEndFrameIx = aStartFrameIx + aNFrames;
  if (aNFrames == 0) aEndFrameIx = this.NFrames-1;
  if (aEndFrameIx >= this.NFrames) aEndFrameIx = this.NFrames-1;
  if (!xDef(aStartState) || aStartState == null) {
    aStartState = new CObjState( [], this.OffsetX, this.OffsetY );
  } else {
    aStartState = new CObjState( aStartState, this.OffsetX, this.OffsetY );
  }
  if (!xDef(aEndState) || aEndState == null) {
    aEndState = aStartState;
  } else {
    aEndState = new CObjState( aEndState, this.OffsetX, this.OffsetY );
  }
  var trk = new CTrack( this, aDivID, aStartFrameIx, aEndFrameIx, aStartState, aEndState, aPathModifyFunc, aAnimator );
  this.Tracks[this.NTracks++] = trk;
  if (!this.PathModifyForAll) this.PathModifyFunc = null;
}

CAnim.prototype.AddTracks = function( aDivID, aStartFrameIx )
// AddTracks( aDivID, aStartFrameIx, [...], <n>, [...], <n>, ... )
{
  var a = this.AddTracks.arguments, startState, endState;
  if (a.length == 3 || !xDef(a[3])) {
    this.AddTrack( aDivID, aStartFrameIx, 0, a[2] );
    return;
  }
  var startFrameIx, endFrameIx;
  startFrameIx = aStartFrameIx + this.FrameOffset;
  if (startFrameIx < 0 || startFrameIx >= this.NFrames-1) {
    alert( 'CAnim.AddTracks(\''+aDivID+'\'): nonexistent Frame '+startFrameIx );
    return;
  }
  var commonPathModifyFunc = null;
  var pathModifyFunc = this.PathModifyFunc;
  var i = 2;
  if (xFunc(a[i])) {
    pathModifyFunc = commonPathModifyFunc = a[i];
    i++;
    if (i >= a.length) return;
  }
  startState = new CObjState( a[i], this.OffsetX, this.OffsetY );
  i++;
  while (i < a.length && xDef(a[i]))
  {
    endFrameIx = startFrameIx + 1;
    while (i < a.length && (xNum(a[i]) || xFunc(a[i]))) {
      if (xNum(a[i])) {
        if (a[i] == 0) {
          endFrameIx = this.NFrames-1;
        } else {
          endFrameIx = startFrameIx + a[i];
        }
      }
      else if (xFunc(a[i])) {
        pathModifyFunc = a[i];
      }
      i++;
    }
    if (endFrameIx >= this.NFrames) endFrameIx = this.NFrames-1;
    if (startFrameIx == endFrameIx) { alert('CAnim.AddTracks(\''+aDivID+'\'): tracks exceeds end of animation');return; }
    if (i < a.length && xDef(a[i])) {
      if (a[i] == null) {
        endState = startState;
      } else {
        endState = new CObjState( a[i], this.OffsetX, this.OffsetY );
      }
      i++;
    } else {
      endState = startState;
    }
    var trk = new CTrack( this, aDivID, startFrameIx, endFrameIx, startState, endState, pathModifyFunc );
    pathModifyFunc = commonPathModifyFunc || this.PathModifyFunc;
    this.Tracks[this.NTracks++] = trk;
    startFrameIx = endFrameIx;
    startState = endState;
  }
  if (!this.PathModifyForAll) this.PathModifyFunc = null;
}

CAnim.prototype.SetFrameStop = function( aFrameIx, aStopTime, aStopB )
// SetFrameStop( aFrameIx, aStopTime=0, aStopB=true )
{
  aFrameIx += this.FrameOffset;
  if (aFrameIx < 0 || aFrameIx >= this.NFrames) return;
  if (!xNum(aStopTime)) aStopTime = 0;
  if (!xDef(aStopB)) aStopB = true;
  this.Frames[aFrameIx].SetStop( aStopB, aStopTime );
  if (aStopTime == 0) this.Frames[aFrameIx].ID = this.CurrFrameID++;
}

CAnim.prototype.SetFrameRepeat = function( aFrameIx, aNRepeats, aRepeatFrame )
// SetFrameRepeat( aFrameIx, aNRepeats=1, aRepeatFrame=-1 )
// aRepeatFrame  < 0: -> n Frames back
// aRepeatFrame >= 0: -> to that absolute FrameIx
{
  aFrameIx += this.FrameOffset;
  if (!xNum(aNRepeats)) aNRepeats = 1;
  if (!xNum(aRepeatFrame)) aRepeatFrame = -1;
  if (aRepeatFrame < 0) aRepeatFrame = aFrameIx + aRepeatFrame;
  if (aFrameIx < 0 || aFrameIx >= this.NFrames) {
    alert('CAnim.SetFrameRepeat: nonexistent Frame '+aFrameIx);
    return;
  }
  this.Frames[aFrameIx].SetRepeat( aNRepeats, aRepeatFrame );
}

CAnim.prototype.CheckReadyState = function()
{
  var stopFrameIx = this.GetCurrStopFrameIx(this.CurrFrameIx);
  var loaded = (this.IsPartLoaded(this.Frames[stopFrameIx].ID));
  var lastState = (this.ReadyState == -1) ? !loaded : ((this.ReadyState == 0) ? false : true);
  if (lastState != loaded) {
    this.OnReadyState.Call(loaded);
  }
  return loaded;
}

CAnim.prototype.Reset = function()
{
  if (!this.Loaded) return;
  this.JumpToStopFrame( 0 );
  this.ClearRepeats();
}

CAnim.prototype.Stop = function()
{
  if (!this.Loaded) return;
  if (this.Timer) clearInterval( this.Timer );
  if (this.PauseTimer) clearTimeout( this.PauseTimer );
  this.Timer = this.PauseTimer = null;
  this.Running = false;
  this.OnStop.Call();
}

CAnim.prototype.Play = function()
{
  if (!this.Loaded) return;
  if (this.Running) return;
  if (this.CheckReadyState()) {
    this.DoPlay();
  }
}

CAnim.prototype.DoPlay = function()
{
  if (this.Running) return;
  if (this.PauseTimer) clearTimeout( this.PauseTimer );
  this.PauseTimer = null;
  if (this.CurrTime >= this.EndTime)
  {
    this.JumpToFrame(0);
    if (this.AutoPlay)
    {
      // closure -> http://walter.bislins.ch/lexi/closure.html
      var me = this;
      this.PauseTimer = setTimeout( function(){ me.Play(); }, this.AutoPlayTime );
    }
  }
  else
  {
    // closure -> http://walter.bislins.ch/lexi/closure.html
    var me = this;
    this.StartTimeClock = xTimeMS() - this.CurrTime;
    this.Timer = setInterval( function(){ me.OnTimer(); }, this.TimerInterval );
    this.Running = true;
  }
  if (this.GetState() != 0) {
    this.OnPlay.Call();
  }
}

CAnim.prototype.PlayOrStop = function()
{
  if (!this.Loaded) return;
  if (this.GetState() != 0) this.Stop(); else this.Play();
}

CAnim.prototype.GetState = function()
// return 0: stop, 1: run, 2: pause
{
  if (this.Running) return 1;
  if (this.PauseTimer != null) return 2;
  return 0;
}

CAnim.prototype.JumpToFrame = function( aFrameIx )
{
  if (!this.Loaded) return;
  this.Stop();
  if (aFrameIx < 0) aFrameIx = 0;
  if (aFrameIx >= this.NFrames) aFrameIx = this.NFrames-1;
  this.CurrTime = this.Frames[aFrameIx].Time;
  this.CurrFrameIx = aFrameIx;
  this.UpdateObjects();
  this.OnFrame.Call(aFrameIx);
  this.CheckReadyState();
}

CAnim.prototype.GetNextStopFrameIx = function( aFrameIx )
{
  for (var i = aFrameIx+1; i < this.NFrames; i++)
  {
    if (this.Frames[i].ID >= 0) return i;
  }
  return this.NFrames-1;
}

CAnim.prototype.GetPrevStopFrameIx = function( aFrameIx )
{
  for (var i = aFrameIx-1; i >= 0; i--)
  {
    if (this.Frames[i].ID >= 0) return i;
  }
  return 0;
}

CAnim.prototype.GetCurrStopFrameIx = function( aFrameIx )
{
  for (var i = aFrameIx; i >= 0; i--)
  {
    if (this.Frames[i].ID >= 0) return i;
  }
  return 0;
}

CAnim.prototype.JumpToStopFrame = function( aFrameID )
{
  if (!this.Loaded) return;
  var frameIx = this.FindStopFrameIx(aFrameID);
  if (frameIx == -1) frameIx = this.NFrames-1;
  this.JumpToFrame( frameIx );
  this.LoadPart( this.Frames[frameIx].ID );
}

CAnim.prototype.JumpNext = function() { this.JumpNextStopFrame(); }
CAnim.prototype.JumpPrev = function() { this.JumpPrevStopFrame(); }

CAnim.prototype.JumpNextStopFrame = function()
{
  if (!this.Loaded) return;
  var frameIx = this.GetNextStopFrameIx(this.CurrFrameIx);
  this.JumpToStopFrame( this.Frames[frameIx].ID );
}

CAnim.prototype.JumpPrevStopFrame = function()
{
  if (!this.Loaded) return;
  var m = (this.CurrTime == this.Frames[this.CurrFrameIx].Time) ? 0 : 1;
  var frameIx = this.GetPrevStopFrameIx( this.CurrFrameIx+m);
  this.JumpToStopFrame( this.Frames[frameIx].ID );
}

CAnim.prototype.FindActor = function( aID )
{
  for (var i = 0; i < this.NActors; i++) if (this.Actors[i].ID == aID) return i;
  return -1;
}

CAnim.prototype.AddActor = function( aDivID, a1, a2 )
// returns new or existing CActor
// aAnimator [a1 or a2] can define the following functions:
//  aAnimator.Preload( aCallbackFunc, aTrack ) -> called to preload animator. Load() is called instead, if Preloed is not defined
//  aAnimator.Load( aCallbackFunc, aTrack ) -> called to load animator
//    aCallbackFunc( aTrack ) -> call this, if load complete
//  aAnimator.IsLoaded(aActor,aTrack) -> called to check load state
//  aAnimotor.Unload( aTrack ) -> called on unload to free some circular object references (gabage collection)
//  aAnimator.OverrideUpdate(aActor,aTrack,aParam)
//  aAnimator.OverrideMove(aActor,aTrack,aParam)
//  aAnimator.OverrideResize(aActor,aTrack,aParam)
//  aAnimator.OnShow(aActor) -> called when div of aAnimator becomes visible
//  aAnimator.OnHide(aActor) -> called when div of aAnimator becomes invisible
//  aAnimator.OnMove(aActor,x,y) -> called when div of aAnimator is moved
//  aAnimator.OnResize(aActor,w,h) -> called when div of aAnimator is resized
//  aAnimator.OnFrame(aActor,aTrack) -> called when frame changed
//  aAnimator.OnUpdate(aActor,aTrack,aParam) -> called on each timer event of active track
{
  var aAnimator = null, aPathModifyFunc = null;
  var actor, actorIx = this.FindActor(aDivID);
  if (xDef(a1)) {
    if (xFunc(a1)) aPathModifyFunc = a1; else aAnimator = a1;
  }
  if (xDef(a2)) {
    if (xFunc(a2)) aPathModifyFunc = a2; else aAnimator = a2;
  }
  if (actorIx == -1) {
    actor = new CActor( this, aDivID, aPathModifyFunc, aAnimator );
    this.Actors[this.NActors++] = actor;
  } else {
    actor = this.Actors[actorIx];
  }
  return actor;
}

CAnim.prototype.UpdateObjects = function()
{
  if (this.CurrFrameIx != this.LastFrameIx)
  {
    for (var i = 0; i < this.NActors; i++) this.Actors[i].SetVisi(false);
  }
  for (var i = 0; i < this.NTracks; i++)
  {
    this.Tracks[i].Update( this.CurrTime, (this.CurrFrameIx != this.LastFrameIx) );
  }
  if (this.CurrFrameIx != this.LastFrameIx)
  {
    this.LastFrameIx = this.CurrFrameIx;
    for (var i = 0; i < this.NActors; i++) this.Actors[i].UpdateVisi();
  }
  this.OnUpdate.Call(this.CurrTime);
}

CAnim.prototype.ClearRepeats = function()
{
  for (var i = 0; i < this.NFrames; i++) this.Frames[i].RepeatCount = 0;
}

CAnim.prototype.OnTimer = function()
{
  this.CurrTime = xTimeMS() - this.StartTimeClock;
  var nextFrameIx = this.CurrFrameIx+1;
  while (nextFrameIx < this.NFrames)
  {
    var nextFrame = this.Frames[nextFrameIx];
    if (this.CurrTime < nextFrame.Time)
    {
      this.UpdateObjects();
      return;
    }
    if (nextFrame.NRepeats != 0)
    {
      if (nextFrame.NRepeats == -1 || nextFrame.RepeatCount < nextFrame.NRepeats)
      {
        nextFrame.RepeatCount++;
        this.JumpToFrame( nextFrame.RepeatFrame );
        this.DoPlay();
        return;
      }
      else
      {
        nextFrame.RepeatCount = 0;
      }
    }
    if (nextFrame.Stop || nextFrameIx == this.NFrames-1)
    {
      this.JumpToFrame( nextFrameIx );
      if (nextFrame.StopTime > 0)
      {
        // closure -> http://walter.bislins.ch/lexi/closure.html
        var me = this;
        this.PauseTimer = setTimeout( function(){ me.DoPlay(); }, nextFrame.StopTime );
        this.OnPlay.Call();
        return;
      }
      if (this.AutoPlay)
      {
        if (nextFrameIx < this.NFrames-1 || this.Loop)
        {
          // closure -> http://walter.bislins.ch/lexi/closure.html
          var me = this;
          this.PauseTimer = setTimeout( function(){ me.Play(); }, this.AutoPlayTime );
          this.OnPlay.Call();
          return;
        }
      }
      return;
    }
    this.CurrFrameIx = nextFrameIx;
    this.OnFrame.Call(this.CurrFrameIx);
    nextFrameIx++;
  }
  this.JumpToFrame( this.NFrames-1 ); // fallback
}

CAnim.prototype.CollectUnloadedTracksInPart = function( aFrameIx )
{
  var lastFrameIx = this.GetNextStopFrameIx(aFrameIx);
  if (this.Frames[aFrameIx].ID == -1) aFrameIx = this.GetPrevStopFrameIx(aFrameIx);
  if (this.Frames[aFrameIx].LoadComplete) return 0;
  this.Frames[aFrameIx].ClearUnloadedTracks();
  for (var i = aFrameIx; i <= lastFrameIx; i++) {
    this.CollectUnloadedTracksInFrame( i, aFrameIx );
  }
  return this.Frames[aFrameIx].UnloadedTracks.length;
}

CAnim.prototype.CollectUnloadedTracksInFrame = function( aFrameIx, aStopFrameIx )
{
  if (aFrameIx < 0 || aFrameIx >= this.NFrames) return;
  for (var i = 0; i < this.NTracks; i++)
  {
    var trk = this.Tracks[i];
    if (trk.InFrame(aFrameIx)) {
      if (!trk.IsLoaded()) this.Frames[aStopFrameIx].AddUnloadedTrack(trk);
    }
  }
  return;
}

CAnim.prototype.OnTrackLoad = function( aTrack )
{
  aTrack.Update( this.CurrTime, true );
  this.CollectUnloadedTracks(false);
  this.CheckReadyState();
}

CAnim.prototype.OnLoadComplete = function( aStopFrameID )
{
  this.OnPartLoaded.Call(aStopFrameID);
}

CAnim.prototype.CollectUnloadedTracks = function(aInitB)
{
  for (var i = 0; i < this.NFrames; i++) {
    var frame = this.Frames[i];
    if (frame.ID >= 0) {
      if (aInitB) frame.LoadComplete = false;
      if (!frame.LoadComplete && this.CollectUnloadedTracksInPart(i) == 0) {
        frame.LoadComplete = true;
        if (!aInitB) this.OnLoadComplete(frame.ID);
      }
    }
  }
}

CAnim.prototype.Load = function()
{
  this.CurrTime = 0;
  this.CurrFrameIx = 0;
  this.Stop();
  this.CollectUnloadedTracks(true);
  this.UpdateObjects();
  // save to call play() here
  this.OnFrame.Call(this.CurrFrameIx);
  this.CheckReadyState();
  for (var i = 0; i < this.NTracks; i++) {
    this.Tracks[i].Preload();
  }
  this.LoadPart( 0 );
  this.Loaded = true;
}

CAnim.prototype.LoadPart = function( aStopFrameID )
{
  var thisStopFrameIx = this.FindStopFrameIx(aStopFrameID);
  if (thisStopFrameIx == -1) return;
  var nextStopFrameIx = this.GetNextStopFrameIx( thisStopFrameIx );
  for (var ti = 0; ti < this.NTracks; ti++) {
    for (var i = thisStopFrameIx; i <= nextStopFrameIx; i++) {
      if (this.Tracks[ti].InFrame(i)) this.Tracks[ti].Load();
    }
  }
  this.CollectUnloadedTracks(false);
}

CAnim.prototype.FindStopFrameIx = function( aStopFrameID )
{
  for (var i = 0; i < this.NFrames; i++) if (this.Frames[i].ID == aStopFrameID) return i;
  return -1;
}

CAnim.prototype.GetNUnloadedOfPart = function( aStopFrameID )
{
  var thisStopFrameIx = this.FindStopFrameIx(aStopFrameID);
  if (thisStopFrameIx == -1) return 0;
  var nUnloaded = this.Frames[thisStopFrameIx].UnloadedTracks.length;
  return nUnloaded;
}

CAnim.prototype.IsPartLoaded = function( aStopFrameID ) {
  return this.GetNUnloadedOfPart(aStopFrameID) == 0;
}

CAnim.prototype.Unload = function()
{
  if (!this.Loaded) return;
  for (var i = 0; i < this.NFrames; i++) this.Frames[i].ClearUnloadedTracks();
  for (var i = 0; i < this.NTracks; i++) this.Tracks[i].Unload();
  for (var i = 0; i < this.NTracks; i++) this.Tracks[i].Free();
  for (var i = 0; i < this.NActors; i++) this.Actors[i].Free();
  this.Init();
}


More Page Infos / Sitemap
Created Dienstag, 17. Juni 2014
Scroll to Top of Page
Changed Donnerstag, 25. Februar 2016