how do I make the values in Chart JS update as the bar progresses?
For example, I want the values for each bar to start at 0 and count up to there value, stopping when the bar has reached it's height.
At the moment, it just displays it's full value upon animation start (when the page loads)
Chart JS docs:
https://www.chartjs.org/
Here is a plugin I found:
https://emn178.github.io/chartjs-plugin-labels/samples/demo/
Here is a JS Fiddle (ignore the shaking!)
https://jsfiddle.net/8uehq5xr/
<!doctype html>
<html>
<head>
<title>Horizontal Bar Chart</title>
<script src="../../node_modules/chart.js/dist/Chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.7.3/dist/Chart.min.js">
</script>
<script src="https://cdn.jsdelivr.net/gh/emn178/chartjs-plugin-
labels/src/chartjs-plugin-labels.js"></script>
</head>
<body>
<canvas id="bar-chart" width="800" height="450"></canvas>
<div id="value">100</div>
<Script>
// Bar chart
new Chart(document.getElementById("bar-chart"), {
type: 'bar',
data: {
labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
datasets: [
{
label: "Population (millions)",
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
data: [2478,5267,734,784,433]
}
]
},
options:
{
plugins: {
labels:
{
render: 'value',
fontSize: 20,
}
},
scales: {
xAxes: [{
display: false
}],
yAxes: [{
display: false
}],
},
tooltips: { enabled: true},
hover: {animationDuration: 1},
}
});
</script>
</body>
</html>
I assume you would like something like a fast counter synchronized with chart drawing, can't you do it with just a for loop refreshing data given to chart.js until you reach their real value ?
No need for a plugin if it's a that small improvement you need
Example:
final data is [2478,5267,734,784,433]
initial data is [0, 0, 0, 0, 0]
1°) set an interval incrementing data of 1/10 of their value by example, each 200ms
-> after 200ms you now have [247, 526, 73, 78, 43]
2°) cancel interval once you reached final data
You can then play with interval and increment steps (1/100 in stead of 1/10 by example)
Here is a workaround (but I bet you block with chart drawing ?)
const currentData = [0,0,0,0,0];
const finalData = [2478,5267,734,784,433];
const stepTick = 0.1;
let stepNumber = 1;
const redrawingAfter1Step = setInterval(() => {
for(let i = 0; i < currentData.length; i++) {
currentData[i] = stepTick * stepNumber * finalData[i];
}
drawChart(currentData);
if ((stepNumber * stepTick) === 1) {
clearInterval(redrawingAfter1Step);
}
stepNumber++;
}, 500);
Related
I use chart.js in my code. Basically, it works well, but I have an issue with creating nth time a chart in the same canvas. I got error message telling me that I suppose to destroy chart first.
Then I found here some ideas how to do this and adapted this part of code as a solution:
let chartStatus = Chart.getChart("line-chart");
if (chartStatus != undefined) {
chartStatus.destroy();
//$("div.line-chart").remove();
//$("div.line-chart").append('<canvas id="line-chart" style="width: 1221px; height: 280px;"></canvas>');
}
It works fine - at least I do not get any errors any more, but when I create chart for second and more times, it gets resized. Please look at the attached pictures:
If you look at the scale you notice it is changed.
My question is: How can I destroy a chart and recreate its size/scale etc correctly or how can I update a chart instead of destroying it?
The code looks like this:
javascript:
let chartStatus = Chart.getChart("line-chart");
if (chartStatus != undefined) {
chartStatus.destroy();
//$("div.line-chart").remove();
//$("div.line-chart").append('<canvas id="line-chart" style="width: 1221px; height: 280px;"></canvas>');
}
new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: labelX,
datasets: [{
data: waga,
label: "Waga",
borderColor: "#3e95cd",
borderWidth: 1,
fill: false
}
]
},
options: {
title: {
display: true,
responsive: true,
maintainAspectRatio: false
}
}
});
HTML:
<div><canvas id="line-chart" style="width: 1221px; height: 280px;"></canvas></div>
I have found a solution that works for me here.
Now, my code looks like this:
let chartStatus = Chart.getChart("line-chart");
if (chartStatus != undefined) {
document.getElementById('line-chart').remove();
let canvas = document.createElement('canvas');
canvas.setAttribute('id','line-chart');
canvas.setAttribute('width','1221');
canvas.setAttribute('height','280');
document.querySelector("#line-chart-wrapper").appendChild(canvas);
}
and HTML part...
<div id="line-chart-wrapper"><canvas id="line-chart" style="width: 1221px; height: 280px;"></canvas></div>
Now chart looks OK no matter how many times it is created.
If you set the size of the canvas in css, it will be resized each time the chart is drawn, and the second time you lose its height completely.
The solution you propose - to size the canvas in code is less than ideal because chart.js draws the plot at its own devised size and then you just scale the resulted image to your desired size, which may look OK in your specific case, but in most cases you lose the quality of the result with possible overlapping items or lost resolution.
The standard way to have chart.js (re)draw the plot at your intended size is to have a div that includes only the canvas and set the css size of the div, not the canvas, together with the option maintainAspectRatio: false (which you already set). See also this section from the docs.
The following example, loosely based on your code excerpt, does that.
let labelX = [], waga = [];
function resetData(){
const d0 = Date.now() - 3*365*24*3600*1000*Math.random();
// change the whole arrays, since the chart is destroyed and rebuilt
labelX = Array.from({length: 10}, (_, i) =>
new Date(d0 + i * 24 * 3600 * 1000).toLocaleDateString(undefined,
{year: 'numeric', month: 'numeric', day: 'numeric'}));
waga = Array.from({length: 10}, (_, i) => Math.random());
}
let chart;
function drawChart(){
chart = new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: labelX,
datasets: [{
data: waga,
label: "Waga",
borderColor: "#3e95cd",
borderWidth: 1,
fill: false
}]
},
options: {
title: {
display: true,
responsive: true,
maintainAspectRatio: false
},
scales: {
x: {
bounds: 'ticks',
type: 'category',
},
y: {
type: 'linear',
display: true,
min: 0,
max: 1
}
}
}
});
}
resetData();
drawChart();
document.getElementById('reset').onclick = function(){
resetData();
document.querySelector('canvas#line-chart').innerHTML = '';
chart.destroy();
drawChart();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.1.2/chart.umd.js"
integrity="sha512-t41WshQCxr9T3SWH3DBZoDnAT9gfVLtQS+NKO60fdAwScoB37rXtdxT/oKe986G0BFnP4mtGzXxuYpHrMoMJLA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="line-chart-container" style="height:300px; width:1000px; border: 1px solid red">
<canvas id="line-chart"></canvas>
</div>
<button id="reset">Reset</button>
Alternatively (with the same setting for styling the div not the canvas), the more efficient solution would be to just change the data and then call chart.update, if it fits your purpose, like this:
const labelX = [], waga = [];
function resetData(){
const d0 = Date.now() - 3*365*24*3600*1000*Math.random();
// using splice 0 to infinity to replace all data *inside* the arrays
labelX.splice(0, 1/0, ...Array.from({length: 10}, (_, i) =>
new Date(d0 + i * 24 * 3600 * 1000).toLocaleDateString(undefined,
{year: 'numeric', month: 'numeric', day: 'numeric'})));
waga.splice(0, 1/0, ...Array.from({length: 10}, (_, i) => Math.random()));
}
resetData();
const chart = new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: labelX,
datasets: [{
data: waga,
label: "Waga",
borderColor: "#3e95cd",
borderWidth: 1,
fill: false
}]
},
options: {
title: {
display: true,
responsive: true,
maintainAspectRatio: false
},
scales: {
x: {
bounds: 'ticks',
type: 'category',
},
y: {
type: 'linear',
display: true,
min: 0,
max: 1
}
}
}
});
document.getElementById('reset').onclick = function(){
resetData();
chart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.1.2/chart.umd.js"
integrity="sha512-t41WshQCxr9T3SWH3DBZoDnAT9gfVLtQS+NKO60fdAwScoB37rXtdxT/oKe986G0BFnP4mtGzXxuYpHrMoMJLA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<body>
<div id="line-chart-container" style="height:300px; width:1000px; border: 1px solid red">
<canvas id="line-chart"></canvas>
</div>
<button id="reset">Reset</button>
</body>
I am using chart.js for my project. As you can see in the following code, chart.js uses different minimum and maximum values for y-axis for line and bar graphs for same data. In the given code, 1100-1700 is the value range for line graph whereas 0-2000 is the value range used for bar graph.
I like to make same minimum and maximum values for both line and bar graphs. One option is to find minimum, maximum and step-size values my own and use the properties min, max and ticks.stepSize under scales. But finding minimum, maximum and step-size for a given data data is a serious task.
For my requirement, default minimum ,maximum and stepSize used by chart.js for line graph is fine and I would like to use the same values for bar graph also.
I could first render line graph, get these values from this graph (like scales.y.min) and then use it for bar graph, which is perfectly working
Is there a way I could get the default min, max, stepSize values used by chart.js for line graph before actually drawing the graph? Any answers or pointers are really appreciated. Thanks
var chart1 = new Chart(document.getElementById('chart1'),
{
type: 'line',
data:
{
labels: ['a','b','c'],
datasets:
[
{
label: 'A',
data: [1212,1122, 1188, 1617, 1116],
borderColor: 'green',
}
]
}
});
var chart2 = new Chart(document.getElementById('chart2'),
{
type: 'bar',
data:
{
labels: ['a','b','c'],
datasets:
[
{
label: 'A',
data: [1212,1122, 1188, 1617, 1116],
backgroundColor: 'green',
}
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<div style="width:300px;height:200px"><canvas id="chart1"></canvas></div>
<div style="width:300px;height:200px"><canvas id="chart2"></canvas></div>
The first thing you could do is to set beginAtZero to false to Y axis of bar chart because is set to true by default:
https://github.com/chartjs/Chart.js/blob/v3.9.1/src/controllers/controller.bar.js#L636-L650
Then you can set the same scale config to both charts.
options: { // options of charts
scales: {
y: {
beginAtZero: false,
}
}
}
var chart1 = new Chart(document.getElementById('myChart1'),
{
type: 'line',
data:
{
labels: ['a','b','c'],
datasets:
[
{
label: 'A',
data: [1212,1122, 1188, 1617, 1116],
borderColor: 'green',
}
]
},
options: {
scales: {
y: {
beginAtZero: false,
}
}
}
});
var chart2 = new Chart(document.getElementById('myChart2'),
{
type: 'bar',
data:
{
labels: ['a','b','c'],
datasets:
[
{
label: 'A',
data: [1212,1122, 1188, 1617, 1116],
backgroundColor: 'green',
}
]
},
options: {
scales: {
y: {
beginAtZero: false,
}
}
}
});
.myChartDiv {
max-width: 300px;
max-height: 200px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.9.1/dist/chart.min.js"></script>
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart1" width="300" height="200"/>
</div>
<div class="myChartDiv">
<canvas id="myChart2" width="300" height="200"/>
</div>
</body>
</html>
I am trying to find the currently visible data points following a zoom event using chartjs-plugin-zoom. Following examples I came up with the following onZoomComplete callback, but it is not working.
function getVisibleValues({chart}) {
const x = chart.scales.x;
let visible = chart.data.datasets[0].data.slice(x.minIndex, x.maxIndex + 1);
}
One immediate issue is that chart.data doesn't seem to exist (when using console.log(chart.data) it comes back undefined). Same with x.minIndex and x.maxIndex... Any ideas on what I'm doing wrong would be much appreciated.
Below is how I setup the chart (data is an array of x,y pairs):
ctx = new Chart(document.getElementById(ctx_id), {
type: "scatter",
data: {
datasets: [
{
label: "Data",
lineTension: 0,
showLine: true,
data: data,
},
],
},
options: {
animation: false,
plugins: {
zoom: {
zoom: {
mode: "x",
drag: {
enabled: true,
borderColor: "rgb(54, 162, 235)",
borderWidth: 1,
backgroundColor: "rgba(54, 162, 235, 0.3)",
},
onZoomComplete: getVisibleValues,
},
},
},
},
});
You can access the c.chart.scales["x-axis-0"]._startValue and c.chart.scales["x-axis-0"]._valueRange. These two give the first and last visible values respectively.
These values can be used to get the dataset data available at c.chart.config.data.datasets[0].data, or the label names at c.chart.config.data.labels.
If you only need to get the visible tick labels, you can do this by simply accessing the chart.scales["x-axis-0"].ticks object.
function getVisibleValues(c) {
document.getElementById("visibleTicks").textContent = JSON.stringify(
c.chart.scales["x-axis-0"].ticks // This is one way to obtain the visible ticks
);
const start = c.chart.scales["x-axis-0"]._startValue // This is first visible value
const end = start + c.chart.scales["x-axis-0"]._valueRange // This is the last visible value
document.getElementById("visibleValues").textContent = JSON.stringify(
c.chart.config.data.datasets[0].data.slice(start, end + 1) // Access chart datasets
//Note: You can also get the labels from here, these are available at `c.chart.config.data.labels`
);
}
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: "line",
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: "# of Votes",
data: [12, 19, 3, 5, 2, 3]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
plugins: {
zoom: {
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: "x",
onZoomComplete: getVisibleValues
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom#0.7.5/dist/chartjs-plugin-zoom.min.js"></script>
<html>
<body>
Visible ticks: <span id="visibleTicks">Begin zooming</span><br/>Visible data: <span id="visibleValues">Begin zooming</span>
<div class="myChartDiv" style="width: 400px;">
<canvas id="myChart"></canvas>
</div>
</body>
</html>
I created a dynamic line chart based on some input data. The intention is that the customer can indicate with a dropdown on which month the "Investment" should start.
So, for example, if the "Investment" does not start until month 6, then that line should only start at 6 on the x-axis. But the other lines "Case" and "ROI" should still just start at 1.
I've tried several things but to no avail.
I tried changing the x-axis "min ticks" based on the selection the user made, but that makes all lines start at another point instead of the "Investment" line only. Another problem is that every number before the selection then dissapears from the x-axis. But I really want to keep every number from 1-60, even if the user chooses to start the "Investment" on month 10, for example.
I would really appreciate some help! Thanks.
Here's my fiddle: https://jsfiddle.net/js5pha24/
var options = {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Case',
data: [],
backgroundColor: 'rgba(152,164,135, 0.5)',
borderColor: 'rgb(152,164,135)',
fill: false
}, {
label: 'Case',
data: [],
backgroundColor: 'rgba(145,139,167, 0.5)',
borderColor: 'rgb(145,139,167)',
fill: false
}, {
label: 'Case',
data: [],
backgroundColor: 'rgba(206,157,206, 0.5)',
borderColor: 'rgb(206,157,206)',
fill: false
}]
},
options: {
legend: {
display: true,
position: "top"
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
autoSkip: true,
maxRotation: 0,
minRotation: 0
}
}],
yAxes: [{
ticks: {
callback: value => {
return "€ " + value;
}
}
}]
}
}
}
for (let i = 1; i <= 60; i++) {
options.data.labels.push(i);
const caseMonth = 118187 * i;
options.data.datasets.find(set => set.label === "Case").data.push(caseMonth);
const investMonth = 500000 + (20000 * i);
options.data.datasets.find(set => set.label === "Investment").data.push(investMonth);
const roiMonth = caseMonth - investMonth;
options.data.datasets.find(set => set.label === "ROI").data.push(roiMonth);
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
canvas { background-color : #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.js"></script>
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>
You can put null values on the chart data so one line can start after the others. For example if you want the investment line start at month 10, you can replace the the first ten investMonth values with null.
If understood correctly you still want to use the investMonth value in the roiMonth calculation so I created "investMonthValue" so only investment will get null if it is less than investmentStartMonth.
let investmentStartMonth = 10
for (let i = 1; i <= 60; i++) {
options.data.labels.push(i);
const caseMonth = 118187 * i;
options.data.datasets.find(set => set.label === "Case").data.push(caseMonth);
let investMonth = 500000 + (20000 * i);
let investMonthValue = i<investmentStartMonth?null:investMonth
options.data.datasets.find(set => set.label === "Investment").data.push(investMonthValue);
const roiMonth = caseMonth - investMonth;
options.data.datasets.find(set => set.label === "ROI").data.push(roiMonth);
}
I am working on a webpage that presents dashboard on th basis of invoice processing project.
using below code i am populating bar chart.
function loadVolumeChart()
{
var pieChartContent = document.getElementById('chartAreaWrapper');
pieChartContent.innerHTML = '';
$('#chartAreaWrapper').append('<canvas id="line-chart" height="300" width="1500px"><canvas>');
//getData For Volume Analysis Chary
var url_string = document.referrer;;
var url = new URL(url_string);
var name = url.searchParams.get("name");
var user=url.searchParams.get("user");
var team=url.searchParams.get("team");
var date=url.searchParams.get("date");
var dates = [];
var count = [];
var from = date.split("-")[0];
var to = date.split("-")[1];
var re=$.ajax({
url: 'getTotalCounts.php',
type: 'POST',
data: {
from:from,
to:to,
team:team,
totalVolume: '00'
},
async:false,
success: function(data) {
var result =data;
var json = JSON.parse(result);
dates=json[0].data;//json[0].data;
count=json[1].data;
//alert(dates);
}
}).done(function(data){
// openPage(data);
}).fail(function(data){
alert(data.responseText);
});
//volume chart
new Chart(document.getElementById("line-chart"), {
type: 'bar', //line
data: {
labels:dates,
datasets: [{
data:count,
label: "Total Inward",
backgroundColor: "#0E6655", //borderColor
fill: true
},
]
},
options: {
responsive:false,
maintainAspectRatio: true,
legend: {
display: false
},
tooltips: {
enabled: true
},
scales: {
xAxes: [{
gridLines: {
display:false
},
barThickness: 15,
}],
yAxes: [{
barPercentage: 1.0,
categoryPercentage: 1.0,
gridLines: {
display:false
},
ticks: {
min: 0,
max:10,
stepSize: 1
}
}]
},
}
});
//end of volume chart
}
But the problem is when data is low, means if the x axis data contains only 2 dates, then the gap between two bars is too large, like the image below,
but if i add more dates then the gap reduces.i want to set gap between two bars even if their are only two bars. the gap between both should not increase if the dates (bars) according to the size of x axis data. if the data is large then it should only scroll. thats why i have added scroll bar.
the div of chart is as:
<div class="parentDiv" >
<div class="chartAreaWrapper" id="chartAreaWrapper" style="height:80%;width:70%;margin-left: 20px; margin-top: 10px;float: left;">
<canvas id="line-chart" height="300" width="1500px"></canvas>
</div>
</div>
In your code, the option xAxis.barThickness defines that the width of individual bars has to be of 15 pixels. Simply remove this option.
You should also consider to use the latest stable version of Chart.js (currently v2.9.3) where the option xAxis.barThickness is deprecated. The options barThickness, barPercentage and categoryPercentage are now part of the dataset configuration.