How to display values in Stacked Multi-bar chart - nvd3 Graphs

后端 未结 1 1862
心在旅途
心在旅途 2020-12-18 06:54

I got a scenario where in I need to display value for every stack in stacked multi-bar chart - nvd3 graph as we can display value in discrete value - nvd3 graph.

I u

相关标签:
1条回答
  • 2020-12-18 07:43

    Currently isn't possible. That option is available only in the discrete bar chart.

    From the maintainer:

    We don't have this ability. Stacked/Grouped charts also have complex animations making this a tricky thing to solve. We use tooltips instead.

    Source https://github.com/novus/nvd3/issues/150

    If you want to do this you need to make your own implementation using d3. There is a good example here http://plnkr.co/edit/BNpAlFalKz0zkkSszHh1?p=preview. It's using the angular wrapper but it's a good starting point.

    var app = angular.module('app', ['nvd3ChartDirectives']);
    
    app.controller('chartCtrl', function($scope, $timeout) {
    
      var ANIMATION_TIME = 1500,
        countSeriesDisplayed = 2,
        promise,
        labels = ["label1", "label2", "label3", "label4", "label5"];
    
      $scope.isStacked = false;
    
      // Example data
      $scope.chartData = [{
        "key": "Series 1",
        "values": [
          [0, 10],
          [1, 20],
          [2, 30],
          [3, 40],
          [4, 50]
        ]
      }, {
        "key": "Series 2",
        "values": [
          [0, 10],
          [1, 40],
          [2, 60],
          [3, 20],
          [4, 40]
        ]
      }];
    
      /* To add labels, images, or other nodes on the created SVG, we need to wait
       *  for the chart to be rendered with a callback.
       *  Once the chart is rendered, a timeout is set to wait for the animation to
       *  finish.
       *
       *  Then, we need to find the position of the labels and set it with the
       *  transform attribute in SVG.
       *  To do so, we have to get the width and height of each bar/group of bar 
       *  which changes if stacked or not
       *
       */
    
      // Callback called when the chartData is assigned
      $scope.initLabels = function() {
        return function(graph) {
          promise = $timeout(function() {
            var svg = d3.select("svg"),
              lastRects, rectWidth,
              heightForXvalue = []; // Used for grouped mode
    
            // We get one positive rect of each serie from the svg (here the last serie)
            lastRects = svg.selectAll("g.nv-group").filter(
              function(d, i) {
                return i == countSeriesDisplayed - 1;
              }).selectAll("rect.positive");
    
            if ($scope.isStacked) {
              // If stacked, we get the width of one rect
              rectWidth = lastRects.filter(
                function(d, i) {
                  return i == countSeriesDisplayed - 1;
                }).attr("width");
            } else {
              // If grouped, we need to get the greatest height of each bar
              var nvGroups = svg.selectAll("g.nv-group").selectAll("rect.positive");
              nvGroups.each(
                function(d, i) {
                  // Get the Min height space for each group (Max height for each group)
                  var rectHeight = parseFloat(d3.select(this).attr("y"));
                  if (angular.isUndefined(heightForXvalue[i])) {
                    heightForXvalue[i] = rectHeight;
                  } else {
                    if (rectHeight < heightForXvalue[i]) {
                      heightForXvalue[i] = rectHeight;
                    }
                  }
                }
              );
    
              // We get the width of one rect multiplied by the number of series displayed
              rectWidth = lastRects.filter(
                function(d, i) {
                  return i == countSeriesDisplayed - 1;
                }).attr("width") * countSeriesDisplayed;
            }
    
            // We choose a width equals to 70% of the group width
            var labelWidth = rectWidth * 70 / 100;
    
            var groupLabels = svg.select("g.nv-barsWrap").append("g");
    
            lastRects.each(
              function(d, index) {
                var transformAttr = d3.select(this).attr("transform");
                var yPos = parseFloat(d3.select(this).attr("y"));
                groupLabels.append("text")
                  .attr("x", (rectWidth / 2) - (labelWidth /2)) // We center the label
                  // We add a padding of 5 above the highest rect
                  .attr("y", (angular.isUndefined(heightForXvalue[index]) ? yPos : heightForXvalue[index]) - 5)
                  // We set the text
                  .text(labels[index])
                  .attr("transform", transformAttr)
                  .attr("class", "bar-chart-label");
              });
    
          }, ANIMATION_TIME);
        }
      };
    
      // Tooltips
      $scope.toolTipContentFunction = function () {
        return function (key, x, y, e, graph) {
          return labels[x];
        }
    };
    
      $scope.$on('$destroy', function () {
        // Cancel timeout if still active
        $timeout.cancel(promise);
      });
    
    });
    

    UPDATE:

    I've created a gist that could help you to implement this by yourself.

    https://gist.github.com/topicus/217444acb4204f364e46

    0 讨论(0)
提交回复
热议问题