D3条形图在所有负值和正值情况下都不能正常工作

D3 bar chart not working properly with all negative and positive values

本文关键字:常工作 都不能 工作 情况下 条形图 D3      更新时间:2023-09-26

我需要在d3的帮助下创建具有正值和负值的条形图。图表可以很好地结合正值和负值,但如果我只通过负值或正值,X轴就不在正确的位置。感谢

Jsfidle

条形图图像

//var data = [["Since Mar 10, 2015",150], ["1 year",-100], ["3 year",6.9], ["Since Mar 10, 2010",100]];
var data = [["Since Mar 10, 2015",-100], ["1 year",-10]];
d3.select("#example")
.datum(data)
.call(columnChart()
  .width(320)
  .height(240)
  .x(function(d, i) { return d[0]; })
  .y(function(d, i) { return d[1]; }));
function columnChart() {
  var margin = {top: 30, right: 10, bottom: 50, left: 50},
  width = 20,
  height = 20,
  xRoundBands = 0.6,
  xValue = function(d) { return d[0]; },
  yValue = function(d) { return d[1]; },
  xScale = d3.scale.ordinal(),
  yScale = d3.scale.linear(),
  yAxis = d3.svg.axis().scale(yScale).orient("left"),
  xAxis = d3.svg.axis().scale(xScale);
  var isNegative = false;    
 function chart(selection) {
   selection.each(function(data) {
   // Convert data to standard representation greedily;
   // this is needed for nondeterministic accessors.
  for(var i=0; i< data.length; i++){
     if(data[i][1] < 0){
       isNegative = true;
    }
  }  
  data = data.map(function(d, i) {
    return [xValue.call(data, d, i), yValue.call(data, d, i)];
  });
  // Update the x-scale.
  xScale
      .domain(data.map(function(d) { return d[0];} ))
      .rangeRoundBands([0, width - margin.left - margin.right], xRoundBands);
  // Update the y-scale.
  if( data.length < 2 ){
     if( data.map(function(d) { return d[1];} ) < 0){
         yScale
          .domain([data.map(function(d) { return d[1];} ), 0])
          .range([height - margin.top - margin.bottom, 0])
          .nice();         
     }else{
         yScale
          .domain([0, data.map(function(d) { return d[1];} )])
          .range([height - margin.top - margin.bottom, 0])
          .nice();
     }
  }else{
      yScale
          .domain(d3.extent(data.map(function(d) { return d[1];} )))
          .range([height - margin.top - margin.bottom, 0])
          .nice();
  }
    // Select the svg element, if it exists.
  var svg = d3.select(this).selectAll("svg").data([data]);
  // Otherwise, create the skeletal chart.
  var gEnter = svg.enter().append("svg").append("g");
  gEnter.append("g").attr("class", "y axis");
  gEnter.append("g").attr("class", "x axis");
  gEnter.append("g").attr("class", "x axis zero");
  // Update the outer dimensions.
  svg .attr("width", width)
      .attr("height", height);
  // Update the inner dimensions.
  var g = svg.select("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    // Update the y-axis.
     g.select(".y.axis")
        .call(yAxis.tickSize(0).ticks(5))
        .selectAll("g")
        .selectAll("text")
        .text(function(d){
            return d+"%";
        });
    g.append("g")
        .selectAll("line.line")
        .data(yScale.ticks(5))
        .enter()
        .append("line")
         .attr(
            {
                "class":"line grid tick",
                "x1" : 0,
                "x2" : ( width - 60 ),
                "y1" : function(d){ return yScale(d);},
                "y2" : function(d){ return yScale(d);},
            });
    gEnter.append("g").attr("class", "bars");
    // Update the bars.
    var bar = g.select(".bars")
    .selectAll(".bar")
    .data(data);
    bar.enter().append("rect");
    bar.exit().remove();
    bar .attr("class", function(d, i) { return d[1] < 0 ? "bar negative" :   "bar positive"; })
      .attr("x", function(d) { return X(d); })
      .attr("y", function(d, i) { return d[1] < 0 ? Y0() : Y(d); })
      .attr("width", xScale.rangeBand())
      .attr("height", function(d, i) { return Math.abs( Y(d) - Y0() ); });
  // x axis at the bottom of the chart
  if( isNegative === true ){
    var xScaleHeight = height - margin.top - margin.bottom+12;
  }else{
    var xScaleHeight = height - margin.top - margin.bottom;
 }        
  g.select(".x.axis")
    .attr("transform", "translate(0," + ( xScaleHeight ) + ")")
    .call(xAxis.orient("bottom"))
    .selectAll("text")
    .call(wrap, xScale.rangeBand());
  // zero line
  g.select(".x.axis.zero")
    .attr("transform", "translate(0," + Y0() + ")")
    .attr("class", "zero axis")
    .call(xAxis.tickFormat("").tickSize(0));

   // Update the text in bars.
   var bar1 = svg.select(".bars").selectAll("text").data(data);
   bar1 .data(data)
       .enter()
       .append("text")
       .attr("class", "text")
       .text(function(d) { return d[1]+"%"; })
       .attr("x", function(d) { return X(d); })
       .attr("y", function(d, i) { return d[1] < 0 ? Math.abs(Y(d)+10) : Y(d)-2; });
});

}

  // Custom function for text wrap
   function wrap(text, width) {
     text.each(function() {
     var text = d3.select(this),
     words = text.text().split(/'s+/).reverse(),
     word,
     line = [],
     lineNumber = 0,
     lineHeight = 1, // ems
     y = text.attr("y"),
     dy = parseFloat(text.attr("dy")),
     tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
   while (word = words.pop()) {
   line.push(word);
   tspan.text(line.join(" "));
   if (tspan.node().getComputedTextLength() > 55) {
     line.pop();
     tspan.text(line.join(" "));
     line = [word];
     tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
   }
 }

});}

 // The x-accessor for the path generator; xScale ∘ xValue.
  function X(d) {
  return xScale(d[0]);
 }
 function Y0() {
  return yScale(0);
 }
 // The x-accessor for the path generator; yScale ∘ yValue.
 function Y(d) {
  return yScale(d[1]);
 }
 chart.margin = function(_) {
  if (!arguments.length) return margin;
   margin = _;
   return chart;
  };
 chart.width = function(_) {
   if (!arguments.length) return width;
    width = _;
    return chart;
  };
  chart.height = function(_) {
    if (!arguments.length) return height;
     height = _;
     return chart;
   };
  chart.x = function(_) {
   if (!arguments.length) return xValue;
    xValue = _;
    return chart;
   };
   chart.y = function(_) {
    if (!arguments.length) return yValue;
      yValue = _;
      return chart;
    };
   return chart;
}

我稍微修改了您的示例http://jsfiddle.net/fjuak9bo/9/我相信它提供了您想要的解决方案。

重要的部分是在yscale上适当地设置domain。在这里,我检测所有值是+ve还是-ve,并将域设置在-100-0或0-100之间。

yScale.domain([-100, 100])
          .range([height - margin.top - margin.bottom, 0])
          .nice();         
    if(min > 0) { 
        // All positive
        yScale.domain([0, 100]);
    } else if(max < 0) {
        // All negative
        yScale.domain([-100, 0]);   
    }

如果你的值超过+-100%,你可以在域上扩展最小值/最大值。