blob: 2cb61aada891bd6fec052577f61bdb43f2a4089c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2012 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
rwt.qx.Class.define( "rwt.animation.Animation", {
extend : rwt.qx.Target,
construct : function() {
this.base( arguments );
this._renderer = [];
// properties:
this._duration = 1000;
this._transitionFunction = rwt.animation.Animation.transitions.linear;
// setup:
this._startOn = null;
this._finishOn = null;
this._totalTime = null;
this._config = null;
this._defaultRenderer = null;
// state info:
this._isRunning = false;
this._inQueue = false;
this._exclusive = false;
},
destruct : function() {
if( this.isRunning() ) {
this.skip();
} else {
this.cancel();
}
try {
for( var i = 0; i < this._renderer.length; i++ ) {
this._renderer[ i ].dispose();
}
} catch( ex ) {
throw "Could not Dispose AnimationRenderer: " + ex;
}
this._renderer = null;
},
events: {
"init" : "rwt.event.DataEvent",
"finish" : "rwt.event.DataEvent",
"cancel" : "rwt.event.DataEvent"
},
members : {
/////////////
// Public API
// Time in milliseconds
setDuration : function( value ) {
this._duration = value;
},
getDuration : function( value ) {
return this._duration;
},
setTransition : function( type ) {
this._transitionFunction = rwt.animation.Animation.transitions[ type ];
},
setProperties : function( properties ) {
this.setDuration( properties[ 0 ] );
this.setTransition( properties[ 1 ] );
},
getRenderer : function( number ) {
return this._renderer[ number ];
},
getDefaultRenderer : function( active ) {
if( this._defaultRenderer == null || this._defaultRenderer.isDisposed() ) {
this._defaultRenderer = new rwt.animation.AnimationRenderer( this );
}
if( typeof active != "undefined" ) {
this._defaultRenderer.setActive( active );
}
return this._defaultRenderer;
},
getRendererLength : function() {
return this._renderer.length;
},
getRendererIndex : function( renderer ) {
return this._renderer.indexOf( renderer );
},
getConfig : function() {
return this._config;
},
setExclusive : function( value ) {
this._exclusive = value;
},
getExclusive : function() {
return this._exclusive;
},
// config can by any value, but "appear", "disappear" and "change" are
// used by AnimationRenderer when autoStart is enabled. When using
// the widget-integration of AnimationRenderer, those should be used
// in the appropriate scenarios. A config is valid between start and cancel,
// then its set to null. Its given in the setup-function and all events.
start : function( config ) {
if ( !this.isStarted() ) {
rwt.animation.Animation._addToQueue( this );
this._inQueue = true;
this._config = config;
this._init();
}
return this.isStarted();
},
restart : function() {
var result = false;
if( this.isStarted() ) {
var config = this._config;
this.cancel();
result = this.start( config );
}
return result;
},
cancel : function() {
if( this.isStarted() ) {
this._inQueue = false;
this._isRunning = false;
this.createDispatchDataEvent( "cancel", this._config );
this._config = null;
rwt.animation.Animation._removeFromLoop( this );
}
},
// Unlike "cancel", this properly finishs the animation in any case by
// simply skipping over the remaining frames (If there are any).
skip : function() {
if( this.isStarted() ) {
if( !this.isRunning() ) {
this._render( 0 );
}
this._finish();
}
},
setRendererActive : function( value ) {
for( var i = 0; i < this._renderer.length; i++ ) {
this._renderer[ i ].setActive( value );
}
},
activateRendererOnce : function() {
for( var i = 0; i < this._renderer.length; i++ ) {
this._renderer[ i ].activateOnce();
}
},
isStarted : function() {
return this._inQueue;
},
isRunning : function() {
return this._isRunning;
},
////////////
// internals
_addRenderer : function( renderer ) {
this._renderer.push( renderer );
},
_removeRenderer : function( renderer ) {
if( this.isStarted() ) {
throw "Cannot remove AnimationRenderer: Animation already started!";
}
rwt.util.Arrays.remove( this._renderer, renderer );
},
_init : function() {
this.createDispatchDataEvent( "init", this._config );
this._startOn = null;
this._numberRenderer = this._renderer.length;
},
_loop : function( time ) {
if( this._startOn === null ) {
this._startOn = new Date().getTime();
this._finishOn = this._startOn + ( this._duration );
this._totalTime = this._duration;
}
if ( time >= this._finishOn ) {
this._finish();
} else {
var position = ( time - this._startOn ) / this._totalTime;
this._render( position );
}
},
_render : function( position ) {
if( !this._isRunning ) {
for( var i = 0; i < this._numberRenderer; i++ ) {
this._renderer[ i ]._setup( this._config );
}
this._isRunning = true;
}
var transitionValue = this._transitionFunction( position );
for( var i = 0; i < this._numberRenderer; i++ ) {
this._renderer[ i ]._render( transitionValue );
}
},
_finish : function() {
this._render( 1 );
var config = this._config;
this.cancel();
for( var i = 0; i < this._numberRenderer; i++ ) {
this._renderer[ i ]._finish( config );
}
this.createDispatchDataEvent( "finish", config );
}
},
statics : {
///////////////
// Global Queue
FPS : 60,
_queue : [],
_interval : null,
_exclusive : null,
_addToQueue : function( animation ) {
if( animation.getExclusive() ) {
this._exclusive = animation;
} else {
this._queue.push( animation );
}
if ( this._interval == null ) {
this._startLoop();
}
},
_removeFromLoop : function( animation ) {
if( animation === this._exclusive ) {
this._exclusive = null;
} else {
rwt.util.Arrays.remove( this._queue, animation );
}
if( this._exclusive === null && this._queue.length === 0 ) {
this._stopLoop();
}
},
_startLoop : function() {
this._interval = setInterval( this._mainLoop, Math.round( 1000 / this.FPS ) );
},
_stopLoop : function() {
window.clearInterval( this._interval );
this._interval = null;
},
_mainLoop : function() {
try {
if( !rwt.remote.EventUtil.getSuspended() ) {
var time = new Date().getTime();
var Animation = rwt.animation.Animation;
try {
if( Animation._exclusive !== null ) {
Animation._exclusive._loop( time );
} else {
for( var i=0, len = Animation._queue.length; i < len; i++ ) {
if( Animation._queue[ i ] ) {
Animation._queue[ i ]._loop( time );
}
}
}
} catch( e ) {
// prevent endless error-messages:
rwt.animation.Animation._stopLoop();
throw "Animation aborted: " + e;
}
}
} catch( ex ) {
rwt.runtime.ErrorHandler.processJavaScriptError( ex );
}
},
////////
// Util
blockGlobalFlushs : function( value ) {
rwt.widgets.base.Widget._inFlushGlobalQueues = value;
if( !value ) {
rwt.widgets.base.Widget._initAutoFlush( 0 );
}
},
//////////////
// Transitions
transitions : {
linear : function( position ) {
return position;
},
ease : function ( position ) {
var easeOut = -Math.pow( position - 1 , 2 ) + 1;
return ( -Math.cos( easeOut * Math.PI ) / 2 ) + 0.5;
},
easeIn : function ( position ) {
return Math.pow( position, 2 );
},
easeOut : function ( position ) {
return -Math.pow( position - 1 , 2 ) + 1;
},
easeInOut : function( position ) {
return ( -Math.cos( position * Math.PI ) / 2 ) + 0.5;
}
}
}
} );