Draw a horizontal bar chart from right to left - javascript

I am creating a horizontal bar chart in Chart.js as you can see in this jsfiddle.
I want to change the chart direction so that it draws from right to left. I tried dir = rtl and in CSS direction=rtl but they don't work.
This is what I want:

I can't find a native way in the documentation to do this, but you can do it via the fairly simple workaround of negating the data so that Chart.js draws negative values:
invert the values in the dataset:
data.datasets[0].data.map((currentValue, index, array) => {
array[index] = currentValue * -1;
});
right-align the y-axis:
scales: {
yAxes: [{
position: 'right'
...
reformat tooltips for display:
options = {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += tooltipItem.xLabel * -1;
return label;
}
}
...
reformat ticks for display:
xAxes: [{
ticks: {
callback: function(value, index, values) {
return value * -1;
}
...
Here's a working example:
var data = {
labels: ["x1", "x2", "x3", "x4", "x5"],
datasets: [{
label: "Actual",
backgroundColor: 'rgba(0, 0, 255, 0.5)',
borderWidth: 1,
data: [40, 150, 50, 60, 70],
yAxisID: "bar-y-axis1"
}]
};
// invert the sign of each of the values.
data.datasets[0].data.map((currentValue, index, array) => {
array[index] = currentValue * -1;
});
var options = {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += tooltipItem.xLabel * -1; // invert the sign for display.
return label;
}
}
},
scales: {
yAxes: [{
id: "bar-y-axis1",
categoryPercentage: 0.5,
barPercentage: 0.5,
position: 'right' // right-align axis.
}],
xAxes: [{
id: "bar-x-axis1",
stacked: false,
ticks: {
callback: function(value, index, values) {
return value * -1; // invert the sign for tick labelling.
},
beginAtZero: true
}
}]
}
};
var ctx = document.getElementById("canvas").getContext("2d");
var myBarChart = new Chart(ctx, {
type: 'horizontalBar',
data: data,
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<canvas id="canvas"></canvas>

You can do it with this in Chart.js v4. Its same keyword in lower version:
var options = {
scales: {
x: { reverse: true },
},
}
Docs: https://www.chartjs.org/docs/latest/api/interfaces/CoreScaleOptions.html

Related

How to fix overflow data labels in graph created with Chart.js?

I have a graph created with Chart.js. There are options and one of them 'responsive: true' that provides a responsive view. The problem is when I load a graph with data the first time the y-ax labels overflow border and show as on the screen below. When I change the view y-ax labels show with overflow hidden and elepsis at the end of the label.
Also, I need to add a custom baloon for labels, how can I do it?
Before view change:
After view change:
Here is my chank of code:
drawWordsChart: function(placeholder, data, options = {}) {
console.log('drawWordsChart', data);
var sortable = [];
for (var word in data) {
sortable.push([data[word].text, data[word].weight]);
}
sortable.sort(function(a, b) {
return b[1] - a[1];
});
sortable = sortable.slice(0, 20);
let labels = [];
let values = [];
let colors = [], intense = 1;
angular.forEach(sortable, (elem, key) => {
colors.push('rgb(30, 144, 255,' + (intense - key / 20) + ')');
labels.push(elem[0]);
values.push(elem[1]);
});
var horizontalBarChartData = {
labels: labels,
datasets: [{
label: options.label || 'weight',
backgroundColor: colors,
borderColor: colors,
data: values
}]
};
const ctx = document.getElementById(placeholder).getContext('2d');
const myHorizontalBar = new Chart(ctx, {
type: options.type || 'horizontalBar',
data: horizontalBarChartData,
options: {
elements: {
rectangle: {
borderWidth: 1,
}
},
responsive: true,
legend: {
position: 'right',
display: false
},
scales: {
yAxes: [{
barPercentage: 0.1,
barThickness: 10,
maxBarThickness: 10,
minBarLength: 2,
gridLines: {
offsetGridLines: true
},
ticks: {
callback: function(value, index, values) {
return $filter('limitTo')(value, 8) + (value.length > 8 ? '...' : '');
}
}
}]
},
maintainAspectRatio: false
}
});
return myHorizontalBar;
}
Here I added property 'ticks' in options with callback, which limiting value to 8 letters, and add ellipsis at the end. Also, I added a custom filter.
ticks: {
callback: function(value, index, values) {
return $filter('limitTo')(value, 8) + (value.length > 8 ? '...' : '');
}
}

How to add custom text inside the bar and how to reduce the step size in y axis in chart js( Bar chart )

I am trying to insert the custom text inside the bar, I have searched lot of threads still i didn't get any solution. Then i want to reduce the step size in y axis. I have attached my code.
jQuery( document ).ready(function() {
var ctx = document.getElementById('myChart');
if(ctx){
var ctxn = ctx.getContext('2d');
var myChart = new Chart(ctxn, {
type: 'bar',
data: {
labels: ['Sale Estimate'],
datasets: [{
label: 'Original Sale Estimate',
data: [4200000],
backgroundColor: '#bcbec0'
}, {
label: 'Final Sale Price',
data: [5000000],
backgroundColor: '#5a00fe'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stacked: true,
// Abbreviate the millions
callback: function(value, index, values) {
return '$' +value / 1e6 + 'M';
}
}
}],
xAxes: [{
// Change here
gridLines : {
display : false
},
barPercentage: 0.8,
barThickness: 84,
stacked: true
}]
}, legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
var roundoffLabel = Math.round(tooltipItems.yLabel);
var millionAft = convertNum(roundoffLabel);
return data.datasets[tooltipItems.datasetIndex].label +': ' + '$' + millionAft;
},labelTextColor: function(tooltipItem, chart) {
return '#000';
}
},
titleSpacing: 5,
backgroundColor: '#ffffff',
titleFontColor : '#000000',
cornerRadius : 0,
xPadding : 10,
yPadding : 10,
mode: 'index'
}
}
});
}
});
My current code giving this output. I need exact design attached above. I have tried to reduce the stepsize in y-axis i am not able to find the correct solution.
Please anyone help me to fix this.
You can add labels using
afterDatasetsDraw
and change the steps using
stepSize
jQuery( document ).ready(function() {
var maxValue = 5200000;
var ctx = document.getElementById('myChart');
if(ctx){
var ctxn = ctx.getContext('2d');
var myChart = new Chart(ctxn, {
type: 'bar',
data: {
labels: ['Sale Estimate'],
datasets: [{
label: 'Original Sale Estimate',
data: [3950000],
backgroundColor: '#bcbec0'
}, {
label: 'Final Sale Price',
data: [maxValue],
backgroundColor: '#5a00fe'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stacked: true,
// Abbreviate the millions
callback: function(value, index, values) {
return '$ ' + value / 1e6 + 'M';
},
stepSize: 1000000, // <----- This prop sets the stepSize,
max: 6000000
}
}],
xAxes: [{
// Change here
gridLines : {
display : false
},
barPercentage: 0.8,
barThickness: 84,
stacked: true
}]
}, legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
var roundoffLabel = Math.round(tooltipItems.yLabel);
var millionAft = parseFloat(roundoffLabel);
return data.datasets[tooltipItems.datasetIndex].label +': ' + '$' + millionAft;
},labelTextColor: function(tooltipItem, chart) {
return '#000';
}
},
titleSpacing: 5,
backgroundColor: '#ffffff',
titleFontColor : '#000000',
cornerRadius : 0,
xPadding : 10,
yPadding : 10,
mode: 'index'
}
}
});
Chart.plugins.register({
afterDatasetsDraw: function(chart, easing) {
// To only draw at the end of animation, check for easing === 1
var ctx = chart.ctx;
chart.data.datasets.forEach(function (dataset, i) {
var meta = chart.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
if (dataset.data[index] == 5000000) return;
// Draw the text in white, with the specified font
ctx.fillStyle = 'rgb(255, 255, 255)';
var fontSize = 16;
var fontStyle = 'bold';
var fontFamily = 'Arial';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// Just naively convert to string for now
var dataString = dataset.data[index].toString();
// Make sure alignment settings are correct
ctx.textAlign = 'center';
ctx.textBaseline = 'text-top';
var padding = 30;
var position = element.tooltipPosition();
ctx.fillText((maxValue - dataset.data[index])/1000000, position.x, position.y - (fontSize / 2) - padding);
//ctx.fillText(dataset.data[index], position.x, position.y - (fontSize / 2) - padding);
padding = 12;
fontStyle = 'normal';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillText("million", position.x, position.y - (fontSize / 2) - padding);
});
}
});
}
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js" type="text/javascript"></script>
<canvas id="myChart"></canvas>

How to display different tooltip based on data values in ChartJS v2?

I’m using ChartJs, to display a Line chart and I’m trying to do 2 things :
The first one is to display different colors based on the tooltip’s value. Highest value vs Medium value
The second one is to display a different tooltip if the tooltips value is the lowest. Minimun value
I’ve tried to use a custom plugins to do this, but It didn’t work
This is the code I've managed to do so far :
Chart.plugins.register({
beforeRender: function(chart) {
if (chart.config.options.showAllTooltips) {
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function(dataset, i) {
chart.getDatasetMeta(i).data.forEach(function(sector, j) {
console.log(j, sector);
chart.pluginTooltips.push(
new Chart.Tooltip(
{
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector],
},
chart
)
);
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function(chart, easing) {
if (chart.config.options.showAllTooltips) {
if (!chart.allTooltipsOnce) {
if (easing !== 1) return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function(tooltip) {
tooltip.initialize();
tooltip._options.bodyFontFamily = "Visby";
// Change color based on value
tooltip._options.bodyFontColor = '#FEB122';
// Change tooltip's html if minimun value of dataset
// Values .datapoints[0].value
// console.log(tooltip._model);
tooltip._options.displayColors = false;
tooltip._options.bodyFontSize = tooltip._chart.height * 0.05;
tooltip._options.yPadding = 0;
tooltip._options.xPadding = 0;
tooltip.update();
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
let canvas = document.getElementById("myLineChart");
Chart.defaults.global.defaultFontFamily = "Visby";
const ctx = canvas.getContext('2d');
const labels = JSON.parse(ctx.canvas.dataset.dates);
const prices = JSON.parse(ctx.canvas.dataset.prices);
const myLineChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: "Prix du billet",
data: prices,
backgroundColor: [
'rgba(0, 0, 0, 0)',
],
borderColor: [
'#F2F2F2',
],
pointBackgroundColor:
'#FEB122',
pointBorderColor:
'#FEB122',
borderWidth: 3,
}]
},
options: {
showAllTooltips: true, // call plugin we created
responsive: true,
cutoutPercentage: 60,
legend: {
position: "bottom"
},
animation: {
animateScale: true,
animateRotate: true
},
tooltips: {
enabled: false,
backgroundColor:"rgba(0,0,0,0)",
callbacks: {
title: function(tooltipItems, data) {
return "";
},
label: function(tooltipItem, data) {
var datasetLabel = "";
var label = data.labels[tooltipItem.index];
return data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + '€';
},
labelColor: function(tooltipItem, data) {
// console.log(data);
}
}
},
legend: {
display: false
},
layout: {
padding: {
left: 32,
right: 32,
top: 32,
bottom: 32
}
},
scales: {
xAxes: [{
gridLines: {
display: false,
drawBorder: false,
},
}],
yAxes: [{
display: false
}]
}
}
});
How could I make this work ?
I've managed to do that by using the plugin Chartjs Datalabels.
And using the scriptable colors options.

Display multiple tags in the legend for a single data value in Chart.js

I have graph that I built with the Chart.js library :
Normally, Sshare is represented with two color, red and green. In the legend, however, Sshare displays with just the first color value, red.
How can I get both Sshare colors to show in the legend?
I tried searching for a solution in the Chart.js documentation, but could not find a way to edit the legend properties.
My code:
<script>
// chart colors //BLUE & RED & VERT
var colors = ['#007bff','#dc3545',"#008000"];
var colors_suggested =[];
var labels_in =[];
var mshare_value =[];
var svalues =[];
var data_work_in =[
{ "ID":12, "Les": "AB", "Name": " AB_12", "Mmin": 75, "Sshare": 29},
{ "ID":13, "Les": "BB", "Name": " BB_13", "Mmin": 26.8, "Sshare": 36}
];
for (var i = 0; i < data_work_in.length;i++)
{
labels_in.push(data_work_in[i].Name+";"+data_work_in[i].Mill);
mshare_value.push(data_work_in[i].Mmin);
svalues.push(data_work_in[i].Sshare);
if ( data_work_in[i].Sshare >= data_work_in[i].Mmin)
colors_suggested.push(colors[2]);
else {
colors_suggested.push(colors[1]);
}
}
var chBar = document.getElementById("chBar");
var chartData = {
// Label of Entity
labels: labels_in,
// Value of percent category RTI|| VSM ...
datasets: [{
label: 'Mmin',
data: mshare_value,
backgroundColor: colors[0]
},
{
label: 'Sshare',
data: svalues,
xAxisID:'xAxis1',
backgroundColor: colors_suggested
}]
};
if (chBar) {
// new graph
new Chart(chBar, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes:[
{
barPercentage: 0.9,
categoryPercentage: 0.7,
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return sublabel_x;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return label_p;
}
}
}],
yAxes: [{
ticks: {
beginAtZero: false
},
scaleLabel: {
display: true,
labelString: '%'
}
}]
},
legend: {
display: true,
legendText : ['Mmin','Sshare']
}
}
});
}
</script>

ChartJS New Lines '\n' in X axis Labels or Displaying More Information Around Chart or Tooltip with ChartJS V2

I'm using chart.js (V2) to try to build a bar chart that has more information available to user without having to hover over or click anywhere. I've provided two examples of how I hope to edit my chart.
Two edited versions of what I hope to achieve
As can be seen, I hope to place (somewhere), some extra information outside of the labels. I had hope that by adding '\n' to the labels I might have been able to get what I was looking for similar to option A.
Some edited code is provided blow:
var barChartData = {
labels: playerNames,
datasets: [{
label: 'Actual Score/Hour',
backgroundColor: "rgba(0, 128, 0,0.5)",
data: playerScores
}, {
label: 'Expected Score/Hour',
backgroundColor: "rgba(255,0,0,0.5)",
data: playerExpected
}]
};
function open_win(linktosite) {
window.open(linktosite)
}
canvas.onclick = function(evt){
var activePoints = myBar.getElementsAtEvent(evt);
console.log(activePoints);
linktosite = 'https://www.mytestsite.com/' + activePoints[1]['_model']['label'];
open_win(linktosite);
};
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
title:{
display:true,
text:"Player Expected and Actual Score per Hour"
},
tooltips: {
mode: 'label'
},
responsive: true,
scales: {
xAxes: [{
stacked: false,
}],
yAxes: [{
stacked: false
}]
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx;
ctx.textAlign = "center";
Chart.helpers.each(this.data.datasets.forEach(function (dataset) {
Chart.helpers.each(dataset.metaData.forEach(function (bar, index) {
// console.log("printing bar" + bar);
ctx.fillText(dataset.data[index], bar._model.x, bar._model.y - 10);
}),this)
}),this);
}
}
}
});
// Chart.helpers.each(myBar.getDatasetMeta(0).data, function(rectangle, index) {
// rectangle.draw = function() {
// myBar.chart.ctx.setLineDash([5, 5]);
// Chart.elements.Rectangle.prototype.draw.apply(this, arguments);
// }
// }, null);
};
At this point I'd be satisfied with having the extradata anywhere on the bar. Any help would be appreciated. Thanks~
Chart.js v2.1.5 allows for multi-line labels using nested arrays (v2.5.0 fixes it for radar graphs):
...
data: {
labels: [["Jake", "Active: 2 hrs", "Score: 1", "Expected: 127", "Attempts: 4"],
["Matt", "Active: 2 hrs", "Score: 4", "Expected: 36", "Attempts: 4"]],
...
However, this does mean that you will have to pre-calculate the label values.
var config = {
type: 'line',
data: {
labels: [["January","First Month","Jellyfish","30 of them"], ["February","Second Month","Foxes","20 of them"], ["March","Third Month","Mosquitoes","None of them"], "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 40, 80, 81, 56, 85, 45],
backgroundColor: "rgba(255,99,132,0.2)",
}, {
label: "My Second dataset",
data: [40, 80, 21, 56, 85, 45, 65],
backgroundColor: "rgba(99,255,132,0.2)",
}]
},
scales : {
xAxes : [{
gridLines : {
display : false,
lineWidth: 1,
zeroLineWidth: 1,
zeroLineColor: '#666666',
drawTicks: false
},
ticks: {
display:true,
stepSize: 0,
min: 0,
autoSkip: false,
fontSize: 11,
padding: 12
}
}],
yAxes: [{
ticks: {
padding: 5
},
gridLines : {
display : true,
lineWidth: 1,
zeroLineWidth: 2,
zeroLineColor: '#666666'
}
}]
},
spanGaps: true,
responsive: true,
maintainAspectRatio: true
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
<div class="myChart">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
<canvas id="myChart"></canvas>
</div>
If a label is an array as opposed to a string i.e. [["June","2015"], "July"] then each element is treated as a separate line. The appropriate calculations are made to determine the correct height and width, and rotation is still supported.
charJS version 2.7.2 used
this also works in https://github.com/jtblin/angular-chart.js
If you are using Chart.js v2.7.1, the above solution might not work.
The solution that actually worked for us was adding a small plugin right in the data and options level:
const config = {
type: 'bar',
data: {
// ...
},
options: {
// ...
},
plugins: [{
beforeInit: function (chart) {
chart.data.labels.forEach(function (label, index, labelsArr) {
if (/\n/.test(label)) {
labelsArr[index] = label.split(/\n/)
}
})
}
}]
};
A full description of how to fix this issue can be found here.
With Chart.js v2.1, you can write a chart plugin to do this
Preview
Script
Chart.pluginService.register({
beforeInit: function (chart) {
var hasWrappedTicks = chart.config.data.labels.some(function (label) {
return label.indexOf('\n') !== -1;
});
if (hasWrappedTicks) {
// figure out how many lines we need - use fontsize as the height of one line
var tickFontSize = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontSize, Chart.defaults.global.defaultFontSize);
var maxLines = chart.config.data.labels.reduce(function (maxLines, label) {
return Math.max(maxLines, label.split('\n').length);
}, 0);
var height = (tickFontSize + 2) * maxLines + (chart.options.scales.xAxes[0].ticks.padding || 0);
// insert a dummy box at the bottom - to reserve space for the labels
Chart.layoutService.addBox(chart, {
draw: Chart.helpers.noop,
isHorizontal: function () {
return true;
},
update: function () {
return {
height: this.height
};
},
height: height,
options: {
position: 'bottom',
fullWidth: 1,
}
});
// turn off x axis ticks since we are managing it ourselves
chart.options = Chart.helpers.configMerge(chart.options, {
scales: {
xAxes: [{
ticks: {
display: false,
// set the fontSize to 0 so that extra labels are not forced on the right side
fontSize: 0
}
}]
}
});
chart.hasWrappedTicks = {
tickFontSize: tickFontSize
};
}
},
afterDraw: function (chart) {
if (chart.hasWrappedTicks) {
// draw the labels and we are done!
chart.chart.ctx.save();
var tickFontSize = chart.hasWrappedTicks.tickFontSize;
var tickFontStyle = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = Chart.helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
chart.chart.ctx.font = tickLabelFont;
chart.chart.ctx.textAlign = 'center';
var tickFontColor = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].fontColor, Chart.defaults.global.defaultFontColor);
chart.chart.ctx.fillStyle = tickFontColor;
var meta = chart.getDatasetMeta(0);
var xScale = chart.scales[meta.xAxisID];
var yScale = chart.scales[meta.yAxisID];
chart.config.data.labels.forEach(function (label, i) {
label.split('\n').forEach(function (line, j) {
chart.chart.ctx.fillText(line, xScale.getPixelForTick(i + 0.5), (chart.options.scales.xAxes[0].ticks.padding || 0) + yScale.getPixelForValue(yScale.min) +
// move j lines down
j * (chart.hasWrappedTicks.tickFontSize + 2));
});
});
chart.chart.ctx.restore();
}
}
});
and then
...
data: {
labels: ["January\nFirst Month\nJellyfish\n30 of them", "February\nSecond Month\nFoxes\n20 of them", "March\nThird Month\nMosquitoes\nNone of them", "April", "May", "June", "July"],
...
Note - we assume that the maximum content of one line will fit between the ticks (i.e. that no rotation logic is needed. I'm sure it's possible to incorporate rotation logic too, but it would be a tad more complicated)
You should format the tooltips to not show the x axis label, or format it to show a shorter version of the label.
Fiddle - http://jsfiddle.net/m0q03wpy/

Categories

Resources