Spacing out stacked bars in chart.js

Viewed 46

I am trying to do a stacked bar chart with only one bar in Chart.js using JS, CSS, and HTML. This is the chart I have constructed so far: enter image description here

And I am trying to make it look like the following:

enter image description here

I don't know how or if its possible in Chart.js to space out the bars like I did above and add the bracket labels below the chart. TYIA

var len_one = 0.02;
var len_two = 0.05;
var len_three = 0.72;
var len_four = 0.16;
var len_five = 0.05;

const ctx = document.getElementById("myChart").getContext('2d');
const myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: [0],
        datasets: [{
            label: 'red-low',
            data: [len_one],
            backgroundColor: ['#ab3025'],
            barThickness: 50,
            borderSkipped: false,
            borderRadius: {
                bottomLeft: 3,
                topLeft: 3,
                topRight: 3,
                bottomRight: 3
            },
        }, {
            label: 'rose-low', 
            data: [len_two],
            backgroundColor: ['#d59792'],
            barThickness: 50,
            borderSkipped: false,
            borderRadius: {
                bottomLeft: 3,
                topLeft: 3,
                topRight: 3,
                bottomRight: 3
            },
        }, {
            label: 'Target',
            data: [len_three],
            backgroundColor: ['#44c973'],
            barThickness: 50,
            borderSkipped: false,
            borderRadius: 3,
        }, {
            label: 'high-yellow',
            data: [len_four],
            backgroundColor: ['#f9c63d'],
            barThickness: 50, 
            borderSkipped: false,
            borderRadius: {
                bottomLeft: 3,
                topLeft: 3,
                topRight: 3,
                bottomRight: 3
            },
        }, {
            label: 'high-orange',
            data: [len_five],
            backgroundColor: ['#f5a000'],
            barThickness: 50,
            borderSkipped: false,
            borderRadius: {
                bottomLeft: 3,
                topLeft: 3,
                topRight: 3,
                bottomRight: 3
            },
        }]
    },
    options: {
        indexAxis: 'y',
        events: [], 
        maintainAspectRatio: false,
        layout: {
            padding: 20,
        },
        plugins: {
            legend: {
                display: false,
            }
        },
        scales: {
            x: {
                stacked: true,
                display: false,
            },
            y: {
                beginAtZero: true,
                stacked: true,
                display: false

            },
        },
    },

});
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <body>
        <!-- Chart that displays the time in ranges-->
        <div class="col-6 offset-3 my-5" id="time_in_range">
            <canvas id="myChart"></canvas>
        </div>
        
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
     
    </body>

</html>

2 Answers

Regarding adding spaces between the bars, I wasn't either able to find any direct configuration that allowed such behavior.

My first thought was to add borders and make them invisible, however you wouldn't see the effect of borderRadius.

Then, I come to another approach that interleaves the dataset object with a 'fake' data with transparent color, which has customizable length acting as the gap. With the use of flatMap, you can construct such interleaved array.

Also putting everything in the outer object, you can avoid redundant code.

 datasets: {
      bar: {
        barThickness: 50,
        borderSkipped: false,
        borderRadius: {
          topLeft: 3,
          topRight: 3,
          bottomLeft: 3,
          bottomRight: 3
        }
      }
    },

That corresponds to:

var len_one = 0.02;
var len_two = 0.05;
var len_three = 0.72;
var len_four = 0.16;
var len_five = 0.05;

// Change this to adjust gap size
var len_separator = 0.05

const separator = {
  label: 'sepataror',
  data: [len_separator],
  backgroundColor: ['transparent'],
}

let data = [{
    label: 'red-low',
    data: [len_one],
    backgroundColor: ['#ab3025']
  },
  {
    label: 'rose-low',
    data: [len_two],
    backgroundColor: ['#d59792'],
  }, {
    label: 'Target',
    data: [len_three],
    backgroundColor: ['#44c973'],
  }, {
    label: 'high-yellow',
    data: [len_four],
    backgroundColor: ['#f9c63d'],
  }, {
    label: 'high-orange',
    data: [len_five],
    backgroundColor: ['#f5a000'],
  }
].flatMap(x => [x, { ...separator}])

const ctx = document.getElementById("myChart").getContext('2d');
const myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: [0],
    datasets: data,
  },
  options: {
    datasets: {
      bar: {
        barThickness: 50,
        borderSkipped: false,
        borderRadius: {
          topLeft: 3,
          topRight: 3,
          bottomLeft: 3,
          bottomRight: 3
        }
      }
    },
    indexAxis: 'y',
    events: [],
    maintainAspectRatio: false,
    layout: {
      padding: 20
    },
    plugins: {
      legend: {
        display: false
      }
    },
    scales: {
      x: {
        stacked: true,
        display: false
      },
      y: {
        beginAtZero: true,
        stacked: true,
        display: false,
      },
    },
  },

});
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <!-- Chart that displays the time in ranges-->
  <div class="col-6 offset-3 my-5" id="time_in_range">
    <canvas id="myChart"></canvas>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>

</body>

</html>

About the grouped labels with brackets, I'm not sure it is possible without a plugin or something fancier.

The answer above was a correct solution to my problem, but that answer had been posted I figured out a different way to do it. I made 0.01 size bars in between each colored bar and made the color the same as the background of the webpage I was working on. That fixed my issue as well.

Related