Can I offset the bars in the chart.js stacked bar chart like so:
Asked
Active
Viewed 2,547 times
2

Rolandas Burbulis
- 165
- 2
- 6
-
1As far as I know: No, you can't.. the ones in their [samples' page](https://www.chartjs.org/samples/latest/) are the only thing you can do – Soul Eater Dec 31 '19 at 16:38
2 Answers
3
Based on this answer, I created a runnable code snippet that illustrates how it could be done. I'm not using stacked bars because the use case is not clear to me and the image from the question rather looks like a bar with shadows.
const dataset = [40, 80, 50, 60, 70];
const offset = 8;
Chart.pluginService.register({
afterUpdate: function(chart) {
var dataset = chart.config.data.datasets[1];
for (var i = 0; i < dataset._meta[0].data.length; i++) {
var model = dataset._meta[0].data[i]._model;
model.x += offset;
model.controlPointNextX += offset;
model.controlPointPreviousX += offset;
}
}
});
var data = {
labels: ["A", "B", "C", "D", "E"],
datasets: [{
backgroundColor: [
'rgba(255, 99, 132)',
'rgba(255, 206, 86)',
'rgba(54, 162, 235)',
'rgba(75, 192, 192)',
'rgba(153, 102, 255)'
],
borderWidth: 1,
data: dataset,
xAxisID: "bar-x-axis1",
categoryPercentage: 0.5,
barPercentage: 0.5,
},
{
backgroundColor: 'rgba(0, 0, 0, 0.2)',
data: dataset.map(v => v + offset),
xAxisID: "bar-x-axis2",
categoryPercentage: 0.5,
barPercentage: 0.5
}
]
};
var options = {
legend: {
display: false
},
tooltips: {
enabled: false
},
scales: {
xAxes: [
{
id: "bar-x-axis2"
},
{
id: "bar-x-axis1",
offset: true,
ticks: {
display: false
}
}
],
yAxes: [{
id: "bar-y-axis1",
ticks: {
beginAtZero: true,
stepSize: 50
}
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="60"></canvas>

uminder
- 23,831
- 5
- 37
- 72
-
wow, thanks. I already implemented this functionality without chart.js just using plain html using divs to create rectangles to representing the bars. – Rolandas Burbulis Jan 17 '20 at 15:18
-
@uminder It seems like _,meta has been removed. Any chance you have a new solution for chart.js v3? – Arib Mar 09 '22 at 07:05
-
1@Arib: I posted another answer that illustrates how to solve the problem with Chart.js v3: https://stackoverflow.com/a/71410608/2358409 – uminder Mar 09 '22 at 14:06
1
For Chart.js v3, you can use a different approach.
- In the
beforeDatasetsDraw
, you save the define the shadow properties on the canvas through CanvasRenderingContext2D. Make sure to first save the state of the rendering context by invokingctx.save()
. - In the
afterDatasetsDraw
hook, you need to restore the state of the rendering context by invokingctx.restore()
. - Convert the bars into floating bars through
data: data.map(v => [-20, v])
to makes sure, the shadows appear from the base of the bars. - Define a
tooltip.callbacks.label
function that provides the initial values in the tooltips.
Please take a look at below runnable code and see how it works.
const data = [40, 80, 50, 60, 70];
const offset = 10;
new Chart('myChart', {
type: 'bar',
plugins: [{
beforeDatasetsDraw: chart => {
const ctx = chart.ctx;
ctx.save();
ctx.shadowOffsetX = offset;
ctx.shadowOffsetY = -offset;
ctx.shadowBlur = 5;
ctx.shadowColor = 'rgb(220, 220, 220)';
},
afterDatasetsDraw: chart => chart.ctx.restore()
}],
data: {
labels: ["A", "B", "C", "D", "E"],
datasets: [{
label: 'My Data',
data: data.map(v => [-20, v]),
backgroundColor: [
'rgba(255, 99, 132)',
'rgba(255, 206, 86)',
'rgba(54, 162, 235)',
'rgba(75, 192, 192)',
'rgba(153, 102, 255)'
],
categoryPercentage: 0.8
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: ctx => data[ctx.dataIndex]
}
}
},
scales: {
y: {
min: 0,
max: Math.max(...data) + offset
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<canvas id="myChart" height="100"></canvas>

uminder
- 23,831
- 5
- 37
- 72
-
This creates a shadow of one bar rather than two bars that are overlapping. I think the question is for the latter. – Brendan Whiting Jan 03 '23 at 18:16