Siddharth Jain
Siddharth Jain

Reputation: 13

concentric emanating circles d3

I have an array of equally spaced values which I am using to draw concentric circles. I want to use an emanating effect, in essence, remove the outermost circle once its value exceeds the maximum value and add a new one at the center to compensate. I am unsure about manipulation on data set to remove and add new circle.

<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("body").append("svg");
var w = window.innerWidth;
var h = window.innerHeight;
var separation = Math.min(50, w/12);
var n=Math.floor((w/2)/separation);
var ellipse=new Array(n);
for(var i=1;i<=n;i++){
 ellipse[i-1]=(i*separation);
}

svg.attr("width", w).attr("height", h);
var g = svg.append("g");

var e=g.selectAll("ellipse")
  .data(ellipse)
  .enter()
  .append("ellipse")
  .attr("cx", w/2)
  .attr("cy", h/2)
  .attr("rx",0)
  .attr("ry",0)
  .transition()
  .duration(5000)
  .attr("rx", function(d,i){return ellipse[i];})
  .attr("ry", function(d,i){return ellipse[i]/5;});

  loop();
  function loop(){
    e.transition()
    .attr("rx", function(d,i){
           return ellipse[i]+separation;
         })
    .attr("ry", function(d,i){
           return (ellipse[i]+separation)/5;
         })
    .on("end",loop());
    }
  </script>

Upvotes: 1

Views: 709

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38171

You could approach it with a remove().exit() and enter().append() selection for each ring - but essentially you always have the same number of rings on the screen. Why not just recycle the same elements? When the size hits a threshold, reset it to zero, or some other starting value?

Something along the lines of:

var scale = d3.scaleLinear()
  .range(["orange","steelblue","purple"])
  .domain([0,60]);
  
var data = [0,10,20,30,40,50,60];

var width = 200;
var height = 200;

var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height);
  
var circles = svg.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("r",function(d) { return d; })
  .attr("transform","translate(80,80)")
  .attr("fill","none")
  .style("stroke-width","4")
  .style("stroke",function(d) { return scale(d) });
  
function transition() {
  // Update data, max d is 60:
  data = data.map(function(d) { return d == 60 ? 0 : d + 10});
  
  var i = 0;
  // Grow circles
  circles
     .data(data)
     .filter(function(d) { return d > 0 })
     .transition()
     .ease(d3.easeLinear)
     .attr("r", function(d) { return d; })
     .style("stroke", function(d) { return scale(d) })
     .style("opacity",function(d) { return d == 60 ? 0 : 1 }) 
     .duration(1000)
     .on("end",function(){if(++i==circles.size()-1) { transition(); } });
    
     
  // Reset circles where r == 0
  circles
    .filter(function(d) { return d == 0 })
    .attr("r", 0)
    .style("opacity",1)
    .style("stroke",function(d) { return scale(d); });
     
    
}

transition();
<script src="https://d3js.org/d3.v4.min.js"></script>

Note that .on("end",... triggers on each element's transition end - this is why I count to see if all elements are done transitioning before running the transition function again.

Upvotes: 3

Related Questions