Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating circles with D3.js

So I have been messing around with D3.js for a couple of days now and I have basic circle generation / animation (that act as bubbles), and I was wondering how I could animate the circles on the x axsis, so they wobble back forth as the travel / transition to the top of the page. The current animation can be viewed at chrisrjones.com/bubbles-v1.html

like image 492
ipatch Avatar asked Dec 26 '22 15:12

ipatch


2 Answers

Demo of Solution:

Note the lack of symmetry:

http://jsfiddle.net/blakedietz/R5cRK/1/embedded/result/

Approach:

  • Determine a mathematical function that would properly model the movement that you want.
    • In this case we want a sine wave. We can modify aspects of each bubbles characteristics to give a unique movement pattern to each bubble.
  • Build or find a solution that utilizes the key concepts needed for this problem.
    • I like to search on bl.ocks.org/mbostock for examples that have the foundational parts of the problem that I'm trying to solve. On the site I found this example:http://bl.ocks.org/mbostock/1371412
    • Modify the given example to more similarly mirror the specified outcome.

Solution:

Here is a quick demo of a solution. I'll return to this to give you a full walk through. Modifications can be made to make the bubble placement and sizing as well as wiggle random/semi-unique per each bubble.

w = 960,
h = 500;

var svg = d3.select("body").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

var circle = svg.selectAll("circle")
    .data(d3.range(70).map(function(datum,interval) {
      return {
        x: interval*20,
        y: 0,
        dx: 5,
        dy: -3 * (Math.random()+1),
        mu: Math.random()*2
      };
    }))
  .enter().append("svg:circle")
    .attr("r", 2.5)
    .attr("fill","blue")
    .attr("opacity",".5");

var text = svg.append("svg:text")
    .attr("x", 20)
    .attr("y", 20);

var start = Date.now(),
    frames = 0;

d3.timer(function() 
{

  // Update the FPS meter.
  var now = Date.now(), duration = now - start;
  text.text(~~(++frames * 1000 / duration));
  if (duration >= 1000) frames = 0, start = now;

  // Update the circle positions.
  circle
      .attr("cx", function(d) { d.x += Math.random()*3*Math.sin(Math.random()*3*d.x + Math.random()*10); if (d.x > w) d.x -= w; else if (d.x < 0) d.x += w; return d.x; })
      .attr("cy", function(d) { d.y += d.dy ; if (d.y > h) d.y -= h; else if (d.y < 0) d.y += h; return d.y; })
      .attr("r",function(d)
      {
        return (d.y < 100) ? d3.select(this).attr("r") : d.mu*500/d.y;
      });
});
like image 116
arete Avatar answered Dec 31 '22 13:12

arete


You can do that using custom tween function for cx:

var circlesTransition = d3.selectAll("circle")
   .transition()
   .duration(5000)
   .attr("cy", "0")
   .attrTween('cx', function (d, i, a) {
        return function (t) {
          // Add salt, pepper and constants as per your taste
          return a + (Math.random() - 0.5) * 10;
        };
    });
like image 26
musically_ut Avatar answered Dec 31 '22 14:12

musically_ut