Search code examples
chartsgoogle-visualizationchart.jsstacked-chartcolumn-chart

Chart.js how to display multiple labels on multi bar stacked chart


How can i display different labels under each column and also have another label for the entire group?

As you can see in the picture below i want to use fontawesome icons for each column but another label for the main group. I found other answers how to use fa icons but don't know how to position them under each bar.

The trendlines which connect distinct columns are not so important but would be great if i can find out how to add them also.

Also the chart needs to be scrollable as it can hold lots of data and the labels need to be shown. I found some examples with scroll as well.

Any info is highly appreciated. Or are there any other open source chart frameworks in which i could implement this or something similar to fit my needs?

Chart


Solution

  • using google charts...

    on the chart's 'ready' event,
    you can use chart method --> getChartLayoutInterface()

    var chartLayout = chart.getChartLayoutInterface();
    

    the interface has a method --> getBoundingBox()
    which will return the position of requested chart element
    to get the position of a bar...

    var barBounds = chartLayout.getBoundingBox('bar#0#0');
    

    where the first #0 is the series, and the second is the row,
    'bar#0#0' would get the first bar on the first row

    we can also get the position of the axis label...

    var labelBounds = chartLayout.getBoundingBox('hAxis#0#label#0');
    

    we can use a combination of the bar and label bounds to position the icon
    we want the left position from the bar, and the top position from the label

    see following working snippet,
    a column property is used to store the icon name,
    the x-axis labels are used for the group
    once the icon is in position, the axis label is moved down to make room

    google.charts.load('current', {
      packages: ['corechart']
    }).then(function () {
      var data = new google.visualization.DataTable({
        cols: [
          {label: 'x', type: 'string'},
          {label: 'file', type: 'number', p: {icon: 'fa-file'}},
          {label: 'database', type: 'number', p: {icon: 'fa-database'}},
          {label: 'random', type: 'number', p: {icon: 'fa-random'}},
        ],
        rows: [
          {c:[{v: 'Label 1'}, {v: 11}, {v: 6}, {v: 15}]},
          {c:[{v: 'Label 2'}, {v: 8}, {v: null}, {v: 12}]},
          {c:[{v: 'Label 3'}, {v: 6}, {v: 8}, {v: 18}]},
          {c:[{v: 'Label 4'}, {v: null}, {v: 1}, {v: 16}]},
        ]
      });
    
      var options = {
        bar: {
          groupWidth: '50%',
          width: 20
        },
        colors: ['#ffc107', '#d32f2f', '#00bcd4'],
        height: 600,
        legend: 'none',
        title: 'Capacities',
        width: 1000,
      };
    
      var container = document.getElementById('chart_div');
      var chart = new google.visualization.ColumnChart(container);
    
      google.visualization.events.addListener(chart, 'ready', function () {
        // initialize bounds variables
        var axisLabels = container.getElementsByTagName('text');
        var chartLayout = chart.getChartLayoutInterface();
        var chartBounds = chartLayout.getChartAreaBoundingBox();
        var containerBounds = container.getBoundingClientRect();
        var labelIndex;
    
        // add icons
        for (var r = 0; r < data.getNumberOfRows(); r++) {
          var barBounds;
          var icon;
          var iconBounds;
          var labelBounds = chartLayout.getBoundingBox('hAxis#0#label#' + r);
          for (var c = 1; c < data.getNumberOfColumns(); c++) {
            barBounds = chartLayout.getBoundingBox('bar#' + (c - 1) + '#' + r);
            if (barBounds !== null) {
              icon = container.appendChild(document.createElement('i'));
              icon.className = 'fa ' + data.getColumnProperty(c, 'icon');
              icon.style.position = 'absolute';
              iconBounds = icon.getBoundingClientRect();
              icon.style.top = (containerBounds.top + labelBounds.top) + 'px';
              icon.style.left = (barBounds.left + containerBounds.left + (barBounds.width / 2) - (iconBounds.width / 2)) + 'px';
            }
          }
    
          // move axis label down
          labelIndex = -1;
          Array.prototype.forEach.call(axisLabels, function(label) {
            if (label.getAttribute('text-anchor') === 'middle') {
              labelIndex++;
              if (labelIndex === r) {
                label.setAttribute('y', (parseFloat(label.getAttribute('y')) + (iconBounds.height * 2)));
              }
            }
          });
        }
      });
    
      chart.draw(data, options);
    });
    i {
      color: #00bcd4;
    }
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <div id="chart_div"></div>