I would like to create a horizontal BarChart.
The data range is from 0 to 20 or from 0 to -20.
The labels of the data should always be on the opposite side of the bar.
I also need additional text on the left and right.
The picture should make it clear:
I would like to do it with Chart.js.
edit:
This is my approach with Chart.js:
var ctx = document.getElementById("myChart");
var chartStatus = Chart.getChart("myChart");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["Cats", "Dogs", "Dugs", "Mice", "Hamster", "Tiger"],
datasets: [
{
label: false,
data: [-9, 1, 9, -9, 1, 9],
// data: [12, 19, -3],
backgroundColor: [
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)",
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)"
],
borderColor: [
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)",
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)"
],
borderWidth: 6
}
]
},
options: {
responsive: true,
indexAxis: "y",
plugins: {
gridLines: {
display: false
},
title: {
display: true,
text: "Pets",
font: {
size: 22
}
},
legend: {
display: false
},
tooltip: {
enabled: false
}
},
scales: {
x: {
min: -20,
max: 20,
gridLines: {
display: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
y: {
labels: ["Text1", "Text2", "Text3", "Text4", "Text5", "Text6"],
gridLines: {
display: false
},
barPercentage: 1,
gridLines: {
display: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
gridLines: {
display: false
},
secondY: {
labels: ["Text7", "Text8", "Text9", "Text10", "Text11", "Text12"],
position: "right",
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="myChart"></canvas>
<img style="display:none" id="url" />
I could solve the different text for the left and the right side with the labels on the respective axes.
But how do I get the labels (cat, dog etc) into the graph so that they are always on the opposite side of the bars?
Solution:
var ctx = document.getElementById("myChart");
var chartStatus = Chart.getChart("myChart");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["Cats", "Dogs", "Dugs", "Mice", "Hamster", "Tiger"],
datasets: [
{
label: false,
data: [-9, 1, 9, -9, 1, 9],
// data: [12, 19, -3],
backgroundColor: [
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)",
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)"
],
borderColor: [
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)",
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)"
],
borderWidth: 6
}
]
},
options: {
animation: {
duration: 1,
// https://github.com/jtblin/angular-chart.js/issues/605
onComplete: function () {
var chart = this;
var ctx = chart.ctx;
ctx.font = Chart.helpers.fontString(
Chart.defaults.font.size,
Chart.defaults.font.style,
Chart.defaults.font.family
);
ctx.textAlign = "left";
ctx.textBaseline = "censter";
var labels = this.data.labels;
this.data.datasets.forEach(function (dataset, i) {
var meta = chart.getDatasetMeta(i);
meta.data.forEach(function (bar, index) {
var data = dataset.data[index];
let posx;
if (data < 0) {
posx = bar.x + bar.$context.element.width + 10;
ctx.textAlign = "left";
} else {
posx = bar.x - bar.$context.element.width - 10;
ctx.textAlign = "right";
}
// posx = bar.x;
ctx.fillText(`${labels[index]} (${data})`, posx, bar.y + 3);
});
});
}
},
responsive: true,
indexAxis: "y",
plugins: {
title: {
display: true,
text: "Pets",
font: {
size: 22
}
},
legend: {
display: false
},
tooltip: {
enabled: false
}
},
scales: {
x: {
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
labels: {
display: false
},
min: -20,
max: 20,
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
y: {
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
labels: ["Text1", "Text2", "Text3", "Text4", "Text5", "Text6"],
gridLines: {
display: false
},
barPercentage: 1,
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
secondY: {
labels: ["Text7", "Text8", "Text9", "Text10", "Text11", "Text12"],
position: "right",
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="myChart"></canvas>
Related
I'm currently using chart.js with Vue 2 and I'm facing a problem when using border-radius with stacked bars.
I want to have a border-top radius but when doing that I get a "gap" between the border that was created and the bar above.
Is there any option that can fill that empty space?
Current behaviour
Expected behaviour
These are the current options I'm using for the graph.
options: {
responsive: true,
lineTension: 1,
plugins: {
legend: {
position: 'bottom',
// prevent filtering using labels
'onClick' : function (evt, item, legend) {
return;
},
labels: {
usePointStyle: 'circle',
},
}
},
elements: {
bar: {
borderRadius: {
topLeft: 4.6,
topRight: 4.6,
},
}
},
scales: {
y: {
stacked: true,
ticks: {
maxTicksLimit: 6,
callback: function(value, index, ticks) {
return value + ' min';
}
},
},
x: {
stacked: true,
grid: {
color: "rgba(0, 0, 0, 0)",
}
}
}
}
What you need it is not configurable by any options.
Nevertheless you can use a plugin to change the base of the bar to draw, taking care of the border radius.
EDIT: I have found a specific option in bar element, base, which should address the use case.
See snippet:
const ctx = document.getElementById("myChart");
// not used but leave it
const plugin = {
id: 'fillGaps',
beforeDatasetDraw(chart, args) {
if (args.index > 0) {
args.meta.data.forEach(function(item){
const pre = item.base;
item.base += 12;
});
}
},
};
//
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['January', 'Fabruary', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'user 1 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'rgba(40, 139, 170, 1)',
borderWidth: 0,
borderSkipped: 'start',
base: 0
},
{
label: 'user 2 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'orange',
borderWidth: 0,
borderSkipped: 'start',
base: 0
}]
},
options: {
elements: {
bar: {
borderRadius: {
topLeft: 12,
topRight: 12,
bottomLeft: 12,
bottomRight: 12
},
}
},
scales: {
y: {
stacked: true,
},
x: {
stacked: true,
}
}
}
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.9.1/dist/chart.min.js"></script>
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"/>
</div>
</body>
</html>
I have the below ChartJS code and I would like to hide the grid vertical lines only and not the horizontal ones and keep only one vertical line per two like in the capture below:
image]1
Thanks.
<canvas id="line-chart" width="800" height="450"></canvas>
new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050],
datasets: [{
data: [86,114,106,106,107,111,133,221,783,2478],
label: "Africa",
borderColor: "#3e95cd",
fill: false
}, {
data: [282,350,411,502,635,809,947,1402,3700,5267],
label: "Asia",
borderColor: "#8e5ea2",
fill: false
}, {
data: [168,170,178,190,203,276,408,547,675,734],
label: "Europe",
borderColor: "#3cba9f",
fill: false
}, {
data: [40,20,10,16,24,38,74,167,508,784],
label: "Latin America",
borderColor: "#e8c3b9",
fill: false
}, {
data: [6,3,2,2,7,26,82,172,312,433],
label: "North America",
borderColor: "#c45850",
fill: false
}
]
},
options: {
title: {
display: true,
text: 'World population per region (in millions)'
}
}
});
Just add grid color as function in options for x axis scale and conditionally change the colors like its done below:
var chartInstance = new Chart(document.getElementById("chartJSContainer"), {
type: 'line',
data: {
labels: [1500, 1600, 1700, 1750, 1800, 1850, 1900, 1950, 1999, 2050],
datasets: [{
data: [86, 114, 106, 106, 107, 111, 133, 221, 783, 2478],
label: "Africa",
borderColor: "#3e95cd",
fill: false
}, {
data: [282, 350, 411, 502, 635, 809, 947, 1402, 3700, 5267],
label: "Asia",
borderColor: "#8e5ea2",
fill: false
}, {
data: [168, 170, 178, 190, 203, 276, 408, 547, 675, 734],
label: "Europe",
borderColor: "#3cba9f",
fill: false
}, {
data: [40, 20, 10, 16, 24, 38, 74, 167, 508, 784],
label: "Latin America",
borderColor: "#e8c3b9",
fill: false
}, {
data: [6, 3, 2, 2, 7, 26, 82, 172, 312, 433],
label: "North America",
borderColor: "#c45850",
fill: false
}]
},
options: {
title: {
display: true,
text: 'World population per region (in millions)'
},
scales: {
x: {
grid: {
display: true, //Make it false to remove all the vertical lines.
color: function(context) {
if (context.tick.value %2 == 0) {
return 'gray';
} else {
return 'white';
}
},
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.3.1/dist/chart.js"></script>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
I don’t really use chart.js so there may be a better way to do this. I think there may be a built in way to use a callback for this but I couldn’t figure it out. Here’s what does work.
Add this function at the top to create an array of alternating colors.
let colors = [];
function gridColors() {
for (let i=0; i<10; i++) {
colors.push("rgba(0,0,0,0)")
colors.push("rgba(0,0,0,0.1)")
}
}
gridColors()
And in your options add the following
options: {
title: {
display: true,
text: 'World population per region (in millions)'
},
scales: {
x: {
grid: {
beginAtZero: true,
color: colors,
}
}
}
}
});
Please if you have successfully worked on months or other time like years days in Django chartjs, please kindly share a link where I can see how it is utilized. As I can't figure why mine isn't working, the moment I put the x-axis it doesn't show any data.
But if I remove the x-axis part, it works perfectly. my main aim is to make the months display so I can get some data into it. Any help would be really appreciated.
Below is the updated script snippet
<script>
$(document).ready(function(){
var ctx2 = document.getElementById("myChart2");
var myChart2 = new Chart(ctx2, {
type: "bar",
data: {
datasets: [
{
label: "US Dates",
data: [
{
x: "04/01/2014",
y: 175,
},
{
x: "10/01/2014",
y: 175,
},
{
x: "04/01/2015",
y: 178,
},
{
x: "10/01/2015",
y: 178,
},
],
backgroundColor: "rgba(255, 99, 132, 0.2)",
borderColor: "rgba(255, 99, 132, 1)",
borderWidth: 2,
},
{
label: "UK Dates",
data: [
{
x: "04/01/2014",
y: 143,
},
{
x: "10/1/2014",
y: 175,
},
{
x: "04/01/2015",
y: 165,
},
{
x: "10/1/2015",
y: 178,
},
],
backgroundColor: "rgba(99, 255, 132, 0.2)",
borderColor: "rgba(99, 255, 132, 1)",
borderWidth: 2,
},
],
},
options: {
responsive: true,
title: {
display: true,
text: "Chart.js Time Scale",
},
scales: {
xAxes: [
{
type: "time",
position: "bottom",
time: {
displayFormats: { day: "MM/YY" },
tooltipFormat: "DD/MM/YY",
unit: "month",
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: "value",
},
},
],
},
},
});
})
</script>
There is not problem with option for x-axis.
I bet there is a problem with dataset.
Dataset of x and y should be specified.
Following snippet will help you.
var ctx2 = document.getElementById("myChart2");
var myChart = new Chart(ctx2, {
type: "bar",
data: {
datasets: [
{
label: "US Dates",
data: [
{
x: "04/01/2014",
y: 175,
},
{
x: "10/01/2014",
y: 175,
},
{
x: "04/01/2015",
y: 178,
},
{
x: "10/01/2015",
y: 178,
},
],
backgroundColor: "rgba(255, 99, 132, 0.2)",
borderColor: "rgba(255, 99, 132, 1)",
borderWidth: 2,
},
{
label: "UK Dates",
data: [
{
x: "04/01/2014",
y: 143,
},
{
x: "10/1/2014",
y: 175,
},
{
x: "04/01/2015",
y: 165,
},
{
x: "10/1/2015",
y: 178,
},
],
backgroundColor: "rgba(99, 255, 132, 0.2)",
borderColor: "rgba(99, 255, 132, 1)",
borderWidth: 2,
},
],
},
options: {
responsive: true,
title: {
display: true,
text: "Chart.js Time Scale",
},
scales: {
xAxes: [
{
type: "time",
position: "bottom",
time: {
displayFormats: { day: "MM/YY" },
tooltipFormat: "DD/MM/YY",
unit: "month",
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: "value",
},
},
],
},
},
});
I have a strange issue. I'm getting the date from the database. All visualization was good. Then I deleted one record and add another one on the same date and time. But that record moved on the right in the chart instead to be ordered on left:
The js code is this:
$(document).ready(function(){
$.ajax({
url: "js/json/otv_fil_iskar/data.php",
method: "POST",
success: function(data) {
console.log(data);
var progress = document.getElementById('animationProgress');
var dateANDtime = [];
var Gblok9osx = [];
var Gblok9osy = [];
var Gblok11osx = [];
var Gblok11osy = [];
var Gfilblok10 = [];
var Gfilblok11 = [];
var Gcvn = [];
var Gndk2 = [];
for(var i in data) {
dateANDtime.push(data[i].to_char);
Gblok9osx.push(data[i].blok9osx);
Gblok9osy.push(data[i].blok9osy);
Gblok11osx.push(data[i].blok11osx);
Gblok11osy.push(data[i].blok11osy);
Gfilblok10.push(data[i].filblok10);
Gfilblok11.push(data[i].filblok11);
Gcvn.push(data[i].cvn);
Gndk2.push(data[i].ndk2);
}
var chartdata = {
labels: dateANDtime,
datasets : [
{fill: false,
label: 'Отвес блок 9 x',
backgroundColor: 'rgba(199, 228, 238, 0.75)',
borderColor: 'rgba(1, 150, 200, 0.75)',
//hoverBackgroundColor: 'rgba(200, 200, 200, 0.75)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: Gblok9osx,
hidden: true
},
{fill: false,
label: 'Отвес блок 9 y',
backgroundColor: 'rgba(163, 147, 222, 0.75)',
borderColor: 'rgba(50, 10, 200, 0.75)',
//hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: Gblok9osy,
hidden: true
},
{fill: false,
label: 'Отвес блок 11 x',
backgroundColor: 'rgba(221, 221, 241, 0.75)',
borderColor: 'rgba(100, 100, 200, 0.75)',
//hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: Gblok11osx,
hidden: true
},
{fill: false,
label: 'Отвес блок 11 y',
backgroundColor: 'rgba(147, 227, 227, 0.75)',
borderColor: 'rgba(1, 200, 200, 0.75)',
//hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: Gblok11osy,
hidden: true
},
{fill: false,
label: 'Филтрация блок 10',
backgroundColor: 'rgba(139, 105, 132, 0.75)',
borderColor: 'rgba(255, 1, 200, 0.75)',
//hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: Gfilblok10,
hidden: true
},
{fill: false,
label: 'Филтрация блок 11',
backgroundColor: 'rgba(0, 200, 1, 1)',
borderColor: 'rgba(0, 101,1, 0.75)',
hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(50, 50, 50, 1)',
data: Gfilblok11,
hidden: true
},
{fill: false,
label: 'КВН',
backgroundColor: 'rgba(30, 100, 100, 1)',
borderColor: 'rgba(100, 200,1, 0.75)',
hoverBackgroundColor: 'rgba(101, 200, 200, 1)',
hoverBorderColor: 'rgba(50, 50, 50, 1)',
data: Gcvn,
hidden: false
},
{fill: false,
label: 'Ниво дренажен кладенец',
backgroundColor: 'rgba(30, 100, 100, 1)',
borderColor: 'rgba(100, 200,1, 0.75)',
hoverBackgroundColor: 'rgba(101, 200, 200, 1)',
hoverBorderColor: 'rgba(50, 50, 50, 1)',
data: Gndk2,
hidden: true
}
]
};
$('#reset_zoom').click(function() {
barGraph.resetZoom();
})
$("#save-btn").click(function() {
$("#mycanvas").get(0).toBlob(function(blob) {
saveAs(blob, "chart_1.png");
});
});
var ctx = document.getElementById("mycanvas");
var barGraph = new Chart(ctx, {
type: 'line',
data: chartdata,
options: {
title: {
display: true,
text: 'Диаграма - Отвеси и филтрации'
},
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: '[mm] , [l/s]'
},
ticks: {
beginAtZero:false
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Дата/Час'
},
ticks: {
beginAtZero:true
}
}]
},
// Container for pan options
pan: {
// Boolean to enable panning
enabled: true,
// Panning directions. Remove the appropriate direction to disable
// Eg. 'y' would only allow panning in the y direction
mode: 'y'
},
// Container for zoom options
zoom: {
// Boolean to enable zooming
enabled: true,
// Zooming directions. Remove the appropriate direction to disable
// Eg. 'y' would only allow zooming in the y direction
mode: 'xy'
},
animation: {
duration: 2000,
onProgress: function(animation) {
progress.value = animation.currentStep / animation.numSteps;
},
onComplete: function() {
window.setTimeout(function() {
progress.value = 0;
}, 2000);
}
},
legend: {
display: true,
labels: {
//fontColor: 'rgb(255, 99, 132)'
usePointStyle: true
}
}
},
plugins: [{
beforeDraw: function(c) {
var reset_zoom = document.getElementById("reset_zoom"); //reset button
var ticks = c.scales['x-axis-0', 'y-axis-0'].ticks.length; //x-axis ticks array
var labels = c.data.labels.length; //labels array
if (ticks < labels) reset_zoom.hidden = false;
else reset_zoom.hidden = true;
}
}]
}); //end data
},
error: function(data) {
console.log(data);
}
});
});
The same think is happening when i don't have records in particular time of the day.
I changed xAxes option in the same graph and it's similar output:
xAxes: [{
type: 'time',
time: {
displayFormats: {
'hour': 'DD MMM YYYY г. HH:MM:SS ч.'
}
},
scaleLabel: {
display: true,
labelString: 'Дата/Час'
},
ticks: {
beginAtZero: true
}
}]
The order in the dataset is important because chart.js does not do any sorting of the values. Make sure that you fetch data from the database ordered by date and not id which usually is default.
for example, when i load the page, i would like to visually appear slowly, instead of just popups already loaded
For example, when i load this bar graph, everything appears slowly with the nice visualization onto the screen, i think it adds a nice affect
this is my script
<script>
var department = {{department|safe}};
var incomplete = {{incomplete|safe}};
var complete = {{complete|safe}};
// Return with commas in between
var numberWithCommas = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
// Chart.defaults.global.elements.rectangle.backgroundColor = '#FF0000';
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: department,
datasets: [
{
label: 'incomplete',
data: incomplete,
backgroundColor: "rgba(55, 160, 225, 0.7)",
hoverBackgroundColor: "rgba(55, 160, 225, 0.7)",
hoverBorderWidth: 2,
hoverBorderColor: 'lightgrey'
},
{
label: 'complete',
data: complete,
backgroundColor: "rgba(225, 58, 55, 0.7)",
hoverBackgroundColor: "rgba(225, 58, 55, 0.7)",
hoverBorderWidth: 2,
hoverBorderColor: 'lightgrey'
},
]
},
options: {
animation: {
duration: 10,
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: { display: false },
}],
yAxes: [{
stacked: true,
ticks: {
callback: function (value) { return numberWithCommas(value); },
},
}],
}, // scales
legend: { display: true }
} // options
}
);
</script>
In the options object, there is an animation parameter:
animation: {
duration: 10,
},
I believe this is what you want.