向D3瀑布图添加多个小计

时间:2018-08-01 09:37:23

标签: javascript d3.js

我正在使用d3js构建瀑布图,但主要要求是在图表中具有多个小计,类似于下图所示。

Image for reffrence

但是目前我正在使用d3js的图表在图表末尾只有一个总值,下面将介绍我正在处理的代码。

D3.html

<html>
<head>
    <title></title>
    <meta charset="utf-8" />
</head>
<body>
    <svg class="chart"></svg>
</body>
</html>

D3.css

.bar.total rect {
  fill: blue;
}

.bar:hover rect{
    fill:orange;
}

.bar.positive rect {
  fill: green;
}
.bar:hover rect{
    fill:orange;
}


.bar.negative rect {
  fill: red;
}
.bar:hover rect{
    fill:orange;
}


.bar line.connector {
  stroke: grey;
  stroke-dasharray: 3;
}

.axis text {
  font: 12px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}


div.tooltip {   
    color:white;
    position:absolute;
    text-align: center;                         
    padding: 1.2px;             
    font: 11px sans-serif;      
    background: black;  
    border: 0px;        
    border-radius: 8px;         
    pointer-events: none;   
    width: 60px;
}

D3.js

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

var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], padding);

var y = d3.scale.linear()
   // .range([height, 0]);
 .range([height - padding, padding]); 

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");


var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(function(d) { return dollarFormatter(d); })

    // .tickFormat(formatAxis)

//tooltip
  var tooltip = d3.select("body").append("div") 
    .attr("class", "tooltip")               
    .style("opacity", 0)
     .attr("align","middle");

//whole chart
var chart = d3.select(".chart")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");




data = [
  {"name":"List Price","value":120},
  {"name":"County Adj","value":-13},
  {"name":"Country List Price","value":-20}, 
  {"name":"Std Discount","value":-20},
  {"name":"Promotion","value":7},
  {"name":"Volume Discount","value":-10},
  {"name":"Freight Surcharge","value":6},
  {"name":"Sales Discount","value":5},
  {"name":"Invoice Price","value":9}, 
  {"name":"Cash Discount","value":-2},
  {"name":"Customer Rebate","value":-14},
  {"name":"Buying Group Rebate","value":4},
  {"name":"Net Price","value":-12},
  {"name":"Bonus","value":13},
  {"name":"Cost to Serve","value":-9}, 
  {"name":"Pocket Price","value":-12},
  {"name":"Production Cost","value":-12},
  {"name":"Net Margin","value":-18}

    ];

//function to find all the positive values
var positive_val = data.filter(function(d) { return d.value > 0; });
console.log(JSON.stringify(positive_val));

//function to calculate the sum of all the positive values
var maxSum = positive_val.reduce(function(sum, d) {
   return sum + d.value;
}, 0);
console.log("The maximum sum is "+maxSum);

//to calculate the new Domain by adding 120 
var yaxisRange=maxSum+120;
console.log("The y axis sum is "+yaxisRange);

var newDomain=dollarFormatter(yaxisRange);
console.log(newDomain);
var newDomain = newDomain.replace(/[!@#$%^&*]/g, ",");
console.log(newDomain);

  // Transform data (i.e., finding cumulative values and total) for easier charting
  var cumulative = 0;
  for (var i = 0; i < data.length; i++) {
    data[i].start = cumulative;
    cumulative += data[i].value;
    data[i].end = cumulative;

    data[i].class = ( data[i].value >= 0 ) ? 'positive' : 'negative'
  }
  data.push({
    name: 'Total',
    end: cumulative,
    start: 0,
    class: 'total',
    value: cumulative
  });

  x.domain(data.map(function(d) { return d.name; }));
 y.domain([0, d3.max(data, function(d) { return d.end; })]);

  //var y_domain = ([0, d3.max(data, function(d) { return d.end; })]);
// y.domain( y_domain);

  chart.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")

      .call(xAxis)
      .selectAll("text")
    .attr("y", 6)
    .attr("x", 9)
    .attr("dy", ".35em")
    .attr("transform", "rotate(50)")
    .style("text-anchor", "start");

  chart.append("g")
      .attr("class", "y axis")
      .call(yAxis);

  var bar = chart.selectAll(".bar")
      .data(data)
    .enter().append("g")
      .attr("class", function(d) { return "bar " + d.class })
      .attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });

  bar.append("rect")
       //.attr("y", function(d) { return y(d.value); })
      .attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
      .attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) );  })
      //function to draw the tooltip
      .attr("width", x.rangeBand()).on("mouseover", function(d) {
     // to find the parent node,to calculate the x position
     var parentG = d3.select(this.parentNode);
     var barPos = parseFloat(parentG.attr('transform').split("(")[1]);
     var xPosition = barPos+x.rangeBand()/2;
      //to find the y position
     var yPosition = parseFloat(d3.select(this).attr("y"))+ Math.abs( y(d.start) - y(d.end))/2;
           tooltip.transition()     
                .duration(200)      
                .style("opacity", .9);  
          tooltip.html(d.name + "<br/>"  + (d.value))   
                .style("left", xPosition + "px")        
                .style("top",  yPosition + "px");   
            }).on("mouseout", function(d) {     
            tooltip.transition()        
                .duration(500)      
                .style("opacity", 0);   
        });

  // bar.append("text")
  //     .attr("x", x.rangeBand() / 2)
  //     .attr("y", function(d) { return y(d.end) + 5; })
  //     .attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + ".75em" })
  //     .text(function(d) { return dollarFormatter(d.end - d.start);});


  bar.filter(function(d) { return d.class != "total" }).append("line")
      .attr("class", "connector")
      .attr("x1", x.rangeBand() + 5 )
      .attr("y1", function(d) { return y(d.end) } )
      .attr("x2", x.rangeBand() / ( 1 - padding) - 5 )
      .attr("y2", function(d) { return y(d.end) } )


function type(d) {
  d.value = +d.value;
  return d;
}

function dollarFormatter(n) {
  n = Math.round(n);
  var result = n;
  if (Math.abs(n) > 1000) {
    result = Math.round(n/1000) + 'M';
  }
  return '$' + result + 'M';
}

如果需要有关上述代码的更多信息,请在下面找到我目前正在使用的Codepen链接。 https://codepen.io/hari2609/pen/oMxVJZ?editors=0010

0 个答案:

没有答案