简体   繁体   中英

d3js grouped bar chart toggling legend

I am using d3.js to render a grouped bar chart and I am looking to animate transition the bars - (show/hide different series) when clicking on the legend.

from this.

在此处输入图片说明 to this

在此处输入图片说明

perhaps also changing the scale

http://jsfiddle.net/0ht35rpb/202/

legend.append("rect")
  .attr("x", width - 18)
  .attr("width", 18)
  .attr("height", 18)
  .style("fill", color)
  .on("click", function(d) {
    console.log("d", d);
  });

Some bar transition code

  bars.transition()
      .attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
      .attr("x", function(d) { return x(d.state); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) {return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); });

    bars.exit().remove();

Other grouped bar chart references.

https://bl.ocks.org/mbostock/3887051

https://plnkr.co/edit/JUaLXmeCvHh0zUmrKClQ?p=preview

http://jsfiddle.net/ramseyfeng/8790t2vk/

There are a few ways to go through this. You could easily use an enter/update/exit cycle, though this is a little complex when compared to typical use of the cycle because of the nested elements and the need to set keys to ensure smooth transitions between chart states.

In this situation, it may be easier to simply use an array to hold bars that are to be filtered out, hide those bars, update the scales to not use those keys' values, and update the remaining bars.

This requires an onclick event for each legend item. When clicked, in our clicked function we manage the array of filtered out ( filtered ) items like so, where d is the datum associated with the legend rectangle:

// add the clicked key if not included:
if (filtered.indexOf(d) == -1) {
 filtered.push(d); 
  // if all bars are un-checked, reset:
  if(filtered.length == keys.length) filtered = [];
}
// otherwise remove it:
else {
  filtered.splice(filtered.indexOf(d), 1);
}

Then we can update the scales (we need the all the keys that are not in the filtered array for the domain of the x1 scale, hence the newKeys variable):

var newKeys = [];
    keys.forEach(function(d) {
      if (filtered.indexOf(d) == -1 ) {
        newKeys.push(d);
      }
    })
    x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
    y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { if (filtered.indexOf(key) == -1) return d[key]; }); })]).nice();

Then we can select our rectangles, filter by whether they should be hidden or shown, and update accordingly:

var bars = svg.selectAll(".bar").selectAll("rect")
  .data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })

// filter out bars:
bars.filter(function(d) {
  return filtered.indexOf(d.key) > -1;
})
.transition()
.attr("x", function(d) {
  return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width"))/2;  
})
.attr("height",0)
.attr("width",0)     
.attr("y", function(d) { return height; })
.duration(500);

// update persistent bars:
bars.filter(function(d) {
    return filtered.indexOf(d.key) == -1;
  })
  .transition()
  .attr("x", function(d) { return x1(d.key); })
  .attr("y", function(d) { return y(d.value); })
  .attr("height", function(d) { return height - y(d.value); })
  .attr("width", x1.bandwidth())
  .attr("fill", function(d) { return z(d.key); })
  .duration(500);

This solution could be made a little bit more "d3-ish" with the enter/update/exit cycle, but as our elements are relatively fixed in number, this is not as useful as in many other situations.

Here is the above code in action:

https://bl.ocks.org/andrew-reid/64a6c1892d1893009d2b99b8abee75a7

And as noted in the comments, you also need to update the axis, not just the scale. To do so, I added a class to the y scale to allow easy selection when updating the chart:

       svg.select(".y")
        .transition()
        .call(d3.axisLeft(y).ticks(null, "s"))
        .duration(500);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM