//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); });