Search code examples
reactjschart.jsreact-chartjs-2

Issue with chartjs linear gradient for the mixed bar chart in ReactJS is not calculated for each individual Bars


Our requirement is to render the bar chart as Expected Image. we are using chartJs library to render the chart. we are able to render the bar chart but for one of the bar we need to show the background color as gradient. To achieve this we used the following below code snippet: but it render as Rendered graph. Can you please help me out in rendering the chart as expected. JSFiddleLink

const data = [{
  type: "Sample 1",
  data: [600, 400, 200, 800]
}, {
  type: "Sampel 2",
  data: [700, 300, 600, 600]
}, {
  type: "Total",
  data: [1300, 700, 800, 1400]
}];


  const gradient = document.getElementById('myChart').getContext('2d').createLinearGradient(0, 250, 0, 0);
  gradient.addColorStop(1, '#acd7fa')
  gradient.addColorStop(0.4, '#FFFFFF')

new Chart('myChart', {
  type: "bar",
  data: {
    labels: ["A", "B", "C", "D"],
    datasets: [
          {
            label: data[0].type,
            xAxisID: "x1",
            data: data[0].data,
            // fillColor: background_1,
            // highlightFill: background_2,
            backgroundColor: "rgb(54, 162, 235)" ,
            // backgroundColor: background_1,
            barPercentage: 1,
          },
          {
            label: data[1].type,
            xAxisID: "x1",
            data: data[1].data,
            backgroundColor:"rgb(255, 159, 64)",
            barPercentage: 1,
          },
          {
            label: data[2].type,
            xAxisID: "x2",
            data: data[2].data,
            backgroundColor: gradient,
            barPercentage: 1,
          },
        ],
      },
  options: {
    legend: {
      labels: {
        filter: item => item.text != 'Total'
      }
    },
    scales: {
      yAxes: [{
        ticks: {
          min: 0,
          stepSize: 200
        }
      }],
      xAxes: [{
          id: 'x1'
        },
        {
          id: 'x2',
          display: false,
          offset: true
        }
      ]
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="150"></canvas>


Solution

  • The Plugin Core API offers a range of hooks that may be used for performing custom code. You can use the afterLayout hook for creating different gradients for each value of the third dataset (the totals).

    Please take a look at your amended code below and see how it works. You may also consult this StackBlitz that illustrates how it can be done with react-chartjs-2.

    const data = [
      { type: "Sample 1", data: [600, 400, 200, 800] }, 
      { type: "Sampel 2", data: [700, 300, 600, 600] }, 
      { type: "Total", data: [1300, 700, 800, 1400] }
    ];
    
    new Chart('myChart', {
      type: "bar",
      plugins: [{
        afterLayout: chart => {
          let ctx = chart.chart.ctx;
          ctx.save();
          let yAxis = chart.scales["y-axis-0"];
          let yBottom = yAxis.getPixelForValue(0);
          let dataset = chart.data.datasets[2];
          dataset.backgroundColor = dataset.data.map(v => {
            let yTop = yAxis.getPixelForValue(v);
            let gradient = ctx.createLinearGradient(0, yBottom, 0, yTop);
            gradient.addColorStop(0.4, '#FFFFFF');
            gradient.addColorStop(1, '#acd7fa');
            return gradient;
          });
          ctx.restore();
        }
      }],
      data: {
        labels: ["A", "B", "C", "D"],
        datasets: [{
            label: data[0].type,
            xAxisID: "x1",
            data: data[0].data,
            backgroundColor: "rgb(54, 162, 235)",
            barPercentage: 1
          },
          {
            label: data[1].type,
            xAxisID: "x1",
            data: data[1].data,
            backgroundColor: "rgb(255, 159, 64)",
            barPercentage: 1
          },
          {
            label: data[2].type,
            xAxisID: "x2",
            data: data[2].data,
            barPercentage: 1
          }
        ]
      },
      options: {
        legend: {
          labels: {
            filter: item => item.text != 'Total'
          }
        },
        scales: {
          yAxes: [{
            ticks: {
              min: 0,
              stepSize: 200
            }
          }],
          xAxes: [{
              id: 'x1'
            },
            {
              id: 'x2',
              display: false,
              offset: true
            }
          ]
        }
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
    <canvas id="myChart" height="150"></canvas>