Skip to content

Instantly share code, notes, and snippets.

@gtzilla gtzilla/raft.js
Created Jul 30, 2011

Embed
What would you like to do?
RAFT
/*
*
*
* file: raft.js
* R.A.F.T.
*
* Request
* Animation
* Frame
* Timer/Template
*
* author: gregory tomlinson
* desc: handy scaffolding for doing continous animations
*
* the requestion animation frame works very well as a lazy timer
* it's cpu cycles are low and it informs the browser the
* intention is to change the DOM
*
* Some Rafting terminology borrowed from
* <http://en.wikipedia.org/wiki/Rafting>
*
* usage:
* var animantion=raft();
* // where raft_inst is current instance of raft();
* animation.add( function(data, ts, raft_inst) {
* console.log(data.foo, ts);
* }, { foo: "bar" }, this);
*
*
* animation.add(function(data,ts,raft_inst) {
* console.log(data.foo, ts);
* }, { foo: "car" }, this);
*
*
* // start it up
* animation.paddle();
*
*
* // full stop, ensign
* animation.stop();
*
*
* Addtional Declartion Styles / Type / Methods:
*
*
* raft(func, {}, this).paddle();
* // add a single method with data {} and scope 'this'
*
* raft(func).paddle();
* // data is empty, scope defaults to window
*
* raft(func, null, this).paddle();
* // data set to empty object, scope set to 'this'
*
* raft({
* method:func,
* data:{},
* scope:this
* }).paddle();
*
*
* Provide a list of methods, format
* [{ method:func, data:{}, scope:this }]
*
* raft([{
* method:function(data,ts) {
* },
* data:{
* foo:"bar"
* },
* scope:this
* },{
* method:function(data,ts) {
* },
* data:{
* count:0
* },
* scope:this
* }]).paddle();
*
*
* Pass @param data & @param scope to all methods via raft();
* raft([
* function(data,ts) { data.count+=1 },
* function(data,ts, r_inst ) { data.count+=1; if((ts-data.start_ts)/1000 > 200) { r_inst.stop(); } }
* ], { count:0, start_ts:(new Date()).getTime() }, myObj).paddle();
*
*
* var rr=raft(func,{},this);
* var r2 = raft( rr );
* // r2.length === 1
* r2.paddle();
*
* Though more handy would be
* var rs = raft().splice(0, 0, r1, r2, r3 );
* rs.paddle();
* // where r(1-3) are instances of raft(), each with
* // there own list of 'chambers'
*
* // or inflate can you used like raft( ... )
* raft().inflate([func, func], data, scope)
*
*
*
* */
(function(window){
/*
* Public method
* window.raft();
*
* sets up a new raft.prototype.inflate();
*
* Build a raft and go for a ride:
* raft( ... ).paddle();
*
* */
function raft( funcLst, data, scope ) {
return new raft.fn.inflate( funcLst, data, scope );
}
var push=Array.prototype.push;
raft.fn=raft.prototype={
constructor:raft,
iterateMethod:"__loopChambers",
version:"0.2",
allowRun:true,
active:false,
paddle: function() {
// take this raft for a run
// paddle, paddle paddle!
if(this.active) {
return this;
}
this.allowRun=true;
this.active=true;
this.surf();
return this;
},
/*
* Add
* @param method func to execute. Receives 3 arguments
* 1. data, passed in as param 2. An empty obj
* if not specific.
* 2. ts - timestamp from animation frame
* 3. instance of current Raft
*
* @param data data, typically obj to be passed to
* method in param position 1
*
* @param scope An object to execute scope via .call()
*
* */
add:function(method, data, scope ) {
if(!method){ return this; }
push.call(this, this.__chamber( method, data, scope ));
return this;
},
__chamber:function(method,data,scope){
return {
"method":method,
"data":data,
"scope":scope || window
}
},
remove: function( method ) {
var chambers=this, item;
for(var i=0; i<chambers.length; i++) {
item=chambers[i];
if(item.method === method) {
//TODO semi untested
chambers.splice(i,1);
//this.splice(i,1);
}
}
return this;
},
removeAll: function() {
Array.prototype.splice.call(this, 0, this.length);
},
stop: function() {
this.allowRun=false;
return this;
},
/*
*
* run the requestionAnimationFrame and callback to
* __loopChambers
*
* */
surf:function() {
var self=this, func_name=self.iterateMethod;
if(!this.allowRun) {
this.active=false;
return this;
}
this.animation_frame.call(window,function(ts){
var result = self[func_name](ts);
if(!result) {
self.active=false;
return;
}
self.surf(); // continue the loop
});
return this;
},
/*
*
* This method is an exception, doesn't return 'this'
*
* play chambers (this[this.iterateMethod]()) can control
* animation on/off
*
* If all methods return not true, loop will continue.
*
* If any method returns true, the loop stops.
*
* In this way, return true is like <raft instance>.stop()
*
*
* */
__loopChambers:function(ts) {
var chambers=this, item,
results=[], response;
chambersloop: for(var i=0, l=chambers.length; i<l; i++) {
item=chambers[i];
response=item.method.call( item.scope, item.data||{}, ts, this );
if(response) {
// funcs return null (not true)
// by default, leverage this
// funcs by default will contine the teh loop
results.push(i);
}
}
if(results.length > 0) {
return false;
}
// returning true contines the loop
// this is different than the methods called
// which are attempting to become true
// and end the loop
return true;
},
/*
*
* Inflate the raft (initalize)
*
* @param funcLst obj,arr or func
* obj={ method:func, data:{}, scope:this||someObj }
* arr=[{ method:func, data:{}, scope:this||someObj }]
* @param data (optional)
* @param scope (optional)
*
* Inflate is slightly unique. Not only can @param
* funcLst, be an obj, array or func. But when
* param funcLst is an obj or arr, optional params
* are default if not specified.
*
* raft([{
* method:function(data,ts){},
* data:{ local:true }
* },{
* method:function(data,ts){}
* }], { global:true }, this||someObj ).paddle();
*
* */
inflate: function( funcLst, data, scope ) {
var item;
this.allowRun=true;
this.active=this.active||false;
if(funcLst && typeof funcLst === "function") {
this.add( funcLst, data, scope );
} else if(funcLst && funcLst.length > 0 ) {
for(var i=0, l=funcLst.length; i<l; i++) {
item=funcLst[i];
if(typeof item === "function") {
this.add( item, data || {}, scope );
} else {
this.add( item.method, item.data || data || {}, item.scope || scope );
}
}
} else if(funcLst && funcLst.method &&
typeof funcLst.method === "function") {
// { method:func, data:{}, scope:this||someObj }
this.add(funcLst.method, funcLst.data || data || {}, funcLst.scope || scope );
}
return this;
},
/*
* Manage 'chambers' in raft
*
*
* */
// array-like
length:0,
/*
*
* treat lime Array.prototype.slice
*
* @return raft() instance of raft with sliced chambers attached
*
* */
slice:function() {
// doesn't conform to raft norm. returns raft instance, not this
return raft( Array.prototype.slice.apply(this,arguments) );
},
/*
*
* Works like Array.prototype.splice
*
* @param start
* @param howMany
* @param ... added elements
* 3rd param and beyond can resemble any format
* that inflate can handle
* including
* .splice(0,1,raft(), raft())
* .splice(0,0,[func, func]
* // note scope and data are not assignable in this format
*
* .splice(0,1, func)
* .splice(0, 1, [{method:func, data:{}, scope:window},{ ... }]
*
* */
splice:function() {
// doesnt conform to raft norm, returns raft instances cut
var args=Array.prototype.slice.call(arguments,0);
var items=[], item;
if(args.length > 2) {
push.apply(items, args.slice(0,2));
for(var i=2; i<args.length; i++) {
if(typeof args[i] === "function") {
items.push( this.__chamber( args[i], {} ) );
} else if( args[i] && args[i].length>0 ) {
for(var j=0; j<args[i].length; j++) {
item=args[i][j];
if(item.method && typeof item.method === "function") {
items.push( this.__chamber( item.method, item.data || {}, item.scope ) );
} else if( item && typeof item === "function" ) {
items.push( this.__chamber(item, {}) );
}
//NOTE deeply nested would fail on this check
}
}
}
} else if(args.length===2) {
items=args;
}
return raft( Array.prototype.splice.apply(this, items ) );
}
}
// make it so new raft.prototype.init can be called and
// get all the prototypes of raft
raft.fn.inflate.prototype=raft.fn;
// setup a generic wrapper to browsers request animation frame
raft.fn.animation_frame=(function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
setTimeout(function() {
callback(new Date().getTime());
}, 1000 / 60);
};
})();
window.raft=raft;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.