I'm using chart.js API to render a several stacked vertical bars chart, but the performance is slow. I even made some changes such that all the content object was already processed by the server, and not the browser, but I realised the big majority of time comes from the final function new Chart(overallStatsChart, content);.
I also tried switching off animation, which slightly improved performance, but not that much.
Any ideas on how to improve performance, that is, initial loading?
var countryList = {"AR":"Argentina","AU":"Australia","BO":"Bolivia","BR":"Brasil","CA":"Canada","CL":"Chile","CN":"中国","CO":"Colombia","CR":"Costa Rica","CU":"Cuba","CZ":"Česká","DE":"Deutschland","DK":"Danmark","DO":"Rep. Dominicana","EC":"Ecuador","ES":"España","FI":"Suomessa","FR":"France","GR":"Ελλάδα","GT":"Guatemala","HU":"Magyarország","IE":"Ireland","IN":"India","IT":"Italia","JP":"日本","MX":"México","NI":"Nicaragua","NL":"Nederland","NO":"Norge","PA":"Panamá","PE":"Perú","PL":"Polska","PR":"Puerto Rico","PT":"Portugal","PY":"Paraguay","RO":"România","RU":"Россия","SE":"Sverige","SV":"El Salvador","TR":"Türkiye","UA":"Україна","UK":"United Kingdom","US":"USA","UY":"Uruguay","VE":"Venezuela"};
//populates country labels
var labels = [], size = 0;
for (var key in countryList){
labels.push(key);
size++;
}
var _data = function(i){
var arr = [];
for (var n=0; n<size; n++){
arr.push(n==i ? 1 : 0);
}
return arr;
};
var dataset = [], i=0;
for (var key in countryList){
dataset.push(
{
label: "depreciation",
data: _data(i),
backgroundColor: 'navy'
}, {
label: "insurance",
data: _data(i),
backgroundColor: 'blue'
}, {
label: "credit",
data: _data(i),
backgroundColor: 'aqua'
}, {
label: "inspection",
data: _data(i),
backgroundColor: 'teal'
}, {
label: "road taxes",
data: _data(i),
backgroundColor: 'olive'
}, {
label: "maintenance",
data: _data(i),
backgroundColor: 'green'
}, {
label: "repairs",
data: _data(i),
backgroundColor: 'lime'
}, {
label: "fuel",
data: _data(i),
backgroundColor: 'maroon'
}, {
label: "parking",
data: _data(i),
backgroundColor: 'yellow'
}, {
label: "tolls",
data: _data(i),
backgroundColor: 'orange'
}, {
label: "fines",
data: _data(i),
backgroundColor: 'red'
}, {
label: "washing",
data: _data(i),
backgroundColor: 'purple'
}, {
label: "maintenance",
data: _data(i),
backgroundColor: 'green'
}
);
i++;
}
var options = {
maintainAspectRatio: false,
legend: {
position: 'bottom', // place legend on the right side of chart
display: false, //do not display
labels : {
fontSize: 9,
fontColor: 'black'
}
},
scales: {
xAxes: [{
stacked: true, // this should be set to make the bars stacked
beginAtZero: true
}],
yAxes: [{
stacked: true, // this also..
beginAtZero: true
}]
},
animation: {
duration : 1000,
easing : 'linear'
}
};
var content = {
type: 'bar',
data: {
labels: labels,
datasets: dataset
},
options: options
};
//I made tests with timestamps and here it takes the biggest part of the time
new Chart(overallStatsChart, content);
.chart {
position: relative;
margin: auto;
}
#overallStatsChartDiv{
min-height: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<div class="chart" id="overallStatsChartDiv">
<canvas id="overallStatsChart"></canvas>
</div>
Now I get it :)
I was wrongly duplicating the data attributes, based on this answer.
Solution is quite simple, every data attribute can have directly all the values, without the need of introduction of zeros.
var countryList = {"AR":"Argentina","AU":"Australia","BO":"Bolivia","BR":"Brasil","CA":"Canada","CL":"Chile","CN":"中国","CO":"Colombia","CR":"Costa Rica","CU":"Cuba","CZ":"Česká","DE":"Deutschland","DK":"Danmark","DO":"Rep. Dominicana","EC":"Ecuador","ES":"España","FI":"Suomessa","FR":"France","GR":"Ελλάδα","GT":"Guatemala","HU":"Magyarország","IE":"Ireland","IN":"India","IT":"Italia","JP":"日本","MX":"México","NI":"Nicaragua","NL":"Nederland","NO":"Norge","PA":"Panamá","PE":"Perú","PL":"Polska","PR":"Puerto Rico","PT":"Portugal","PY":"Paraguay","RO":"România","RU":"Россия","SE":"Sverige","SV":"El Salvador","TR":"Türkiye","UA":"Україна","UK":"United Kingdom","US":"USA","UY":"Uruguay","VE":"Venezuela"};
//populates country labels
var labels = [], size = 0;
for (var key in countryList){
labels.push(key);
size++;
}
var _data = function(){
var arr = [];
for (var n=0; n<size; n++){
arr.push(1);
}
return arr;
};
var dataset = [
{
label: "depreciation",
data: _data(),
backgroundColor: 'navy'
}, {
label: "insurance",
data: _data(),
backgroundColor: 'blue'
}, {
label: "credit",
data: _data(),
backgroundColor: 'aqua'
}, {
label: "inspection",
data: _data(),
backgroundColor: 'teal'
}, {
label: "road taxes",
data: _data(),
backgroundColor: 'olive'
}, {
label: "maintenance",
data: _data(),
backgroundColor: 'green'
}, {
label: "repairs",
data: _data(),
backgroundColor: 'lime'
}, {
label: "fuel",
data: _data(),
backgroundColor: 'maroon'
}, {
label: "parking",
data: _data(),
backgroundColor: 'yellow'
}, {
label: "tolls",
data: _data(),
backgroundColor: 'orange'
}, {
label: "fines",
data: _data(),
backgroundColor: 'red'
}, {
label: "washing",
data: _data(),
backgroundColor: 'purple'
}, {
label: "maintenance",
data: _data(),
backgroundColor: 'green'
}
];
var options = {
maintainAspectRatio: false,
legend: {
position: 'bottom', // place legend on the right side of chart
display: false, //do not display
labels : {
fontSize: 9,
fontColor: 'black'
}
},
scales: {
xAxes: [{
stacked: true, // this should be set to make the bars stacked
beginAtZero: true
}],
yAxes: [{
stacked: true, // this also..
beginAtZero: true
}]
},
animation: {
duration : 1000,
easing : 'linear'
}
};
var content = {
type: 'bar',
data: {
labels: labels,
datasets: dataset
},
options: options
};
//I made tests with timestamps and here it takes the biggest part of the time
new Chart(overallStatsChart, content);
.chart {
position: relative;
margin: auto;
}
#overallStatsChartDiv{
min-height: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<div class="chart" id="overallStatsChartDiv">
<canvas id="overallStatsChart"></canvas>
</div>
Related
I'm trying to make a simple horizontal stacked bar chart and add labels to it. I'm not sure what I'm doing wrong here, any input would be appreciated.
I can make it work by downgrading to a much lower version of chart.js and the datalabel plugin.
Here is a jsfiddle:
https://jsfiddle.net/chrispret/szLgyu2j/24/
And some test code...
const testData = {
set1: '4',
set2: '3',
set3: '1',
set4: '3',
};
const data = {
labels: ["test"],
datasets: [
{
label: 'set1',
data: [testData.set1],
backgroundColor: 'red',
datalabels: {
anchor: 'center',
align: 'center',
}
},
{
label: 'set2',
data: [testData.set2],
backgroundColor: 'blue',
datalabels: {
anchor: 'center',
align: 'center',
}
},
{
label: 'set3',
data: [testData.set3],
backgroundColor: 'green',
datalabels: {
anchor: 'center',
align: 'center',
}
},
{
label: 'set4',
data: [testData.set4],
backgroundColor: 'gray',
datalabels: {
anchor: 'center',
align: 'center',
}
}
]
};
const config = {
type: 'bar',
data: data,
options: {
aspectRatio: 15 / 3,
indexAxis: 'y',
elements: {
bar: {
borderWidth: 2,
}
},
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
},
plugins: {
datalabels: {
color: 'white',
font: {
weight: 'bold'
},
formatter: Math.round
},
legend: {
position: 'right',
},
title: {
display: true,
text: 'Chart.js Horizontal Bar Chart'
},
}
},
};
const myChart = new Chart(
document.getElementById('chartDemo'),
config
);
Since version 1.x, the chartjs-plugin-datalabels plugin no longer registers itself automatically. It must be manually registered either globally or locally as described here.
Therefore, to make it work, you could add the following line to your code prior to create the chart.
Chart.register(ChartDataLabels);
Please take a look at your amended code and see how it works.
const testData = {
set1: '4',
set2: '3',
set3: '1',
set4: '3',
};
const data = {
labels: ["test"],
datasets: [{
label: 'set1',
data: [testData.set1],
backgroundColor: 'red'
},
{
label: 'set2',
data: [testData.set2],
backgroundColor: 'blue'
},
{
label: 'set3',
data: [testData.set3],
backgroundColor: 'green',
datalabels: {
anchor: 'center',
align: 'center'
}
},
{
label: 'set4',
data: [testData.set4],
backgroundColor: 'gray'
}
]
};
const config = {
type: 'bar',
data: data,
options: {
aspectRatio: 15 / 3,
indexAxis: 'y',
elements: {
bar: {
borderWidth: 2
}
},
responsive: true,
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
},
plugins: {
datalabels: {
anchor: 'center',
align: 'center',
color: 'white',
font: {
weight: 'bold'
},
formatter: Math.round
},
legend: {
position: 'right'
},
title: {
display: true,
text: 'Chart.js Horizontal Bar Chart'
},
}
},
};
Chart.register(ChartDataLabels);
const myChart = new Chart(
document.getElementById('chartDemo'),
config
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0/chartjs-plugin-datalabels.min.js"></script>
<canvas id="chartDemo"></canvas>
Add the labels to the labels array.
const data = {
labels: ["hrZone1","hrZone2","hrZone3","hrZone4"],
datasets: [
{
label: 'peak',
data: [hrData.peak,0,0,0],
backgroundColor: 'red'
},
{
label: 'cardio',
data: [0,hrData.cardio,0,0],
backgroundColor: 'blue'
},
{
label: 'fatBurn',
data: [0,0,hrData.fatBurn,0],
backgroundColor: 'green'
},
{
label: 'under',
data: [0,0,0,hrData.under],
backgroundColor: 'gray'
}
]
};
I currently display my data like this:
Click here
but I would like to display the data like this:
Click here
The idea is that at the bottom it would show the summary of all three colors numbers combined, but once you hover it would show how much each color has.
If all is one bar, if red is 7, blue is 3, the red but be a lot more in the bar.
This is the code for version 1, done with chart js
var ctx = document.getElementById("myChart").getContext("2d");
var data = {
labels: ["Chocolate", "Vanilla", "Strawberry"],
datasets: [
{
label: "Blue",
backgroundColor: "blue",
data: [3, 7, 4]
},
{
label: "Red",
backgroundColor: "red",
data: [4, 3, 5]
},
{
label: "Green",
backgroundColor: "green",
data: [7, 2, 6]
}
]
};
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
barValueSpacing: 20,
scales: {
yAxes: [{
ticks: {
min: 0,
}
}]
}
}
});
const myChart = new Chart(
document.getElementById('myChart'),
config
);
your problem is the next, you are using a wrong config you must something like this:
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.6.0/dist/chart.min.js"></script>
<canvas id="MyChart" width="400" height="200"></canvas>
<script>
new Chart(document.getElementById("MyChart"), {
type: 'bar',
data: {
labels: [2017],
datasets: [{
label: "Chocolate",
type: "bar",
stack: "Base",
backgroundColor: "#eece01",
data: [30],
}, {
label: "Vanilla",
type: "bar",
stack: "Base",
backgroundColor: "#87d84d",
data: [-15],
}, {
label: "Strawberry",
type: "bar",
stack: "Base",
backgroundColor: "#f8981f",
data: [20],
}, {
label: "candy",
type: "bar",
stack: "Base",
backgroundColor: "#00b300",
backgroundColorHover: "#3e95cd",
data: [-10]
}]
},
options: {
plugins: {
title: {
display: true,
text: 'Chart.js Bar Chart - Stacked'
},
},
responsive: true,
interaction: {
intersect: false,
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
});
</script>
Now you just need to fix the styles and sizes of the graphic
If someone finds this, the solution is this:
const config = {
type: 'bar',
data: data,
options: {
plugins: {
title: {
display: true,
text: 'Chart.js Bar Chart - Stacked'
},
},
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
};
So I have 2 datasets, and I would like to have one y-axis to be bigger and one smaller if you know what I mean.
The Return Procent datas are somewhere between 20-100, but price can be small like 0.06 or something. And it makes the chart useless. How can I make two different size y-axis?
var ctx = document.getElementById('skinsChart').getContext('2d');
var skinsChart = new Chart(ctx, {
type: 'line',
data: {
labels: [ < ? php echo $date; ? > ],
datasets: [{
label: 'Return Procent',
data: ["65.7", "63.7", "69.7", "62.7"],
borderColor: 'rgb(0,0,0)',
borderWidth: 1,
backgroundColor: 'rgba(0,0,0,0.2)'
}, {
label: 'Price',
data: ["0.07", "0.08", "0.06", "0.07"],
borderColor: 'rgb(255,0,0)',
borderWidth: 1,
backgroundColor: 'rgba(0,0,0,0.2)'
}]
},
options: {
scales: {
yAxes: [{
beginAtZero: false
}]
},
elements: {
point: {
radius: 0
}
}
}
});
You need to define multiple y-axes as explained in chapter Create Multiple Axis from the Chart.js documentation.
Please take a look at your amended code and see how it works.
var ctx = document.getElementById('skinsChart').getContext('2d');
var skinsChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
label: 'Return Procent',
data: ["65.7", "63.7", "69.7", "62.7"],
borderColor: 'rgb(0,0,0)',
borderWidth: 1,
backgroundColor: 'rgba(0,0,0,0.2)',
yAxisID: 'yLeft'
}, {
label: 'Price',
data: ["0.07", "0.08", "0.06", "0.07"],
borderColor: 'rgb(255,0,0)',
borderWidth: 1,
backgroundColor: 'rgba(0,0,0,0.2)',
yAxisID: 'yRight'
}]
},
options: {
scales: {
yAxes: [{
id: 'yLeft',
},
{
id: 'yRight',
position: 'right',
}]
},
elements: {
point: {
radius: 0
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="skinsChart" height="100"></canvas>
How do I create legends like the following:
So far I have the following:
I can't figure out how to specify labels for the legend. They don't provide examples either.
https://www.chartjs.org/docs/latest/configuration/legend.html
So far I have the following code:
var topClickSourcesChart = new Chart('dashboard-click-source-chart', {
type: 'bar',
data: {
labels: ["Facebook","Google","NONE",],
datasets: [{
data: [10,5,3,],
backgroundColor: ['#4D90C3', '#866DB2', '#EA6F98', '#61BDF6', '#768BB7'],
}],
},
options: {
scales: {
xAxes: [{
display: false,
}]
},
legend: {
position: 'right',
labels: {
boxWidth: 10,
}
}
}
});
To show a legend you need to set the label property of a dataset, so the type of output you are expecting can be built by creating a chart from the code as shown below. Fiddle -> https://jsfiddle.net/6nkcx8sq/
var topClickSourcesChart = new Chart('dashboard-click-source-chart', {
type: 'bar',
data: {
labels: ["Number of users"],
datasets: [{
label: 'Facebook',
data: [10],
backgroundColor: '#4D90C3',
},
{
label: 'Instagram',
data: [15],
backgroundColor: '#866DB2',
},
{
label: 'Twitter',
data: [7],
backgroundColor: '#EA6F98',
}],
},
options: {
scales: {
xAxes: [{
display: false,
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
position: 'right',
labels: {
boxWidth: 10,
}
}
}
});
I was able to get it with the following, but it was a pain in the ass for loops due to the backgroundColor having to be inside the dataset.
var topClickSourceChart = new Chart('dashboard-click-source-chart', {
type: 'bar',
data: {
labels: ["Facebook","Google","NONE",],
datasets: [
{label: "Facebook", data: [10]},{label: "Google", data: [5]},{label: "NONE", data: [3]},
]
},
options: {
scales: {
xAxes: [{
display: false,
}],
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
legend: {
position: 'right',
labels: {
boxWidth: 10,
}
}
}
});
if (topClickSourceChart.data.datasets.length > 0) topClickSourceChart.data.datasets[0].backgroundColor = '#4D90C3';
if (topClickSourceChart.data.datasets.length > 1) topClickSourceChart.data.datasets[1].backgroundColor = '#866DB2';
if (topClickSourceChart.data.datasets.length > 2) topClickSourceChart.data.datasets[2].backgroundColor = '#EA6F98';
if (topClickSourceChart.data.datasets.length > 3) topClickSourceChart.data.datasets[3].backgroundColor = '#61BDF6';
if (topClickSourceChart.data.datasets.length > 4) topClickSourceChart.data.datasets[4].backgroundColor = '#768BB7';
Here is the JSP/JSTL loop:
data: {
labels: [<c:forEach items="${clickSources}" var="cs">"${cs.key}",</c:forEach>],
datasets: [
<c:forEach items="${clickSources}" var="cs">{label: "${cs.key}", data: [${cs.value}]},</c:forEach>
]
},
I still could not find a way to make the top of the bars rounded.
I have a Bar Chart with these data and options:
var data = {
labels: periodnames,
datasets: [
{
yAxisID: "bar-stacked",
data: rcash,
backgroundColor: "#FFCE56",
label:""
},
{
yAxisID:"bar-stacked",
data: pcash,
backgroundColor: "#FFCE56",
label: "cash"
}
]
};
var options = {
animation: {
animateScale: true
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [
{
display:false,
id: "line-axis",
},
{
id: "bar-stacked",
stacked: true,
}
]
}
}
finactivityGraphChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
The result chart is this:
My problem is that I don't want to show the first dataset's label. If I don't define it, it shows the yellow box with the value "undefine" next to it. I suppose that I must modify the Chart.js file. Any suggestions?
This could be achieved, using the filter function of legend*'s* label.
see Legend Label Configuration
In short, add the following in your chart options ...
legend: {
labels: {
filter: function(label) {
if (label.text === 'cash') return true;
}
}
},
ᴅᴇᴍᴏ
var ctx = document.querySelector('#c').getContext('2d');
var data = {
labels: ['Jan', 'Feb', 'Mar'],
datasets: [{
yAxisID: "bar-stacked",
data: [1, 2, 3],
backgroundColor: "#FFCE56",
label: "gold"
}, {
yAxisID: "bar-stacked",
data: [-1, -2, -3],
backgroundColor: "#FFCE56",
label: "cash"
}]
};
var options = {
legend: {
labels: {
filter: function(label) {
if (label.text === 'cash') return true; //only show when the label is cash
}
}
},
animation: {
animateScale: true
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
display: false,
id: "line-axis",
}, {
id: "bar-stacked",
stacked: true,
}]
}
}
finactivityGraphChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="c"></canvas>