Naty Bizz
Naty Bizz

Reputation: 2342

ChartJS - Show values in the center of each bar

See image below, I'm trying to set the value of each bar in the center of my stacked bar; so far I only got on the top and sometimes the position is off (see the 4% yellow in the third bar)

This is the code:

context.data.datasets.forEach(function (dataset) {
    for (var i = 0; i < dataset.data.length; i++) {
        var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
            scale_max = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._yScale.maxHeight;
        var textY = model.y + 50;
        if ((scale_max - model.y) / scale_max >= 0.5)
            textY = model.y + 20;
        fadeIn(ctx, dataset.data[i], model.x, textY, model.y > topThreshold, step);
    }
});

var fadeIn = function(ctx, obj, x, y, black, step) {
    var ctx = modifyCtx(ctx);
    var alpha = 0;
    ctx.fillStyle = black ? 'rgba(' + outsideFontColor + ',' + step + ')' : 'rgba(' + insideFontColor + ',' + step + ')';
    ctx.fillText(obj.toString() + "%", x, y);
};

enter image description here

Upvotes: 1

Views: 1055

Answers (1)

uminder
uminder

Reputation: 26150

This can be done with the Plugin Core API. The API offers different hooks that may be used for executing custom code (that's probably what you already do). In your case, you can use the afterDraw hook as follows to draw text at the desired positions.

afterDraw: chart => {
  let ctx = chart.chart.ctx;
  ctx.save();
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = "12px Arial";
  let xAxis = chart.scales['x-axis-0'];
  let yAxis = chart.scales['y-axis-0'];
  let datasets = chart.chart.data.datasets.filter(ds => !ds._meta[0].hidden);
  xAxis.ticks.forEach((value, xIndex) => {
    let x = xAxis.getPixelForTick(xIndex);
    datasets.forEach((dataset, iDataset) => {
      if (dataset.data[xIndex] > 3) {
        let yValue = datasets.slice(0, iDataset)
          .map(ds => ds.data[xIndex])
          .reduce((a, b) => a + b, 0) +
          dataset.data[xIndex] / 2;
        let y = yAxis.getPixelForValue(yValue);
        ctx.fillStyle = dataset.textColor;
        ctx.fillText(dataset.data[xIndex] + '%', x, y);
      }
    });
  });
  ctx.restore();
}

Please take a look at below runnable code and see how it works.

const chart = new Chart('myChart', {
  type: 'bar',
  plugins: [{
    afterDraw: chart => {
      let ctx = chart.chart.ctx;
      ctx.save();
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.font = "12px Arial";
      let xAxis = chart.scales['x-axis-0'];
      let yAxis = chart.scales['y-axis-0'];
      let datasets = chart.chart.data.datasets.filter(ds => !ds._meta[0].hidden);
      xAxis.ticks.forEach((value, xIndex) => {
        let x = xAxis.getPixelForTick(xIndex);
        datasets.forEach((dataset, iDataset) => {
          if (dataset.data[xIndex] > 3) {
            let yValue = datasets.slice(0, iDataset)
              .map(ds => ds.data[xIndex])
              .reduce((a, b) => a + b, 0) +
              dataset.data[xIndex] / 2;
            let y = yAxis.getPixelForValue(yValue);
            ctx.fillStyle = dataset.textColor;
            ctx.fillText(dataset.data[xIndex] + '%', x, y);
          }
        });
      });
      ctx.restore();
    }
  }],
  data: {
    labels: ['A', 'B', 'C', 'D', 'E'],
    datasets: [{
      label: 'Dataset 1',
      data: [2.5, 48, 9, 17, 23],
      backgroundColor: 'red',
      textColor: 'white'
    }, {
      label: 'Dataset 2',
      data: [2.5, 4, 4, 11, 11],
      backgroundColor: 'orange',
      textColor: 'black'
    }, {
      label: 'Dataset 3',
      data: [95, 48, 87, 72, 66],
      backgroundColor: 'green',
      textColor: 'white'
    }]
  },
  options: {
    scales: {
      xAxes: [{
        stacked: true
      }],
      yAxes: [{
        stacked: true,
        ticks: {
          beginAtZero: true
        }
      }]
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="160"></canvas>

Upvotes: 1

Related Questions