Chart.js x-axis label mirroring on a vertical bar chart - javascript

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

Related

Not able to change chart.js x and y font colors

I tried changing the xticks color of chart js but somehow this is not working. This is the cdn I am using.
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.4.1/dist/chart.min.js"></script>
function plot(x, y, chartTitle, labelText) {
$("#canvasCard").html(
'<canvas id="myChart" width="600px" height="600px"></canvas>'
);
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "line",
data: {
labels: x,
datasets: [
{
label: labelText,
data: y,
backgroundColor: [
"rgba(255, 99, 132, 0.2)",
"rgba(54, 162, 235, 0.2)",
"rgba(255, 206, 86, 0.2)",
"rgba(75, 192, 192, 0.2)",
"rgba(153, 102, 255, 0.2)",
"rgba(255, 159, 64, 0.2)",
],
borderColor: [
"rgba(255, 99, 132, 1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
],
borderWidth: 2,
},
],
},
options: {
responsive: false,
plugins:{
title: {
display: true,
text: chartTitle
}},
scales: {
y: {
beginAtZero: true,
},
xAxes: [{
ticks: {
fontColor: 'green'
}
}]
},
},
});
}
Doesn't have any effect
I googled the problem and many of them have listed the same thing as it is in the scales object. Still it isn't working. Any help appreciated
Thank you in advance.
I found the answer! In v3.4.1 or v3 and above chart js has different description.
Trying this worked. So this works
scales: {
y: {
beginAtZero: true,
ticks:{
color: "white"
}
},
x:{
ticks:{
color: "white"
}
}
}
Instead of the following code. The following works in previous versions.
scales: {
y: {
beginAtZero: true,
},
xAxes: [{
ticks: {
fontColor: 'green'
}
}]
}
Thank You.

Styling Muitextfield input background, hides label

I am trying to style Textfield from the global theme, but, I can't manage to put a colored background for the input only (white), whithout hiding the label when it move inside the input.
I want to have this result :
But, I have this one :
I put the white background with transparency, to show that the text is indeed behind it, but, if I set the transparency to no transparency, the label will be fully hidden behind the white background, like this :
Here is my theme :
export const testGlobal = createMuiTheme({
palette: {
common: {
black: "#000",
white: "rgba(255, 255, 255, 1)",
blue: {
light: "rgba(184, 244, 255, 1)",
default: "rgba(0, 219, 255, 1)",
dark: "rgba(0, 184, 213, 1)",
},
red: {
light: "#ff1744",
default: "#fd0031",
dark: "#e3002c",
},
orange: {
light: "#ffc046",
default: "#ff8f00",
dark: "#c56000",
},
grey: {
light: "rgba(99, 99, 99, 1)",
medium: "rgba(99, 99, 99, 1)",
paper: "rgba(65, 65, 65, 1)",
default: "rgba(99, 99, 99, 1)",
dark: "rgba(60, 60, 60, 1)",
},
},
},
});
testGlobal.root = {
borderColor: "rgba(85, 85, 85, 1)",
};
testGlobal.img = {
width: "79%",
};
testGlobal.palette = {
...testGlobal.palette,
vumeter: {
top: "#fd0031",
mid: "#ff8f00",
bottom: "rgba(0, 219, 255, 1)",
},
playlist: {
playing: "#ef5350",
},
background: {
light: "rgba(99, 99, 99, 1)",
medium: "rgba(99, 99, 99, 1)",
paper: "rgba(65, 65, 65, 1)",
default: "rgba(99, 99, 99, 1)",
dark: "rgba(60, 60, 60, 1)",
},
primary: {
light: "rgba(184, 244, 255, 1)",
main: "rgba(0, 219, 255, 1)",
dark: "rgba(0, 184, 213, 1)",
contrastText: "rgba(255, 255, 255, 1)",
},
text: {
primary: "rgba(255, 255, 255, 0.87)",
secondary: "rgba(255, 255, 255, 0.54)",
disabled: "rgba(255, 255, 255, 0.38)",
hint: "rgba(255, 255, 255, 0.38)",
},
};
testGlobal.overrides = {
MuiFormLabel: {
root: {
background: "black",
fontWeight: "bolder",
color: "grey",
"&.Mui-focused": { color: "grey" },
paddingLeft: theme.spacing(1),
},
},
MuiInput: {
root: {
fontWeight: "bolder",
background: "white", // the white background that keep hiding my label :(
padding: theme.spacing(1),
color: "black",
},
},
};
testGlobal.props = {
MuiInput: { disableUnderline: true },
};
I did not add additional styling to the textfield component
Why would a background get on top of the label? :(
Would be helpful if you could share a working code link on a JavaScript playground site such as codesandbox.io, however this sounds like it could be a z-index issue. Try to add something such as position: relative;z-index: 99; to the label. z-index controls the overlapping order on the page so this should work.

ChartJS: Two Titles for positive and negative part of y axis

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>

How to get the length (height) of the vertical bar in Chart.js

I have a vertical bar in my project (I use chart.js). I need to know its length/height (in pixels). I try to get it like this:
afterDraw: chart => {
var yAxis = chart.scales['y-axis-0'];
chart.config.data.datasets[0].data.forEach((value, index) => {
console.log(Math.round(yAxis.getPixelForValue(value)));
});
}
But I got incorrect values. For horizontal bar it's work correctly (with var xAxis = chart.scales['x-axis-0']).
What the problem? How to do it?
Try using getDatasetMeta() to get the metadata.
var meta = myChart.getDatasetMeta(0);
var height = meta.data[0]._model.height;
The origin (value zero) of the y-axis is at the top of the canvas. Therefore, you need to subtract the value obtained through yAxis.getPixelForValue(value) from yAxis.bottom as follows:
afterLayout: chart => {
var yAxis = chart.scales['y-axis-0'];
chart.data.datasets[0].data.forEach(value => {
console.log(Math.round(yAxis.bottom - yAxis.getPixelForValue(value)));
});
}
Please take a look at below sample and see how it works (code is derived from the Chart.js Bar documentation page).
new Chart('myChart', {
type: "bar",
plugins: [{
afterLayout: chart => {
var yAxis = chart.scales['y-axis-0'];
chart.data.datasets[0].data.forEach(value => {
console.log(Math.round(yAxis.bottom - yAxis.getPixelForValue(value)));
});
}
}],
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
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)", "rgba(54, 162, 235, 0.2)", "rgba(153, 102, 255, 0.2)", "rgba(201, 203, 207, 0.2)"],
borderColor: ["rgb(255, 99, 132)", "rgb(255, 159, 64)", "rgb(255, 205, 86)", "rgb(75, 192, 192)", "rgb(54, 162, 235)", "rgb(153, 102, 255)", "rgb(201, 203, 207)"],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
min: 0,
stepSize: 20
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>

Chart.js - Writing Labels Inside of Horizontal Bars?

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.

Categories