Skip to content

Instantly share code, notes, and snippets.

@willbailey
Created September 12, 2011 01:54
Show Gist options
  • Save willbailey/1210437 to your computer and use it in GitHub Desktop.
Save willbailey/1210437 to your computer and use it in GitHub Desktop.
springy
var extend = function(dest) {
var sources = Array.prototype.slice.call(arguments, 1), source, key;
for (var i = 0, l = sources.length; i < l; i++) {
source = sources[i];
for (key in source) {
if (source.hasOwnProperty(key)) {
dest[key] = source[key];
}
}
}
};
/**
* SpringSolver provides a lazy run loop for solving springs in the system
* when all springs are at rest the loop terminates until a spring in the system
* is no longer at rest
*/
var Solver = (function() {
// collection of springs
var _springs = [];
// run state
var running = false;
// internal runloop
var loop = function() {
if (!running) return;
var allAtRest = true, spring;
for (var i = 0, l = _springs.length; i < l; i++) {
spring = _springs[i];
if (!spring.atRest()) {
allAtRest = false;
spring.solve();
}
}
if (!allAtRest && running) {
setTimeout(arguments.callee, 20);
} else {
running = false;
}
};
return {
// add a spring to the solver system
add: function(spring) {
if (_springs.indexOf(spring) !== -1) return;
_springs.push(spring);
this.solve();
},
// remove a spring from the system
remove: function(spring) {
var idx = _springs.indexOf(spring);
if (idx === -1) return;
springs.splice(idx, 1);
},
// inform the solver that it should check if solving is needed
solve: function() {
running = true;
loop();
}
};
})();
/**
* Spring takes the following named params as options. Tune these inputs
* to achieve the desired behavior.
* - damping: viscous damping coefficient to apply to the spring
* to prevent infinite oscilation
* - mass: mass of the object attached to the spring
* - tension: stiffness of the spring
* - rest: the resting value of the spring
* - prec: precision to use in determining if the spring is at rest
* - callback: callback to run when the value is changed
*/
var nop = function() {};
function Spring(options) {
for (var key in options) {
this[key] = options[key];
}
this.damping = this.damping || 0.92;
this.tension = this.tension || 30;
this.mass = this.mass || 100;
this.rest = this.rest || 0;
this.prec = this.prec || 0.00001;
this.pos = this.pos || this.rest;
this.callback = this.callback || nop;
this.velocity = 0;
Solver.add(this);
}
extend(Spring.prototype, {
// set the resting value for the spring
set: function(val) {
this.rest = val;
Solver.solve();
},
solve: function() {
this.velocity = this.velocity || 0;
var force = -this.tension * (this.pos - this.rest);
var acceleration = force / this.mass;
this.velocity += acceleration;
this.velocity *= this.damping;
this.pos += this.velocity;
this.callback(this.pos);
},
atRest: function() {
return Math.abs(this.pos - this.rest) <= this.prec &&
Math.abs(this.velocity) <= this.prec;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment