D3 vertical line beetween grouped chart bars spacing

一曲冷凌霜 提交于 2020-01-14 05:15:07

问题


The problem:

The number of groups is dynamic, and vertical line (separator) needs dynamic padding. Group width im getting with x0.rangeBand(). Is there any way to get width of space beetween two groups dynamically?

Peace of code:

.....

    var slice = svg.selectAll(".chart")
        .data(data)
        .enter().append("g")
        .attr("class", "g")
        .attr("transform", function (d) {
            return "translate(" + x0(d.category) + ",0)";
        });



    // Create rectangles of the correct width
    slice.selectAll("rect")
        .data(function (d) {
            return d.values;
        })
        .enter().append("rect")
        .attr("width", x1.rangeBand())
        .attr("x", function (d) {
            return x1(d.rate);
        })
        .style("fill", function (d) {
            return color(d.rate)
        })
        .attr("y", function (d) {
            return y(0);
        })
        .attr("height", function (d) {
            return height - y(0);
        })
        .on("mouseover", function (d) {
            d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
            tip.show(d);

        })
        .on("mouseout", function (d) {
            tip.hide
            d3.select(this).style("fill", color(d.rate));
        })

    slice.append("line")
        .attr("class", "blabla")
        .attr("x1", x0.rangeBand()+20)
        .attr("x2", x0.rangeBand()+20)
        .attr("y1", 0)
        .attr("y2", height + margin.top + margin.bottom)
        .style("stroke-width", 1)
        .style("stroke", "#000");
.....

This is how it looks with few groups

This is how it looks with many groups


回答1:


Because I see no reason why to stick to d3v3 and I can't find the d3v3 documentation easily for ordinal scales here is a d3v5 version of the code with correct placement of the vertical bars. You have to use the bandwidth and the step to calculate the position.

It is an adaptation of the example in your other question : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad

I doubled the number of groups and it looks nice.

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x0 = d3.scaleBand()
           .rangeRound([0, width])
           .paddingInner(0.1);

var x1 = d3.scaleBand();

var y = d3.scaleLinear()
    .range([height, 0]);

var xAxis = d3.axisBottom()
    .scale(x0)
    .tickSize(0);

var yAxis = d3.axisLeft()
    .scale(y);

var color = d3.scaleOrdinal()
    .range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);

var svg = d3.select('body').append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.json("barchart.json", {credentials: 'same-origin'}).then(function(data) {

  var categoriesNames = data.map(function(d) { return d.categorie; });
  var rateNames = data[0].values.map(function(d) { return d.rate; });

  x0.domain(categoriesNames);
  x1.domain(rateNames).range([0, x0.bandwidth()]);
  y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .style('opacity','0')
      .call(yAxis)
  .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .style('font-weight','bold')
      .text("Value");

  svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');

  var slice = svg.selectAll(".slice")
      .data(data)
      .enter().append("g")
      .attr("class", "g")
      .attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });

  slice.selectAll("rect")
      .data(function(d) { return d.values; })
  .enter().append("rect")
      .attr("width", x1.bandwidth())
      .attr("x", function(d) { return x1(d.rate); })
      .style("fill", function(d) { return color(d.rate) })
      .attr("y", function(d) { return y(0); })
      .attr("height", function(d) { return height - y(0); })
      .on("mouseover", function(d) {
          d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
      })
      .on("mouseout", function(d) {
          d3.select(this).style("fill", color(d.rate));
      });

  slice.selectAll("rect")
      .transition()
      .delay(function (d) {return Math.random()*1000;})
      .duration(1000)
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); });

slice.append("line")
        .attr("class", "blabla")
        .attr("x1", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
        .attr("x2", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
        .attr("y1", 0)
        .attr("y2", height + margin.top + margin.bottom)
        .style("stroke-width", 1)
        .style("stroke", "#000");

  //Legend
  var legend = svg.selectAll(".legend")
      .data(data[0].values.map(function(d) { return d.rate; }).reverse())
  .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
      .style("opacity","0");

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

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) {return d; });

  legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");

});



回答2:


So the trick is to recalculate padding self. V3 does not have band.step() function so I added this method to get the middle of two items:

function getMiddle(x0){

    var rng = x0.range();
    var band = x0.rangeBand();
    var padding = 0;

    if(rng.length>1){
        padding = (rng[1]-rng[0] - band) *0.5;
    }
    return band + padding;
}

And usage:

    slice.append("line")
        .attr("x1", getMiddle(x0))
        .attr("x2", getMiddle(x0))
        .attr("y1", 0)
        .attr("y2", height + margin.top + margin.bottom)
        .style("stroke-width", 1)
        .style("stroke", "#000");


来源:https://stackoverflow.com/questions/51570854/d3-vertical-line-beetween-grouped-chart-bars-spacing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!