Skip to content

Instantly share code, notes, and snippets.

@edvakf
Created May 1, 2009 19:42
Show Gist options
  • Save edvakf/105217 to your computer and use it in GitHub Desktop.
Save edvakf/105217 to your computer and use it in GitHub Desktop.
javascript:(function(){
/* http://d.hatena.ne.jp/edvakf/20090501/1241208337 */
var Ripple = {
COLOR : [0,0,255], /* color in RGB */
R : 100, /* ripple radius */
T : 50, /* animation timesteps */
HALF_OSCIL_NUM : 4, /* number of half oscillations in T / angular velocity = HALF_OSCIL_NUM*PI/T */
RESOLUTION : 32, /* number of ColorStops to form gradient */
ripplets : [],
timer : null,
cache : {},
getRGBA : function(r,g,b,a){
return 'rgba(' + r + ',' + g + ',' + b + ',' + a +')';
},
/* ripplet generating function : time -> height */
generator : function(t){
return Math.pow(Math.sin(t*this.HALF_OSCIL_NUM*Math.PI/this.T),2).toFixed(2);
},
/* height of ripplet : (distance, time) -> height */
dampedHeight : function(r,t){
var R = this.R, T = this.T;
if (r > t*R/T) return 0;
return this.generator(t-T*r/R) * (R-r)/R * (1-t/T);
},
/* may be replaced by custom function depending on color scheme of your choice */
height2color : function(h){
return this.getRGBA(this.COLOR[0],this.COLOR[1],this.COLOR[2],h);
},
/* get height and convert to color : (distance, time) -> color */
getColorAt : function(r,t){
return this.height2color(this.dampedHeight(r,t));
},
/* set gradients for a state of ripple : (CanvasGradient,time) -> null */
setGradient : function(grad,t){
var num = this.RESOLUTION - 1;
for (var i=0;i<=num;i++) {
grad.addColorStop( i/num , this.getColorAt(this.R*i/num,t) );
}
},
/* start and/or continue animation */
animate : function(){
if (this.timer) {
var n = this.ripplets.length;
while (n--) {
var ripplet = this.ripplets[n];
ripplet.evolve();
if (ripplet.t == this.T) {
ripplet.finalize();
this.ripplets.splice(n,1);
}
}
if (!this.ripplets.length) {
clearInterval(this.timer);
this.timer = null;
}
} else {
var self = this;
this.timer = setInterval(function(){self.animate()},50);
}
},
/* add new Ripplet instance to the stack */
setRipplet : function(x,y){
this.ripplets.push(new this.Ripplet(x,y));
this.animate();
},
finalize : function(){
this.ripplets = [];
this.timer = null;
for(var x in this.cache) if(this.cache.hasOwnProperty(x)){
delete x;
}
this.cache = {};
}
};
/* Ripplet class */
Ripple.Ripplet = function(x,y){
var R = Ripple.R;
this.x = x;
this.y = y;
this.t = 0;
var canvas = this.canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.style.position = 'fixed';
canvas.style.left = x - R + 'px';
canvas.style.top = y - R + 'px';
canvas.width = R*2;
canvas.height = R*2;
this.ctx = canvas.getContext('2d');
this.ctx.clearRect(0,0,2*R,2*R);
};
Ripple.Ripplet.prototype = {
evolve : function(){
var ctx = this.ctx;
var R = Ripple.R;
ctx.clearRect(0,0,2*R,2*R);
if (Ripple.cache[this.t]) {
ctx.drawImage(Ripple.cache[this.t], 0, 0);
} else {
var R = Ripple.R;
ctx.beginPath();
var grad = ctx.createRadialGradient(R,R,0,R,R,R);
Ripple.setGradient(grad,this.t);
ctx.fillStyle = grad;
ctx.rect(0,0,2*R,2*R);
ctx.fill();
var canvas2 = document.createElement('canvas');
canvas2.height = this.canvas.height;
canvas2.width = this.canvas.width;
var ctx2 = canvas2.getContext('2d');
ctx2.drawImage(this.canvas,0,0);
Ripple.cache[this.t] = canvas2;
}
this.t++;
},
finalize : function(){
document.body.removeChild(this.canvas);
delete this.canvas;
delete this;
}
};
/* mouse object */
Ripple.mouse = {
timer : null,
x : null,
y : null,
isDown : false,
delegate : function(event){
if (!event) event = window.event;
Ripple.mouse[event.type](event);
},
mousedown : function(event){
if(event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
this.x = event.clientX;
this.y = event.clientY;
this.isDown = true;
Ripple.setRipplet(this.x,this.y);
/* set timer for mousemove */
var self = this;
this.timer = setInterval(function(){
if(self.isDown) Ripple.setRipplet(self.x,self.y);
},200);
},
mouseup : function(){
this.isDown = false;
if (this.timer){
clearInterval(this.timer);
this.timer = null;
}
},
mousemove : function(event){
if(this.isDown) {
this.x = event.clientX;
this.y = event.clientY;
}
}
};
function listen(event, handler){
if (window.addEventListener) {
window.addEventListener(event,handler,false);
} else if (window.attachEvent) {
window.attachEvent('on'+event,handler);
} else {
window['on'+event] = handler;
}
};
listen('mousedown',Ripple.mouse.delegate);
listen('mouseup',Ripple.mouse.delegate);
listen('mousemove',Ripple.mouse.delegate);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment