Miha Klasinc
Miha Klasinc

Reputation: 11

D3.js v4 circle radius transition not working as expected

Using D3.js v4, I'm trying to update the radius of a circle on button click.

The problem is that, instead of smooth transitions between the radii the circle is redrawn (going from 'radius1' to 0 and only then to 'radius2') upon each update.

Here's the complete code: https://jsfiddle.net/4r6hp9my/

Here's the circle update code snippet:

        var circles = svg.selectAll('circle').data(dataset);

        var enter = circles
        .enter()
        .append('circle')
        .attrs({
            cx: w/2,
            cy: h/2,
            fill: colorsScale,
            r: function(d,i){
                if(i == myCounter){
                    var x = rScale(d)
                return x; 
                }
            }
        });

        d3.select('#theButton')
            .on('click',function(){ 
                myCounter++
                 if(myCounter == dataset.length){
                    myCounter = 0;
                };
                updateData()
            });

        function updateData(){ 
            circles
            .merge(enter)
            .transition()
            .attr('r',function(d,i){
                if(i == myCounter){
                    return rScale(d);
                }
            });
            labels
            .text(function(d,i){
                if(i == myCounter){
                return d;
                }   
            });

        };

Upvotes: 1

Views: 966

Answers (1)

Dan Delaney
Dan Delaney

Reputation: 353

As mentioned by echonax, the issue is you're creating multiple circles based on the dataset. To get the smooth transition, draw only one circle, and update the radius based on 'myCounter'. An example:

var dataset = [2184,2184,3460,2610,2610,2452,842,1349,2430];

var myCounter = 0;
//svg dimensions
var h = 200;
var w = 200;

var svg = d3.select('body')
.append('svg')
.attrs({
  width: w,
  height: h
})
.classed('middle',true);
//color mapping
var colorsScale = d3.scaleLinear()
.domain([d3.min(dataset),d3.max(dataset)])
.range(['#FFB832','#C61C6F']);
//radius mapping
var rScale = d3.scaleLinear().domain([0, d3.max(dataset)]).range([0,50])
//labels
var label = svg.append("text").attrs({
  x: w/2,
  y: 20
}).text(function(){ return dataset[myCounter] });

//draw the circles
var circle = svg.append('circle')
.attrs({
    cx: w/2,
    cy: h/2,
    fill: function() { return colorsScale(dataset[myCounter]) },
    r: function() { return rScale(dataset[myCounter]) }
});
//button click
d3.select('#theButton')
  .on('click',function(){ 
  updateData()
});

function updateData(){
	myCounter++;
  if ( myCounter > dataset.length - 1 ) myCounter = 0;
  circle.transition().attr('r',function(){ return rScale(dataset[myCounter]) }).attr('fill', function() { return colorsScale(dataset[myCounter]) });
  label.text(function(){ return dataset[myCounter] });
};
html, body{
	height: 100%;
}

.middle{
	margin: 100px auto;
}

#theButton{
	position: absolute;
	left:50px;
	top:50px;
}
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
        
<button id="theButton" type="button">Click Me!</button>

Based on your data, there are a couple of times that the circle won't change as the data is the same, but the transition should work when it does.

Upvotes: 3

Related Questions