//Color and distance utils Forked from http://andreasstorm.com/
//Made by Boomer Shin
//CANVAS
$(function(){
var canvas = document.querySelector('canvas');
ctx = canvas.getContext('2d');
canvas.width = window.innerWidth - 30;
canvas.height = window.innerHeight - 30;
ctx.lineWidth = .3;
ctx.strokeStyle = (new Color(150)).style;
var mousePosition = {
x: canvas.width / 2,
y: canvas.height / 2,
trail: [120, 80, 40],
color: new Color(128),
draw: function(){
for(var i = 0; i < this.trail.length; i++){
ctx.beginPath();
ctx.strokeStyle = this.color.style;
ctx.arc(this.x, this.y, this.trail[i], 0, Math.PI * 2, false);
ctx.stroke();
}
},
update: function(){
for(var i = 0; i < this.trail.length; i++){
this.trail[i]--;
}
if(this.trail[this.trail.length - 1] <= 0){
this.trail.pop();
this.trail.unshift(120);
}
}
};
//
var minMass = 50;
var maxMass = 1500;
var minInitialMass = 2 * minMass
var deltaMass = maxMass - minInitialMass;
var G = 5;
var initialOrbs = 10;
var reflectFactor = 0.7;
var chunkLoss = 0.1;
//
//
var orbs = [];
//
//
function Orb(initialMass){
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (-.5 + Math.random()) * 3;
this.vy = (-.5 + Math.random()) * 3;
this.ax = 0;
this.ay = 0;
this.mass = initialMass || (Math.random() * deltaMass + minInitialMass);
this.radius = Math.sqrt(this.mass);
this.color = new Color();
this.update = function(){
this.vx += this.ax;
this.vy += this.ay;
this.x += this.vx;
this.y += this.vy;
// borders
if(this.x < this.radius){
this.x = this.radius;
this.vx = - this.vx * reflectFactor;
}
if(this.x > canvas.width - this.radius){
this.x = canvas.width - this.radius;
this.vx = - this.vx * reflectFactor;
}
if(this.y < this.radius){
this.y = this.radius;
this.vy = - this.vy * reflectFactor;
}
if(this.y > canvas.height - this.radius){
this.y = canvas.height - this.radius;
this.vy = - this.vy * reflectFactor;
}
// for next frame
this.ax = 0;
this.ay = 0;
};
this.changeMass = function(massChunk){
this.mass += massChunk;
this.radius = Math.sqrt(this.mass);
if(this.mass > maxMass){
this.explode();
}
};
this.explode = function(){
var radius = this.radius;
var force = this.mass / 10; // Yeah! Strong, right?
var newBornOrbs = [];
while(this.mass >= minMass){
// var chunk = Math.random() * this.mass + minInitialMass;
var chunk = minInitialMass;
if(chunk > this.mass){
chunk = this.mass;
}
this.mass -= chunk;
// last chunk!
if(this.mass < minMass){
chunk += this.mass;
this.mass -= this.mass;
}
var orb = new Orb(chunk);
newBornOrbs.push(orb);
}
var nChunks = newBornOrbs.length;
var iniAng = Math.random() * 2 * Math.PI;
var deltaAng = 2 * Math.PI / nChunks;
for(var i = 0; i < nChunks; i++){
var orb = newBornOrbs[i];
var cosAng = Math.cos(iniAng + deltaAng * i);
var sinAng = Math.sin(iniAng + deltaAng * i);
orb.x = radius * cosAng + this.x;
orb.y = radius * sinAng + this.y;
orb.vx = this.vx;
orb.vy = this.vy;
orb.color = this.color;
var fx = force * cosAng;
var fy = force * sinAng;
orb.addForce(fx, fy);
orbs.push(orb);
}
};
this.pullOrb = function(target){
if(target != this){ // avoid self-pulling
var dx = this.x - target.x;
var dy = this.y - target.y;
var distance = d2dist(this, target);
var minDistance = this.radius + target.radius;
if(minDistance < 1) { minDistance = 1; }
if(distance < minDistance){
if(this.mass >= target.mass){ // "Consume"
var massChunk = target.mass * chunkLoss;
if((target.mass - massChunk) > minMass){
this.changeMass(massChunk);
target.changeMass(-massChunk);
}
else{
this.changeMass(target.mass);
target.changeMass(-target.mass);
}
};
}
var f = (G * this.mass / (distance * distance));
var fx = (dx / distance) * f;
var fy = (dy / distance) * f;
target.addForce(fx, fy);
}
};
this.addForce = function(fx, fy){
this.ax += fx / this.mass;
this.ay += fy / this.mass;
}
this.draw = function(){
ctx.beginPath();
ctx.fillStyle = this.color.style;
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
ctx.fill();
}
}
//
function Color(min) {
min = min || 0;
this.r = colorValue(min);
this.g = colorValue(min);
this.b = colorValue(min);
this.style = createColorStyle(this.r, this.g, this.b);
};
function colorValue(min) {
return Math.floor(Math.random() * 255 + min);
}
function createColorStyle(r,g,b) {
return 'rgba(' + r + ',' + g + ',' + b + ', 0.8)';
}
function d2dist(dot1,dot2){
return Math.sqrt((dot1.x-dot2.x)*(dot1.x-dot2.x) + (dot1.y-dot2.y)*(dot1.y-dot2.y));
}
//
function createOrbs(){
var sun = new Orb(2500);
sun.x = canvas.width / 2;
sun.y = canvas.height / 2;
sun.vx = 0;
sun.vy = 0;
orbs.push(sun);
for(var i = 0; i < initialOrbs; i++){
orbs.push(new Orb());
}
}
function updateOrbs(){
for(var i = 0; i < orbs.length; i++){
var current = orbs[i];
var dx = mousePosition.x - current.x;
var dy = mousePosition.y - current.y;
var d = d2dist(mousePosition, current);
if(d < 1) d = 1;
var force = 500;
var fx = force * dx / (d*d);
var fy = force * dy / (d*d);
current.addForce(fx, fy);
for(var j = 0; j < orbs.length; j++){
if(i != j){
var target = orbs[j];
current.pullOrb(target);
}
}
}
for(var i = 0; i < orbs.length; i++){
var orb = orbs[i];
orb.update();
}
var aliveOrbs = [];
for(var i = 0; i < orbs.length; i++){
var orb = orbs[i];
if(orb.mass > 0){
aliveOrbs.push(orb);
}
}
orbs = aliveOrbs;
mousePosition.update();
}
function drawOrbs() {
for(i = 0; i < orbs.length; i++){
var orb = orbs[i];
orb.draw();
}
mousePosition.draw();
}
function animationCycle(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateOrbs();
drawOrbs();
requestAnimationFrame(animationCycle);
}
$('canvas').on('mousemove', function(e){
mousePosition.x = e.pageX;
mousePosition.y = e.pageY;
});
$('canvas').on('mouseleave', function(e){
mousePosition.x = canvas.width / 2;
mousePosition.y = canvas.height / 2;
});
createOrbs();
requestAnimationFrame(animationCycle);
});