How to fix bars in Chart.js with long labels - javascript

I use Chart.js( Version: 2.7.2 ) in my application and some labels in resulting rows are rather long
var barCanvas = document.getElementById("canvasVoteNames");
var ctx = barCanvas.getContext('2d');
var numberWithCommas = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
var self = this;
var myChart = new Chart(ctx, { // stacked bar report https://jsfiddle.net/sdfx/hwx9awgn/
type: 'bar',
data: {
labels:monthsXCoordItems,
datasets: [
{
label: 'Correct Votes',
data: voteValuesCorrect,
borderWidth: 1, // The stroke width of the bar in pixels.
backgroundColor : formatColor('#05b932'), //rgba(0, 0, 0, 0.1), // The fill color of the bar. See Colors
borderColor: formatColor('#05b932'),// rgba(255, 0, 0, 0.1) // The color of the bar border.
hoverBackgroundColor : formatColor('#05b932'), // The fill colour of the bars when hovered.
hoverBorderColor: formatColor('#05b932'), // The stroke colour of the bars when hovered.
hoverBorderWidth : 1 // The stroke width of the bars when hovered.
},
{
label: 'Incorrect Votes',
data: voteValuesNoneCorrect,
borderWidth: 1, // The stroke width of the bar in pixels.
backgroundColor : formatColor('#b1a19a'), //rgba(0, 0, 0, 0.1), // The fill color of the bar. See Colors
borderColor: formatColor('#b1a19a'),// rgba(255, 0, 0, 0.1) // The color of the bar border.
hoverBackgroundColor : formatColor('#b1a19a'), // The fill colour of the bars when hovered.
hoverBorderColor: formatColor('#b1a19a'), // The stroke colour of the bars when hovered.
hoverBorderWidth : 1 // The stroke width of the bars when hovered.
},
]
},
options: { // options of Report By Vote Names
animation: {
duration: 10,
},
tooltips: { // tooltip text of Report By Vote Days ( 'bar' report )
mode: 'label',
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
}, // tooltips: { // tooltip text of Report By Vote Days ( 'bar' report )
scales: { // options for x and y scales
xAxes: [{
stacked: true, // Stacked bar charts can be used to show how one data series i
gridLines: { display: false },
}],
yAxes: [{
stacked: true, // Stacked bar charts can be used to show how one data series i
ticks: {
callback: function(value) { // on Y scale show only integer without decimals
if (Math.floor(value) === value) {
return value;
}
}, // callback: function(value) { return numberWithCommas(value); },
},
}],
}, // scales: { // options for x and y scales
legend: {display: true}
} // options: { // options of Report By Vote Names
}); // var myChart = new Chart(ctx, { // stacked bar report https://jsfiddle.net/sdfx/hwx9awgn/
}
The chart I got is what I need
https://imgur.com/a/n1SsW7w
but with long labels for any bar it does not look good and I did not find if there is a way to fix it somehow ?
Why labels has big marging, not as relative bars?
Some options for xAxes or additive legends?
Thanks!

You were using ChartJs version 2.1.3 in your JSFiddle, which does not seem to handle multiline labels
You can use multilines labels with the following solutions:
var dates = [["Some l-o-o-o-o-", "o-o-o-o-o-o-o-", "n-n-n-n-n-n-g-g-g-", "g-g-g-g label"], "DDD", ["EEE", "FFF", "GGG"], "HHH", "III"];
You can replace a label by an array, and each element of the array will be considered as a new line (See JSFiddle): https://jsfiddle.net/cyuwxh3q/
If your labels are generated dinamically, you can split them with a plugin in your chart configuration :
type: 'bar',
data: {...},
options: {...},
plugins: [{
beforeInit: function (chart) {
chart.data.labels.forEach(function (value, index, array) {
var a = [];
a.push(value.slice(0, 5));
var i = 1;
while(value.length > (i * 5)){
a.push(value.slice(i * 5, (i + 1) * 5));
i++;
}
array[index] = a;
})
}
}]
This function will turn each label into an array of element which length is less or equal to the given value (here 5) (See JSFiddle) : https://jsfiddle.net/jhr5nm17/
Those are two easy ways to handle long labels by replacing them by multiline labels, hope it helps.

Related

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

Chartjs gradient background color based on min and max values

I'm using chartjs with vue-chart to display some data in my application and actually what I would like to achieve is to have a Bar chart with a background color from red to green.
The chart has dynamic min and max values but it cannot be less than 0 and greater than 5. Similar to a 5 star rating.
In order to show differences in the displayed data I'm not fixing the chart min/max from 0 to 5.
Currently this is the code to show my chart:
Options:
min is -0.2 than minimum value but not less than 0;
max is +0.2 than maximum value but not greater than 5;
return {
legend: {
display: false
},
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
min,
max,
stepSize: 0.5
}
}
]
}
}
Bar chart data:
return {
labels: this.labels,
datasets: [
{
label: 'Evaluation',
data: [
this.photo.composition,
this.photo.technique,
this.photo.creativity,
this.photo.content,
this.photo.lighting
],
borderWidth: 1
}
]
}
Render chart:
const gradient = this.$refs.canvas
.getContext('2d')
.createLinearGradient(0, 300, 0, 0)
gradient.addColorStop(0, '#FF5722')
gradient.addColorStop(0.5, '#FFC107')
gradient.addColorStop(1, '#8BC34A')
this.data.datasets[0].backgroundColor = gradient
this.renderChart(this.data, this.options)
The result is pretty close to what I'm looking for:
Unfortunately what I would like to see is the red color on 0 and green on 5 so the gradient should not reach the green if maximum value is 2.5 (here I would expect the orange) and the same for lower values... I hope it make sense.
Can someone point me to the right direction? Thanks!
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 a gradient for the y-axis that spreads that desired area (values 0 to 5).
let yAxis = chart.scales["y-axis-0"];
let yBottom = yAxis.getPixelForValue(0);
let yTop = yAxis.getPixelForValue(5);
let gradient = ctx.createLinearGradient(0, yBottom, 0, yTop);
Please take a look at below sample and see how it works.
const data = [2, 2.25, 3.3];
new Chart(document.getElementById("chart"), {
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 yTop = yAxis.getPixelForValue(5);
let gradient = ctx.createLinearGradient(0, yBottom, 0, yTop);
gradient.addColorStop(0, '#FF5722');
gradient.addColorStop(0.5, '#FFC107');
gradient.addColorStop(1, '#8BC34A');
chart.data.datasets[0].backgroundColor = gradient;
ctx.restore();
}
}],
data: {
labels: ["A", "B", "C"],
datasets: [{
label: "Evaluation",
data: data
}]
},
options: {
scales: {
yAxes: [{
ticks: {
min: Math.min(...data) - 0.2,
max: Math.max(...data) + 0.2,
stepSize: 0.5
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="chart" height="80"></canvas>

chartjs: need color bars near yAxis scale

I would like to achieve something like the below image.
Chart with color band in yAxis
I already have working version of line chart in different places in our application. In One place we need a chart with color band in yAxis. Somehow I achieved this result StackBlitz Example
But the problems are
Not able to adjust the width of the bar
Not able to move the position of the bar close to yAxis
You can define a separate x-axis for the bar and name it "bar-x-axis" for example. The important thing is to define offset: false, ticks.display: false and gridLines.display: false.
xAxes: [{
stacked: true
},
{
id: "bar-x-axis",
offset: false,
stacked: true,
ticks: {
display: false
},
gridLines: {
display: false
}
}
]
Your bar datasets are linked to this new x-axis through the option xAxisID: "bar-x-axis" . To change the width of the bars, you would define a value between below 1 for barPercentage.
{
label: "Low-Moderate",
backgroundColor: "#ffe100",
yAxisID: "bar-y-axis",
xAxisID: "bar-x-axis",
data: [20],
barPercentage: 0.5
}
Please have a look at your amended StackBlitz.
You can draw individual boxes directly on the canvas using the Plugin Core API. The API offers a range of hooks that may be used for performing custom code.
First, you would define the color bars in an array as follows:
const colorBars = [
{ y: 20, color: "#aad700" },
{ y: 40, color: "#ffe100" },
{ y: 60, color: "#ef0000" },
{ y: 80, color: "#aad700" },
{ y: 100, color: "#ffe100" }
];
Then you can define a plugin with the beforeDraw hook as shown below:
const plugins = [
{
beforeDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
ctx.save();
var prevY = 0;
colorBars.forEach((c, i) => {
ctx.fillStyle = c.color;
ctx.beginPath();
var yBottom = yAxis.getPixelForValue(prevY);
var yTop = yAxis.getPixelForValue(c.y);
ctx.fillRect(xAxis.left - 10, yTop, 10, yBottom - yTop);
ctx.stroke();
prevY = c.y;
});
ctx.restore();
}
}
];
The drawback of this solution is, that you won't see a tooltip when the mouse pointer hovers over the bars.
Please have a look at your amended StackBlitz and see how it works.

Plot Ranged horizontal bar graph with holes using ChartJS

I already have a graph using chartJS as shown below and the code for that is :
r_chart_data = [[-0.001, 2.052],[-2.385, 2.052]];
dr_chart_data = [[-0.01, 1.83],[1.05, 2.83],[1.05, 2.83]];
myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: label,
datasets: [{
label : 'DataSet1',
data: r_chart_data,
backgroundColor: 'rgb(255, 99, 132)'
},
{
label : 'DataSet2',
data: dr_chart_data,
backgroundColor: 'lightblue'
}
]
},
options: {
lineAtIndex : level,
scales: {
xAxes: [{
ticks: {
min: -5,
max: 5,
stepSize: 0.5,
}
}]
}
}
});
But what if my
r_chart_data[0] i.e. [-0.001,2.052]
is not a continuos range and instead its value is something like
[[-0.001,1.023],[1.5,2.052]]
then how should I plot the graph. Modified r_chart_data:
r_chart_data = [[[-0.001,1.023],[1.5,2.052]],[-2.385, 2.052]];
Similarly if I have a range of data sets that I need to plot in the form of a horizontal bar graph using ChartJS. The data set which are in the form of:
dataset1: [[-10,-4],[-2,2],[4,6]] // should be plotted in a single bar graph
dataset2: [[-2,1],[1.5,3.5],[5.2,8.9]] //should be plotted in a second bar
dataset3: [[2,4],[5,7]] //third bar
.. and so on
Moreover, each dataset need not have three range items. It can have one, two or any number of range items.

In chart.js bar chart, how do I label each bar within a category?

How do I label each bar within a category for the chart.js bar chart?
I have 1 category, with 2 bars within that category. I'm using gradients on the bar chart and these can only be applied to bars within a single category.
options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}],
xAxes: [{
ticks: {
callback: (value, index, values) => {
console.log(value, index, values);
}
}
}]
}
}
let ctx = this.bar.nativeElement.getContext('2d');
let grad1 = ctx.createLinearGradient(0, 0, 0, 200);
grad1.addColorStop(0, 'rgb(105,130,231)');
grad1.addColorStop(1, 'rgb(125,222,238)');
let grad2 = ctx.createLinearGradient(0, 0, 0, 200);
grad2.addColorStop(0, 'rgb(233,105,210)');
grad2.addColorStop(1, 'rgb(247,126,151)');
this.barColors.push({backgroundColor: grad1, hoverBackgroundColor: grad1});
this.barColors.push({backgroundColor: grad2, hoverBackgroundColor: grad2});
HTML:
<canvas #bar baseChart
[datasets]="plotData"
[labels]="l"
[options]="options"
chartType="bar"
[colors]="barColors"
(chartClick)="chartClicked($event)">
</canvas>
1) set 2 labels for each sub category within a category
2) for each bar within a category, add the value if it matches the label position, then add 0 to the value for the alternative sub category not matching the label position
This will produce a bar chart where each category only has 1 bar of a colour gradient unique to that subcategory.
You may need to play around with the bar sizing, since I only have 2 sub categories, I set
scales: {
xAxes: [{
barPercentage: 8,
categoryPercentage: 0.1
}]
}
}
and it aligned with the category label closely enough for me.

Categories

Resources