REPULTION_RANGE = 10; var kill = false; var paper = Raphael("diagram"); var circles = []; circles.push(paper.circle(130, 150, 0, 0)); circles.push(paper.circle(145, 129, 0, 0)); circles.push(paper.circle(120, 122, 0, 0)); circles.push(paper.circle(120, 120, 0, 0)); circles.push(paper.circle(100, 140, 0, 0)); circles.push(paper.circle(105, 100, 0, 0)); circles.push(paper.circle(109, 109, 0, 0)); circles[0].animate({r: 20}, 1000, "ease-in-out"); circles[1].animate({r: 12}, 1000, "ease-in-out"); circles[2].animate({r: 19}, 1000, "ease-in-out"); circles[3].animate({r: 10}, 1000, "ease-in-out"); circles[4].animate({r: 22}, 1000, "ease-in-out"); circles[5].animate({r: 22}, 1000, "ease-in-out"); circles[6].animate({r: 30}, 1000, "ease-in-out", function() { kill = true; }); function spread() { // For each circle... for ( var a = 0; a < circles.length; a++) { // Check the proximity to other circles for ( var b = 0; b < circles.length; b++) { if (a == b) continue; var dx = circles[a].attr("cx") - circles[b].attr("cx"), dy = circles[a].attr("cy") - circles[b].attr("cy"), radiusA = circles[a].attr("r"), radiusB = circles[b].attr("r"); // Use pythagorus theorum to work out how close the balls are var proximity = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)) - radiusA - radiusB; // If the balls are too close... if (proximity < REPULTION_RANGE) { // ... work out the new dx and dy var theta = Math.atan(dx / dy); var targetProximity = radiusA + radiusB + REPULTION_RANGE; // There are two dx and dy combinations for each arctan - ensure we are working with the correct one var xPolarity = (dx == 0) ? 1 : dx / Math.abs(dx), yPolarity = (dy == 0) ? 1 : dy / Math.abs(dy); var dx2 = Math.abs(targetProximity * Math.sin(theta)) * xPolarity, dy2 = Math.abs(targetProximity * Math.cos(theta)) * yPolarity; // ... and the change in dx, dy var ddx = dx2 - dx, ddy = dy2 - dy; // See which mall has the most inertia var totalSize = Math.pow(radiusA, 2) + Math.pow(radiusB, 2); // proportional to Pi var inertia = { a: (totalSize == 0) ? 0.5 : Math.pow(radiusB, 2) / totalSize, b: (totalSize == 0) ? 0.5 : Math.pow(radiusA, 2) / totalSize }; // ... finally, move the balls circles[a].attr({ cx: circles[a].attr("cx") + (ddx * inertia.a), cy: circles[a].attr("cy") + (ddy * inertia.a) }); circles[b].attr({ cx: circles[b].attr("cx") - (ddx * inertia.b), cy: circles[b].attr("cy") - (ddy * inertia.b) }); } } } if (!kill) { setTimeout(spread, 50); } } spread();