React ChartJS -- Scale Bar Chart Vertically With More Datasets - javascript

I'm using React ChartJS to build a bar chart which currently looks like this
The data that this is built on top of is dynamic -- that is, there can be many more industries (or fewer). I'd like to fix the bar width (say, at 30 pixels) and to have the chart grow taller according to how many datasets that I have. In other words, I don't care how tall the chart is, it should be a function of how many datasets that I have.
How would I accomplish this?
I'd share the code I've got already but it's pretty generic, can post it here if folks would find that useful.

First you could wrap the canvas in a div.
<div id="chartWrapper">
<canvas id="myChart"></canvas>
</div>
You can make use of the Plugin Core API and compute the chart height in the beforeRender hook depending on the defined data.barThickness and the visible datasets.
plugins: [{
beforeRender : chart => {
if (!chart.config.options.nonChartAreaHeight) {
var yAxis = chart.scales.y;
chart.config.options.nonChartAreaHeight = chart.height - (yAxis.bottom - yAxis.top);
}
const labelCount = chart.data.labels.length;
const datasetCount = chart.data.datasets.map((ds, i) => chart.getDatasetMeta(i)).filter(m => !m.hidden).length;
const chartAreaHeight = labelCount * datasetCount * chart.data.barThickness * 1.2;
document.getElementById("chartWrapper").style.height = (chartAreaHeight + chart.config.options.nonChartAreaHeight) + 'px';
}
}],
Then you also need to define option.maintainAspectRatio: false to prevent Chart.js from resizing the canvas.
Please take a look at below runnable code and see how it works.
new Chart('myChart', {
type: 'bar',
plugins: [{
beforeRender : chart => {
if (!chart.config.options.nonChartAreaHeight) {
var yAxis = chart.scales.y;
chart.config.options.nonChartAreaHeight = chart.height - (yAxis.bottom - yAxis.top);
}
const labelCount = chart.data.labels.length;
const datasetCount = chart.data.datasets.map((ds, i) => chart.getDatasetMeta(i)).filter(m => !m.hidden).length;
const chartAreaHeight = labelCount * datasetCount * chart.data.barThickness * 1.2;
document.getElementById("chartWrapper").style.height = (chartAreaHeight + chart.config.options.nonChartAreaHeight) + 'px';
}
}],
data: {
labels: ['1', '2', '3', '4'],
barThickness: 30, // change this to adapt the bar thickness
datasets: [{
label: 'Dataset 1',
data: [40, 50, 60, 80],
backgroundColor: 'red'
},
{
label: 'Dataset 2',
data: [100, 90, 80, 70],
backgroundColor: 'blue'
},
{
label: 'Dataset 3',
data: [60, 80, 70, 90],
backgroundColor: 'green'
}]
},
options: {
indexAxis: 'y',
maintainAspectRatio: false
}
});
div {
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<div id="chartWrapper">
<canvas id="myChart"></canvas>
</div>

Related

Changing legend text in chart js causing issues

I'm trying to shorten my chart legends, so I'm using the beforeRender plugin to change the text of the legend. However, once the chart is drawn, it seems to keep the original label size (see below snippet, legends are offset to the left instead of in the centre and if you click on orange, it toggles one of the earlier items).
The same happens if I try in beforeDraw and if I use beforeInit or beforeLayout, the legends don't seem to have been created yet.
Is there any way to get the chart to redraw the legends with the new text? Or change the legend text so that it is rendered properly?
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'pie',
data: {
labels: ['red - some other text that appears after', 'blue - some other text that appears after', 'green - some other text that appears after', 'orange - some other text that appears after'],
datasets: [{
data: [4, 2, 10, 3],
backgroundColor: ['red', 'blue', 'green', 'orange'],
}],
},
options: {
responsive: true,
},
plugins: [{
beforeRender: chart => {
chart.legend.legendItems.forEach(label => {
const labelParts = label.text.split(' - ');
const newLabel = label;
newLabel.text = labelParts[0];
return newLabel;
});
},
}],
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.js"></script>
<div style="width: 100%">
<canvas id="canvas"></canvas>
</div>
A better approach would be to use a custom generateLabels function like so:
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'pie',
data: {
labels: ['red - some other text that appears after', 'blue - some other text that appears after', 'green - some other text that appears after', 'orange - some other text that appears after'],
datasets: [{
data: [4, 2, 10, 3],
backgroundColor: ['red', 'blue', 'green', 'orange'],
}],
},
options: {
responsive: true,
legend: {
labels: {
generateLabels: function(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: label.split(' - ')[0],
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
}
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.js"></script>
<div style="width: 100%">
<canvas id="canvas"></canvas>
</div>

How can I display the xAxes and yAxes data in the tooltip, Chart JS?

I am trying to achieve the Tooltip shown below in the first image. Inside of the tooltip I need to display the yAxes and xAxes data. The chart.js version I am using is 3.7.0
My tooltip looks like this:
The tooltip that I am trying copy:
The chart.js documentation is quite hard for me to understand. Is there any guru that can explain to me.
Question: Why is my tooltip returning the yAxes data, that I return as a variable(label) as undefined?
Are there any other options I can use to make my chart look like the chart in the second picture?
My Code:
tooltip: {
displayColors: false,
backgroundColor: 'rgba(45,132,180,0.8)',
bodyFontColor: 'rgb(255,255,255)',
callbacks: {
title: function(item, everything){
return;
},
label: function(item, everything){
//console.log(item);
//console.log(everything);
let first = item.yLabel;
let second = item.xLabel;
let label = first + ' ppm';
return label;
}
}
}
Thank you in advance for your time and efforts, please help me figure out what am I doing wrong!
yLabel and xLabel dont exist anymore on the tooltip, they are V2 syntax.
You can just axess the y object in the parsed section to get the y value. Then you can use the afterBody callback to show the x label like so:
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}]
},
options: {
plugins: {
tooltip: {
displayColors: false,
backgroundColor: 'rgba(45,132,180,0.8)',
bodyFontColor: 'rgb(255,255,255)',
callbacks: {
title: () => {
return
},
label: (ttItem) => (`${ttItem.parsed.y} ppm`),
afterBody: (ttItems) => (ttItems[0].label)
}
}
}
}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
</body>

ChartJS 3 get height of stacked vertical bar

Ok. So I'm trying to use ChartJS with Datalabels 2.0 (RC) to show labels for stacked vertical bars. When the bar is small, I want those labels to appear outside of it.
Now here is the code I'm using: https://codepen.io/meexo/pen/xxqpELL
The problem I have is that using getProps on dataset, I am unable to retrieve the height of each bar. In the example above I tried to get height by using the getProps() introduced in ChartJS 3. If I console.log it, I see all kind of params, height included (though undefined). What am I doing wrong that height parameter is undefined?
I also tried to use getProps().y param for the code, but in that case it seems to apply only to one section of the stacked bar, not all. And what I want is for labels to appear above the stacked bar when the full height if it is lower than my threshold.
Any ideas?
BTW, though its my first question, its definitely not the first answer I found on StackOverflow :)
My code:
var ctx = document.getElementById("myChart");
Chart.register(ChartDataLabels);
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'red'
}, {
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'blue'
}]
},
options: {
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
},
plugins: {
datalabels: {
anchor: context => {
let bar = context.chart.getDatasetMeta(context.datasetIndex).data[context.dataIndex].getProps().height;
document.getElementById('myHeight').innerHTML = bar;
const threshold = 6;
if (bar + threshold > context.chart.chartArea.bottom) {
return 'end'
}
return 'center'
}
}
}
}
});
The behaviour comes from this part in the code:
const properties = {
horizontal,
base: vpixels.base,
enableBorderRadius: !stack || isFloatBar(parsed._custom) || (me.index === stack._top || me.index === stack._bottom),
x: horizontal ? vpixels.head : ipixels.center,
y: horizontal ? ipixels.center : vpixels.head,
height: horizontal ? ipixels.size : undefined,
width: horizontal ? undefined : ipixels.size
};
It sets the height on undefined for normal bar charts and the width on undefined on horizontal bar charts. Putted in a pr (https://github.com/chartjs/Chart.js/pull/9208) for fix so this should be fixed by the next release of chart.js
Then to get the bar height you have to change your mode to index and then you can use this function to get the total bar height:
onClick: function(event) {
var bar = this.getElementsAtEventForMode(event, 'index', {intersect: true}, true);
if (!bar.length) return; //return if not clicked on bar
var barHeight = bar.reduce((acc , val) => (acc + val.element.height), 0)
document.getElementById('myHeight').innerHTML = barHeight;
}
Example: https://codepen.io/leelenaleee/pen/qBrpLjX

How to know the length of the horizontal bar in Chart.js

I have a horizontal bar in my project (I use chart.js). I need to know its length (in pixels). How to do it?
You can use the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code snippet, I use the afterDraw hook to log the number of pixels of individual bars.
const chart = new Chart(document.getElementById('myChart'), {
type: 'horizontalBar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
chart.config.data.datasets[0].data.forEach((value, index) => {
console.log('index ' + index + ': ' + Math.round(xAxis.getPixelForValue(value)) + 'px');
});
}
}],
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
label: 'By Dataset',
data: [15, 8, 12, 18],
backgroundColor: 'blue'
}]
},
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>

plotly.js waterfall chart with different colors for up and down

I want to create a waterfall chart using plotlyjs which shows ups and downs as compared to its previous value. Up should be represented by green color and down with red color.
How can I create such a graph using plotly.js?
The example given on the plotly site has different colors for different value ranges and it has no connection with ups and downs.
You can actually pass an array of colors to Plotly.
If you have an array of value differences, like [200, 400, -300, -150, -150], then you can formulate a color array like below.
const labels = ["Apples", "Oranges", "Rent", "Water", "Profit"];
const values = [200, 400, -300, -150, -150];
const colors = values.map((v) => v > 0 ? 'rgba(55,128,191,1.0)' : 'rgba(219, 64, 82, 1.0)');
// Use the cumulative sum to calculate the baseline of each bar. Use this to create a stacked bar chart with white bars below and colored bars on top
const baseline = new Array(values.length);
values.reduce((sum, val, idx) => {
baseline[idx] = val > 0 ? sum : sum + val;
return sum + val;
}, 0);
const trace1 = {
x: labels,
y: baseline,
marker: {
color: 'rgba(1,1,1,0.0)'
},
type: 'bar'
};
const trace2 = {
x: labels,
y: values.map(Math.abs),
marker: {
color: colors
},
type: 'bar'
};
var layout = {
title: 'Annual Profit 2018',
barmode: 'stack',
paper_bgcolor: 'rgba(245,246,249,1)',
plot_bgcolor: 'rgba(245,246,249,1)',
width: 600,
height: 600,
showlegend: false,
annotations: []
};
Plotly.newPlot('plot', [trace1, trace2], layout);
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="plot"></div>

Categories

Resources