In Chart.js, is there any way to write the labels inside of the horizontal bars in a "horizontalBar" chart? As in something like:
Is anything similar to this possible in Chart.js?
Thanks!
There is now on option "mirror" to make the label appear inside the bar.
Example of "options" config for a horizontalBar chart :
options: {
scales: {
yAxes: [{ticks: {mirror: true}}]
}
}
Doc : http://www.chartjs.org/docs/latest/axes/cartesian/#common-configuration
With reference to the original solution to displaying data value by using HTML5 Canvas fillText() method in the animation's onComplete, the code below will get you what you need:
The key to custom-displaying any chart related data is inspecting the chart's dataset as in this.data.datasets below. I did this by inspecting where the different values are in this dataset, as well as the position to place them for the fillText method.
Inspecting the chart's dataset: Image
Script (Chart.js 2.0 & up):
var barOptions = {
events: false,
showTooltips: false,
legend: {
display: false
},
scales:{
yAxes: [{
display: false
}]
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
left = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._xScale.left;
ctx.fillStyle = '#444'; // label color
var label = model.label;
ctx.fillText(label, left + 15, model.y + 8);
}
});
}
}
};
jsFiddle: http://jsfiddle.net/jfvy12h9/
You can achieve this effect in chartjs 2.0 + by rotating the labels 90 degrees and applying negative padding, so that the label moves up inside the 'bar'. Something like this:
new Chart(document.getElementById(id), {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [
{
ticks: {
autoSkip: false,
maxRotation: 90,
minRotation: 90,
padding: -110
}
}
]
}
}
});
See the tick configuration documentation for more information.
It seems like there is no direct option for this right now. A possible implementation is iterating over bars/columns by using onAnimationComplete hook. You can view this question for detailed explanations how to display data values on Chart.js
I would like you remind that this has a drawback. Since it uses onAnimationComplete, whenever an animation happens on chart, values will be disappear for a second and appear again.
Since on "bar" chart doesn't work the "mirror" option...
I've found a workaround solution:
Fiddle link
I used a chart.js 2.8.0 version modified in this link
<html>
<body>
<canvas id="myChart" width="800" height="600">
</canvas>
</body>
</html>
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT"],
datasets: [{
label: "Quota Eni mensile",
backgroundColor: [
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)"
],
borderColor: [
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)"
],
borderWidth: 1,
data: [10, 11, 18, 10, 13, 28, 12, 16]
}
]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [{
ticks:{
maxRotation: 90,
minRotation: 90,
display: "top"
},
}],
yAxes: [{
display: false
}]
},
tooltips: {
enabled: true
},
maintainAspectRatio: true,
responsive: true
}
});
Chart.plugins.register({
/* Adjust axis labelling font size according to chart size */
id: "responsiveXlabels",
beforeDraw: function(c) {
var chartHeight = c.chart.height;
var size = (chartHeight * 2.5) / 100;
var xAxis = c.scales["x-axis-0"];
xAxis.options.ticks.minor.fontSize = size;
xAxis.height = 10;
xAxis.minSize.height = 10;
c.options.scales.xAxes[0].ticks.padding = -size * 4.5;
xAxis.margins.bottom = size * 12.5;
},
afterDraw: function(c) {
c.options.scales.xAxes[0].ticks.padding = -158;
}
});
the responsiveXlabel plugin is used to transform text on resize.
There's only one thing remained to solve: align text at the bottom of the axis.
Hope this helps who wants to implement mirroring on X Axis and satisfy responsive request.
Related
I want to draw dashed lines on the x-axis for the negative value on my chart, how I can do that?
You can draw the grid lines labels directly on the canvas using the Plugin Core API. It offers a number of hooks that can be used to perform custom code. In your case, you could use the afterDraw as follows:
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
yAxis.ticks.forEach((v, i) => {
var y = yAxis.getPixelForTick(i);
ctx.strokeStyle = 'rgb(128, 128, 128, 0.3)';
ctx.lineWidth = 1;
ctx.setLineDash(v < 0 ? [10, 5] : []);
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
});
ctx.restore();
}
}],
This code uses CanvasRenderingContext2D to draw lines of different style. It iterate over yAxis.ticks using the Array.forEach() method. The line style (i.e. a dash pattern) is defined using method setLineDash() depending on the value v.
ctx.setLineDash(v < 0 ? [10, 5] : []);
If v is below zero, I provide pattern [10, 5], otherwise an empty array [] in order to obtain a solid line.
Please have a look at below runnable code snippet to see how it works.
new Chart(document.getElementById("chart"), {
type: "bar",
plugins: [{
afterDraw : chart => {
var ctx = chart.chart.ctx;
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
yAxis.ticks.forEach((v, i) => {
var y = yAxis.getPixelForTick(i);
ctx.strokeStyle = 'rgb(128, 128, 128, 0.3)';
ctx.lineWidth = 1;
ctx.setLineDash(v < 0 ? [10, 5] : []);
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
labels: ["A", "B", "C"],
datasets: [{
label: "Dataset 1",
data: [10, 15, 10],
backgroundColor: ["rgba(255, 99, 132, 0.2)", "rgba(255, 159, 64, 0.2)", "rgba(255, 205, 86, 0.2)", "rgba(75, 192, 192, 0.2)"],
borderColor: ["rgb(255, 99, 132)", "rgb(255, 159, 64)", "rgb(255, 205, 86)"],
borderWidth: 1
},
{
label: "Dataset 2",
data: [-15, -10, -15],
backgroundColor: "rgba(0, 255, 0, 0.2)",
borderColor: "rgb(0, 255, 0)",
borderWidth: 1
}
]
},
options: {
layout: {
padding: {
left: 12
}
},
legend: {
display: false
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
}
}],
yAxes: [{
stacked: true,
gridLines: {
drawOnChartArea: false
},
ticks: {
stepSize: 5
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="200"></canvas>
Based on the documentation, the padding property of the title in Chart.js adjusts both the top and bottom paddings. However, I'd like to adjust only the bottom padding.
I've tried using a padding object:
title:{
display: true,
text:'Population of cities in California',
padding:{
left:0,
right:0,
bottom:30,
top:0
}
}
But this is probably not supported, because it just messed everything up and made the chart not even visible.
I've searched the documentation and googled a bit to see if this has been asked somewhere already, but I couldn't find any solutions.
Is there a built-in way to do this within Chart.js? If not, what would be the simplest workaround?
title.padding takes the number of pixels to add above and below the title text.
There exists a workaround to add padding at the bottom of the chart title only. The Plugin Core API offers a range of hooks that may be used for performing custom code. You can use the afterDraw hook to draw the title yourself directly on the canvas using CanvasRenderingContext2D.fillText().
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
ctx.textAlign = 'center';
ctx.font = "18px Arial";
ctx.fillStyle = "gray";
ctx.fillText('My Title', chart.chart.width / 2, 20);
ctx.restore();
}
}],
Together with this, you'll have to define top padding for your chart. This determines the space between your title and the chart (the title bottom padding basicaly).
layout: {
padding: {
top: 80
}
},
Please have a look at below code that shows how it could look like.
new Chart(document.getElementById('myChart'), {
type: 'bar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
ctx.textAlign = 'center';
ctx.font = "18px Arial";
ctx.fillStyle = "gray";
ctx.fillText('My Title', chart.chart.width / 2, 20);
ctx.restore();
}
}],
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 12, 8, 6],
backgroundColor: ['rgba(255, 99, 132, 0.2)', 'rgba(255, 159, 64, 0.2)', 'rgba(255, 205, 86, 0.2)', 'rgba(75, 192, 192, 0.2)'],
borderColor: ['rgb(255, 99, 132)', 'rgb(255, 159, 64)', 'rgb(255, 205, 86)', 'rgb(75, 192, 192)'],
borderWidth: 1
}]
},
options: {
layout: {
padding: {
top: 80
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="100"></canvas>
I'm using a stacked bar chart in ChartJS for plotting the numbers of two contrary datasets. I've managed this by subtracting the numbers of the second dataset from 0, hence we get [-1, -2, -3] instead of [1, 2, 3].
Commonly we would identify the two datasets by a legend.
But is there an option to add two titles on the y-axis, where the top of the y-axis, i.e. on the positive part of the scale, is labeled with "Dataset 1" and the bottom of the y-axis, i.e. on the negative part of the scale, is labeled with "Dataset 2"?
You can draw the y-axis scale labels directly on the canvas using the Plugin Core API. It offers a number of hooks that can be used to perform custom code. In your case, you could use the afterDraw hook as follows:
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.textAlign = "center";
var fontSize = 12;
ctx.font = fontSize + "px Arial";
ctx.rotate(-Math.PI / 2);
var yZero = yAxis.getPixelForValue(0);
ctx.fillText("Dataset 1", yZero / -2, fontSize);
ctx.fillText("Dataset 2", (yAxis.bottom + yZero) / -2, fontSize);
ctx.restore();
}
}],
You also need to define a value for layout.padding.left inside the chart options in order to avoid that your scale labels overlap the tick labels.
options: {
layout: {
padding: {
left: 12
}
},
Please have a look at below runnable code snippet.
new Chart(document.getElementById("chart"), {
type: "bar",
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.textAlign = "center";
var fontSize = 12;
ctx.font = fontSize + "px Arial";
ctx.rotate(-Math.PI / 2);
var yZero = yAxis.getPixelForValue(0);
ctx.fillText("Dataset 1", yZero / -2, fontSize);
ctx.fillText("Dataset 2", (yAxis.bottom + yZero) / -2, fontSize);
ctx.restore();
}
}],
data: {
labels: ["A", "B", "C"],
datasets: [{
label: "Dataset 1",
data: [1, 2, 3],
backgroundColor: ["rgba(255, 99, 132, 0.2)", "rgba(255, 159, 64, 0.2)", "rgba(255, 205, 86, 0.2)", "rgba(75, 192, 192, 0.2)"],
borderColor: ["rgb(255, 99, 132)", "rgb(255, 159, 64)", "rgb(255, 205, 86)"],
borderWidth: 1
},
{
label: "Dataset 2",
data: [-1, -2, -3],
backgroundColor: ["rgba(255, 99, 132, 0.2)", "rgba(255, 159, 64, 0.2)", "rgba(255, 205, 86, 0.2)", "rgba(75, 192, 192, 0.2)"],
borderColor: ["rgb(255, 99, 132)", "rgb(255, 159, 64)", "rgb(255, 205, 86)"],
borderWidth: 1
}
]
},
options: {
layout: {
padding: {
left: 12
}
},
legend: {
display: false
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="200"></canvas>
Is it possible to separate styles in ticks for xAxes or draw by some other function
near.Callback - just operate with values.
For example:
You can make use of 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 draw text of different styles underneath each bars.
Note that no computation of text size is needed when composing your own labels labels. You can simply add a space to the first text section and use ctx.textAlign 'right', then use ctx.textAlign 'left' for drawing the second text section.
When drawing your own tick labels, you need to instruct Chart.js not to display the default labels. This can be done through the following definition inside the chart options.
scales: {
xAxes: [{
ticks: {
display: false
}
}],
You also need to define some padding for the bottom of the chart, otherwise you won't see your custom tick labels.
layout: {
padding: {
bottom: 20
}
},
new Chart(document.getElementById('myChart'), {
type: 'bar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.textAlign = 'right';
ctx.font = '12px Arial';
ctx.fillText(l + ' ', x, yAxis.bottom + 18);
ctx.textAlign = 'left';
ctx.font = 'bold 14px Arial';
ctx.fillStyle = 'blue';
ctx.fillText(value, x, yAxis.bottom + 17);
});
ctx.restore();
}
}],
data: {
labels: ['Error', 'Warn', 'Info'],
datasets: [{
label: 'My First Dataset',
data: [30, 59, 80],
fill: false,
backgroundColor: ['rgba(255, 99, 132, 0.2)', 'rgba(255, 159, 64, 0.2)', 'rgba(255, 205, 86, 0.2)'],
borderColor: ['rgb(255, 99, 132)', 'rgb(255, 159, 64)', 'rgb(255, 205, 86)', 'rgb(75, 192, 192)'],
borderWidth: 1
}]
},
options: {
layout: {
padding: {
bottom: 20
}
},
scales: {
xAxes: [{
ticks: {
display: false
}
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="myChart" width="10" height="5"></canvas>
Chart.js does not support the "mirror" option on vertical "bar" chart.
I tried to search for a workaround but i did not find any help on internet.
I wrote a workaround solution but there's only one thing to solve to be a perfect mirroring:
align text at the bottom axis.
Actual text alignment
Somebody can help me to fix this?
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT"],
datasets: [{
label: "Quota mensile",
backgroundColor: [
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)"
],
borderColor: [
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)",
"rgba(255, 255, 153, 1)"
],
borderWidth: 1,
data: [10, 11, 18, 10, 13, 28, 12, 16]
}]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [{
ticks: {
maxRotation: 90,
minRotation: 90,
display: "top"
},
}],
yAxes: [{
display: false
}]
},
tooltips: {
enabled: true
},
maintainAspectRatio: true,
responsive: true
}
});
Chart.plugins.register({
/* Adjust axis labelling font size according to chart size */
id: "responsiveXlabels",
beforeDraw: function(c) {
var chartHeight = c.chart.height;
var size = (chartHeight * 2.5) / 100;
var xAxis = c.scales["x-axis-0"];
xAxis.options.ticks.minor.fontSize = size;
xAxis.height = 10;
xAxis.minSize.height = 10;
c.options.scales.xAxes[0].ticks.padding = -size * 4.5;
xAxis.margins.bottom = size * 12.5;
},
afterDraw: function(c) {
c.options.scales.xAxes[0].ticks.padding = -158;
}
});
<canvas id="myChart" width="800" height="600"></canvas>
the responsiveXlabel plugin is used to transform text on resize.
Hope this helps who wants to implement mirroring on X Axis and satisfy responsive request.
Fiddle link to my solution: Fiddle link
I used a chart.js 2.8.0 modified version that you can find at this link
A CodePEN BY Jukka Kurkela