So I've designed a stacked bar chart that count the numbers of open issues in our system. But I would need to put 2 red grid lines when these issues are open for too long.
Options:
OrderAgeOptions = {
observeChanges: true, throttle: 100,
title: { display: true, text: "Leeftijd openstaande tickets", fontFamily: 'ASSA Vesta Light', fontSize: 20, },
legend: { display: true, labels: { fontFamily: 'Open Sans', }, position: "top", },
maintainAspectRatio: false,
tooltips: { enabled: false },
hover: { animationDuration: 0 },
scales: {
xAxes: [{ ticks: { beginAtZero: true, fontFamily: "'Open Sans Bold', sans-serif", fontSize: 11, max: 175 }, scaleLabel: { display: true }, gridLines: {}, stacked: true }],
yAxes: [{ ticks: { fontFamily: "'Open Sans Bold', sans-serif", fontSize: 11 }, gridLines: { display: true, }, stacked: true }]
},
animation: { duration: 0.1, onComplete: function () { var chartInstance = this.chart; var ctx = chartInstance.ctx; ctx.textAlign = "right"; ctx.font = "10px Open Sans"; ctx.fillStyle = "#fff"; Chart.helpers.each(this.data.datasets.forEach(function (dataset, i) { var meta = chartInstance.controller.getDatasetMeta(i); Chart.helpers.each(meta.data.forEach(function (bar, index) { data = dataset.data[index]; if (data != 0) ctx.fillText(data, bar._model.x - 2, bar._model.y - 7); }), this) }), this); } },
pointLabelFontFamily: "Open Sans",
scaleFontFamily: "Open Sans",
};
This results into this:
What I need:
Does anyone have an idea how to accomplish this? All I found till now was to change the appearance of all grid lines, not specific ones.
changing the color of a specific grid line is not possible without changing or extending the chart.js library itself as far as I know.
Below is an example of how to extend the library to your needs.
//Build the chart
var data = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [
{
label: "My First dataset",
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: 1,
data: [65, 59, 80, 81, 56, 55, 40],
}
]
};
//Load Chart
var ctx = $("#myChart");
var myBarChart = new Chart(ctx, {
type: 'horizontalBar',
data: data,
options: {
//Set the index of the value where you want to draw the line
lineAtIndex: 60,
legend: {
display: false
}
}
});
//Create horizontalBar plug-in for ChartJS
var originalLineDraw = Chart.controllers.horizontalBar.prototype.draw;
Chart.helpers.extend(Chart.controllers.horizontalBar.prototype, {
draw: function () {
originalLineDraw.apply(this, arguments);
var chart = this.chart;
var ctx = chart.chart.ctx;
var index = chart.config.options.lineAtIndex;
if (index) {
var xaxis = chart.scales['x-axis-0'];
var yaxis = chart.scales['y-axis-0'];
var barHeight = chart.scales['x-axis-0'].height
var x1 = xaxis.getPixelForValue(0);
var y1 = yaxis.getPixelForValue('April') - chart.scales['x-axis-0'].height;
var x2 = xaxis.getPixelForValue(85);
var y2 = yaxis.getPixelForValue('April') - chart.scales['x-axis-0'].height;
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.strokeStyle = 'red';
ctx.lineTo(x2, y2);
ctx.stroke();
var xaxis = chart.scales['x-axis-0'];
var yaxis = chart.scales['y-axis-0'];
var x1 = xaxis.getPixelForValue(0);
var y1 = yaxis.getPixelForValue('May') - 30;
var x2 = xaxis.getPixelForValue(85);
var y2 = yaxis.getPixelForValue('May') - 30 ;
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.strokeStyle = 'red';
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.restore();
}
}
});
<canvas id="myChart"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js'></script>
Related
I've been using ChartJS with PrimeNG, in Angular. Need to make a doughnut chart with i believe one dataset. I need to make it so each value has a different thickness, like this
So far I've tried a lot of things and read a lot of ChartJS documentation on Doughnut charts, but none of the options have helped me.
Here's how I implement my chart in HTML
<p-chart type="doughnut" [data]="donutData" [options]="donutChartOptions" class="h-10 my-4"></p-chart>
And here's the .ts to it
this.donutData = {
labels: ['A', 'B', 'C'],
datasets: [
{
data: [300, 50, 100],
backgroundColor: ['#F36F56', '#FFC300', '#B8A3FF'],
hoverBackgroundColor: ['#F36F56', '#FFC300', '#B8A3FF'],
},
],
};
this.donutChartOptions = {
cutout: 50,
plugins: {
legend: {
display: false,
labels: {
color: '#ebedef',
},
},
},
};
Here you can find the answer of your question: https://github.com/chartjs/Chart.js/issues/6195
I transferred the answer of "ex47" to chart.js 3
I put the constant "data" into the html file just to have less double code, it should better be in the javascript file.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.9.1/dist/chart.min.js"></script>
<script>
const data = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "# of Votes",
data: [12, 19, 3, 5, 2, 3],
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: 1
}
]
}
</script>
<style>
#chartWrapper {
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<div id="chartWrapper">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
</body>
<script src="myChart.js"></script>
</html>
myChart.js
var thickness = {
id: "thickness",
beforeDraw: function (chart, options) {
let thickness = chart.options.plugins.thickness.thickness;
thickness.forEach((item,index) => {
chart.getDatasetMeta(0).data[index].innerRadius = item[0];
chart.getDatasetMeta(0).data[index].outerRadius = item[1];
});
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "doughnut",
plugins: [thickness],
data: data,
options: {
plugins: {
thickness: {
thickness: [[100,130],[80,150],[70,160],[100,130],[100,130],[100,130]],
}
},
}
});
"Spirit04eK"'s solution sets the thickness in descending order of the magnitude of the value
myChart.js
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: 'doughnut',
plugins: [
{
beforeDraw: function (chart) {
const datasetMeta = chart.getDatasetMeta(0);
const innerRadius = datasetMeta.controller.innerRadius;
const outerRadius = datasetMeta.controller.outerRadius;
const heightOfItem = outerRadius - innerRadius;
const countOfData = chart.getDatasetMeta(0).data.length;
const additionalRadius = Math.floor(heightOfItem / countOfData);
const weightsMap = datasetMeta.data
.map(v => v.circumference)
.sort((a, b) => a - b)
.reduce((a, c, ci) => {
a.set(c, ci + 1);
return a;
}, new Map());
datasetMeta.data.forEach(dataItem => {
const weight = weightsMap.get(dataItem.circumference);
dataItem.outerRadius = innerRadius + additionalRadius * weight;
});
}
}
],
data: data,
options: {
layout: {
padding: 10,
},
plugins: {
legend: false,
datalabels: {
display: false
},
},
maintainAspectRatio: false,
responsive: true,
}
});
I have implemented a graph with four horizontal bars. Last one has nearly 35k records so the stepSize automatically is 2250. The first bar has only 20 records.
First bar's 20 records are not showing any color as the numbers are less at compare to stepSize 2250.
This is my code
scales: {
xAxes: [
{
ticks: {
beginAtZero: true,
stepSize: 50,
},
stacked: true
}
],
yAxes: [
{
ticks: {
fontSize: 12
},
stacked: true
}
]
},
animation: {
onComplete: function() {
var chartInstance = this.chart;
var ctx = chartInstance.ctx;
ctx.textAlign = "left";
ctx.fillStyle = "#fff";
//draw total count
charData.datasets[0].data.forEach(function(data, index) {
var total = this.data.datasets[0].data[index];
var meta = chartInstance.controller.getDatasetMeta(0);
var posX = meta.data[index]._model.x;
var posY = meta.data[index]._model.y;
ctx.fillStyle = "black";
if(total.toString().length>=5)
ctx.fillText(total, posX -40, posY + 2);
else if(total==0)
ctx.fillText(total, posX -4, posY + 4);
else
ctx.fillText(total, posX - 10, posY + 4);
}, this);
}
This is output
How can I fix this issue?
Your problem is not related to ticks.stepSize, this option simply controls how to create the ticks but doesn't change the size of the bars.
You can define the x-axis as a logarithmic cartesian axis as shown in the runnable code snippet below.
new Chart('myChart', {
type: 'horizontalBar',
data: {
labels: ['0-12 hr', '12-24 hr', '1-3 day', '3-15 day'],
datasets: [{
label: '',
data: [20, 0, 0, 34343],
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: {
legend: {
display: false
},
scales: {
xAxes: [{
type: 'logarithmic',
ticks: {
beginAtZero: true,
userCallback: (value, index) => {
const remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (remain == 1 || remain == 2 || remain == 5 || index == 0) {
return value.toLocaleString();
}
return '';
}
},
gridLines: {
display: false
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<canvas id="myChart" height="150"></canvas>
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>
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>