four-eyes
four-eyes

Reputation: 12459

Updating Pie Chart with Specific colors not working as expected

Looking at this fiddle (http://jsfiddle.net/gs6rehnx/2106/), there are four arcs with four different colors. I expect the Pie Chart to have three colors after clicking the update button. However, there are still four arcs. I assue, the mapping of the specific colors to the values is not working properly? Or is something else not working?

const chart = {};
const duration = 750;
const width = 160;
const height = 160;

const min = Math.min(width, height);
const oRadius = min / 2 * 0.9;
const iRadius = min / 2.5 * 0.85;

const pie = d3
  .pie()
  .value(function(d) {
    return d.value;
  })
  .sort(null);

const arc = d3
  .arc()
  .outerRadius(oRadius)
  .innerRadius(iRadius);

function arcTween(a) {
  const i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
};

const labels = ['1', '2', '3', '4'];
const color = ["rgba(126,211,33,1)", "rgba(39,173,232,1)", "rgba(229,5,1,1)", "rgba(245,166,35,1)"];

const scale = d3.scaleOrdinal()
  .domain(labels)
  .range(color);

const create = function(data) {
  const svg = d3
    .select('.foo')
    .append('svg')
    .attr('class', 'pie')
    .attr('width', width)
    .attr('height', height)
    .attr('id', 'svgClass');

  svg
    .append('g')
    .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
    .attr('id', 'bar');

  draw(data);
}

const draw = function(data) {
  const path = d3.select('#bar')
    .selectAll('path')
    .data(pie(data))

  path
    .enter()
    .append('g')
    .append('path')
    .attr('d', arc)
    .attr('fill', (d, i) => {
      return scale(d.data.name)
    });

  path
    .transition()
    .duration(duration)
    .attrTween('d', function(d) {
      const interpolate = d3.interpolate({
        startAngle: 0,
        endAngle: 0
      }, d);
      return function(t) {
        return arc(interpolate(t));
      };
    });
};

const data = [{
  "name": "1",
  "value": 2
}, {
  "name": "2",
  "value": 1
}, {
  "name": "3",
  "value": 2
}, {
  "name": "4",
  "value": 1
}];

const newData = [{
  "name": "1",
  "value": 2
}, {
  "name": "2",
  "value": 1
}, {
  "name": "3",
  "value": 2
}];

function createPie() {
  create(data)
}

function updatePie() {
  draw(newData)
}
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
    
<button type="button" onclick="createPie()">Click Me First!</button>
<button type="button" onclick="updatePie()">Update Diagram!</button>
<div class='foo'></div>

Upvotes: 0

Views: 98

Answers (1)

Ian
Ian

Reputation: 34549

The reason this isn't working is because d3 has three main parts to a selection:

  • enter
  • update
  • exit

In your example above you're dealing with the enter, the update isn't quite right and the exit is completely missing. This is a really good article from the author discussing data joins.

Enter

The following grabs all the new data elements, and adds a new g element for each one.

path.enter().append('g')

Exit

The following which you're missing will take all the items in the DOM that are no longer represented in the data, and remove them.

path.exit().remove();

const chart = {};
const duration = 750;
const width = 160;
const height = 160;

const min = Math.min(width, height);
const oRadius = min / 2 * 0.9;
const iRadius = min / 2.5 * 0.85;

const pie = d3
  .pie()
  .value(function(d) {
    return d.value;
  })
  .sort(null);

const arc = d3
  .arc()
  .outerRadius(oRadius)
  .innerRadius(iRadius);

function arcTween(a) {
  const i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
};

const labels = ['1', '2', '3', '4'];
const color = ["rgba(126,211,33,1)", "rgba(39,173,232,1)", "rgba(229,5,1,1)", "rgba(245,166,35,1)"];

const scale = d3.scaleOrdinal()
  .domain(labels)
  .range(color);

const create = function(data) {
  const svg = d3
    .select('.foo')
    .append('svg')
    .attr('class', 'pie')
    .attr('width', width)
    .attr('height', height)
    .attr('id', 'svgClass');

  svg
    .append('g')
    .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
    .attr('id', 'bar');

  draw(data);
}

const draw = function(data) {
  const path = d3.select('#bar')
    .selectAll('path')
    .data(pie(data))

  path
    .enter()
    .append('g')
    .append('path')
    .attr('d', arc)
    .attr('fill', (d, i) => {
      return scale(d.data.name)
    });
    
  path.exit().remove();

  path
    .transition()
    .duration(duration)
    .attrTween('d', function(d) {
      const interpolate = d3.interpolate({
        startAngle: 0,
        endAngle: 0
      }, d);
      return function(t) {
        return arc(interpolate(t));
      };
    });
};

const data = [{
  "name": "1",
  "value": 2
}, {
  "name": "2",
  "value": 1
}, {
  "name": "3",
  "value": 2
}, {
  "name": "4",
  "value": 1
}];

const newData = [{
  "name": "1",
  "value": 2
}, {
  "name": "2",
  "value": 1
}, {
  "name": "3",
  "value": 2
}];

function createPie() {
  create(data)
}

function updatePie() {
  draw(newData)
}
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
    
<button type="button" onclick="createPie()">Click Me First!</button>
<button type="button" onclick="updatePie()">Update Diagram!</button>
<div class='foo'></div>

Upvotes: 1

Related Questions