Why does Chartjs line chart does not animate to new values? - javascript

I am using a chartjs line chart showing time data where I want to update with new values from an ajax call.
I have time on the X axis (1 days on 15 minutes intervals) , and just numbers for the Y axis.
So it looks like the following..
My setup is as follows.
this.chartSetup = {
type: 'line',
data: {
labels: this.times,
datasets: [{
fill: true,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
label: title,
data: this.vals,
pointRadius: 0,
}]
},
options: {
spanGaps: true,
legend: {
position: 'bottom',
labels: {
boxWidth: 10
}
},
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
stepSize: 6,
unitStepSize: 5
},
type: 'time',
time: {
displayFormats: {
hour: 'h:mm a',
minute: 'h:mm a',
}
}
}],
},
}
};
Chart.defaults.global.animation.easing = "easeOutBounce";
this.chart = new Chart(this.canvas.nativeElement, this.chartSetup);
When the chart first draws it animates the new values. However, if I then remove the data, and add a new set, it does not animate to the new values.
For example, to mimic new server data, using a timer, if I do the following,
where this.times holds the X data, this.vals holds the Y..
setTimeout(() => {
let temp = this.makeDummyData(20);
//let dataToUpdate = (<any>(this.chart.data)).datasets[0].data;
//let labels = (<any>(this.chart.data)).labels;
this.times.splice(0, this.times.length);
this.vals.splice(0, this.vals.length);
for (let item of temp) {
this.times.push(item.time);
this.vals.push(item.val);
}
this.chart.update();
}, 8000)
The chart does update, but it does not have any animation, it just jumps straight to the new display. If I just date the exiting data without removing it first, does does animate to the new display.
Is there any reason the animation does not work when I replace the data as in the above example?
Thanks in advance for any suggestions

Related

How to display single data-values with ApexCharts correctly?

I am currently trying to build a barchart with ApexCharts. Everything works fine when the data-array has more than one entry. Somehow, whenever data only contains one element, the diagram messes up the width of the bar.
Correct state with multiple entries:
Buggy state with one entry
My goal is to display a single value with the same xaxis scale and the same barwitdh as the barchart displays multiple values.
I already tried using an array of timestamps for each hour as catigories-prop but this didn't work for me.
I use 'dateime' as type for my xaxis.
Data is an array that can contain 0, 1 or x entries. It is given to the Barchart-Component as data-prop.
Categories contains a the timestamp to each entry of data. Therefore it can also have 0, 1, or x values. Categories is used as the Barcharts categories-prop.
Here are my configurations (For the buggy state):
var options = {
series: [
{
name: "Servings",
data: [[1669082400000, 44]]
}
],
chart: {
height: 350,
type: "bar",
animations: {
enabled: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
xaxis: {
type: 'datetime',
labels: {
datetimeUTC: false,
datetimeFormatter: {
year: 'yyyy',
month: 'MMM',
day: 'dd',
hour: 'HH:mm'
}
},
categories: [
1669082400000,
],
min: 1669075200000,
max: 1669161600000
},
tooltip: {
intersect: false,
shared: true
},
yaxis: {
reversed: false,
}
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
Would be nice if somebody could help me out, I'm tripping over this one.

Chart.js : How I change the x axes ticks labels alignment in any sizes?

How can I move my labels on my x axes in between another x axes label. Nothing seems to work and I was unable to find anything on the docs. Is there a workaround? I'm using line chart time series.
https://www.chartjs.org/samples/latest/scales/time/financial.html
Currently, with the code I have its generating the figure below:
var cfg = {
elements:{
point: {
radius: 4
}
},
data: {
datasets: [
{
label: 'vsy',
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
borderColor: window.chartColors.red,
data: firstData,
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
},
{
label: 'de vsy',
backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
borderColor: window.chartColors.blue,
data: dataMaker(15),
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
}
],
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
offset: true,
time: {
unit: 'month',
displayFormats: {
month: 'MMM'
}
},
ticks: {
autoSkip: true,
autoSkipPadding: 75,
sampleSize: 100
},
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index',
}
}
};
This is what I have now:
I want the labels on the x-axis to be on center instead of below the y axis grid line.
Thanks to uminder, with his comment it solves the issue but now I have a conflicting tooltip which lie on a same grid. When I hover to april line first point it shows me mar 30 which lies just above it and vice versa.
I fixed it by changing the mode to nearest but why is it activating the another point?
The option you're looking for is offsetGridLines.
If true, grid lines will be shifted to be between labels.
xAxes: [{
...
gridLines: {
offsetGridLines: true
}
In most cases, this produces the expected result. Unfortunately it doesn't work for time axes as documented in Chart.js issue #403. Thanks to Antti Hukkanen, there exists a workaround.
Please have a look at below runnable code snippet to see how it works.
function generateData() {
var unit = 'day';
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomPoint(date, lastClose) {
var open = randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = randomNumber(open * 0.95, open * 1.05).toFixed(2);
return {
t: date.valueOf(),
y: close
};
}
var date = moment().subtract(1, 'years');
var now = moment();
var data = [];
for (; data.length < 600 && date.isBefore(now); date = date.clone().add(1, unit).startOf(unit)) {
data.push(randomPoint(date, data.length > 0 ? data[data.length - 1].y : 30));
}
return data;
}
var TimeCenterScale = Chart.scaleService.getScaleConstructor('time').extend({
getPixelForTick: function(index) {
var ticks = this.getTicks();
if (index < 0 || index >= ticks.length) {
return null;
}
// Get the pixel value for the current tick.
var px = this.getPixelForOffset(ticks[index].value);
// Get the next tick's pixel value.
var nextPx = this.right;
var nextTick = ticks[index + 1];
if (nextTick) {
nextPx = this.getPixelForOffset(nextTick.value);
}
// Align the labels in the middle of the current and next tick.
return px + (nextPx - px) / 2;
},
});
// Register the scale type
var defaults = Chart.scaleService.getScaleDefaults('time');
Chart.scaleService.registerScaleType('timecenter', TimeCenterScale, defaults);
var cfg = {
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
backgroundColor: 'red',
borderColor: 'red',
data: generateData(),
type: 'line',
pointRadius: 0,
fill: false,
lineTension: 0,
borderWidth: 2
}]
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'timecenter',
time: {
unit: 'month',
stepSize: 1,
displayFormats: {
month: 'MMM'
}
},
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index'
}
}
};
var chart = new Chart('chart1', cfg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart1" height="90"></canvas>
For chartJs v3 you can use offset property:
scales: {
x: {
grid: {
offset: true
}
},
...
}

Showing a duration of time within a Chart.js chart

I'm attempting to create a chart using Chart.JS for a project that will show the duration a device has been switched on for in a given day.
The dates are on the Y-axis with the X-axis containing a 24-hour time scale from 00:00 to 23:59 with horizontal bars expressing the time durations. I suppose I'm attempting to make something similar in style to a Gantt chart.
The Javascript code I have at the moment is this:
var timeAxis = [];
var dateAxis = [];
var startDate = moment('2000-01-01 00:00');
var endDate = moment('2000-01-01 23:59');
while (startDate <= endDate) {
timeAxis.push(startDate.format('HH:mm'));
startDate.add(1, 'minutes');
}
startDate = moment('2019-12-06');
endDate = moment('2019-12-13');
while (startDate <= endDate) {
dateAxis.push(startDate.format('ddd DD/MM'));
startDate.add(1, 'days');
}
var ctx = document.getElementById('deviceOnChart').getContext('2d');
var testChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
datasets: [{
label: 'Device On Time',
data: [
'2019-12-13 06:32',
'2019-12-12 09:26',
'2019-12-11 11:45',
'2019-12-10 18:06',
'2019-12-09 03:56',
'2019-12-08 15:34',
'2019-12-07 12:53',
'2019-12-06 21:35',
],
backgroundColor: 'rgba(0, 100, 0, 0.2)',
borderColor: 'rgba(0, 150, 0, 1)',
borderWidth: 0.5,
xAxisID: 'OnTime'
},
{
label: 'Device Off Time',
data: [
'2019-12-13 21:34',
'2019-12-12 17:28',
'2019-12-11 22:58',
'2019-12-10 21:45',
'2019-12-09 14:27',
'2019-12-08 19:35',
'2019-12-07 18:53',
'2019-12-06 22:35'
],
backgroundColor: 'rgba(100, 0, 0, 0.2)',
borderColor: 'rgba(150, 0, 0, 1)',
borderWidth: 0.5,
xAxisID: 'OffTime'
}]
},
options: {
scales: {
yAxes: [{
type: 'time',
time: {
unit: 'day',
parser: 'ddd DD/MM',
displayFormats: {
day: 'ddd DD/MM'
}
},
ticks: {
source: 'labels'
},
labels: dateAxis
}],
xAxes: [{
id: 'OnTime',
type: 'time',
time: {
unit: 'minute',
parser: 'HH:mm',
displayFormats: {
minute: 'HH:mm'
}
},
ticks: {
source: 'labels',
autoSkip: true
},
labels: timeAxis,
stacked: true
},
{
display: false,
id: 'OffTime',
type: 'time',
time: {
unit: 'minute',
parser: 'HH:mm',
displayFormats: {
minute: 'HH:mm'
}
},
ticks : {
source: 'labels',
autoSkip: true
},
labels: timeAxis,
stacked: true
}]
}
}
});
I have also created a JSFiddle to provide a demo of what the chart looks like at present (which is pretty bad). I've never really done much work with time axes in Chart.JS before so it's proving to be somewhat confusing for me and the documentation seems a little sparse in places.
I've been able to get the axes to display the dates and times correctly in what I am assuming is in a correct manner. However, I am uncertain as to how to correctly format the data such that Chart.JS can parse it. At the moment the chart is empty presumably because it doesn't know how to interpret the data I am trying to get it to display.
I have tried specifying the data as an array of objects with x and y attributes containing the time and date respectively. I have also tried putting it as a single dataset, as well as two datasets with the X-axis set as stacked and the chart just will not show anything.
Any help that anyone could provide would be much appreciated.
Here's my solution: https://jsfiddle.net/e6td4sh0/
I use the new floating-bar-feature from the quite new chart.js 2.9-version.
Everything works, except the timezone and the time format for the tooltip. I post the complete code here when I solved these problems. If somebody else solves them I would be glad if he/she updates my answer with the correct code.

ChartJS, Primeng, Gap first and end of line chart

I am using primeng chart component which uses chartjs. We are using chartjs 2.5.0 alongside primeng 4.0 and angular 4.
I created a dynamic chart and I put the data into chart after it came to us through some services. The problem is, after a while chartjs will put a gap at first and end of the chart.
Here is our options for chartjs:
this.options = {
responsive: true,
tooltips: {
mode: 'index',
intersect: false, // all points in chart to show tooltip
callbacks: { // adding labels as title in tooltip
title: function(tooltipItems, data) {
let date = tooltipItems[0].xLabel;
return me._rhDatePipe.transform(date, 'time');
}
}
},
hover : {
mode: 'index',
intersect: false
},
scales: {
xAxes: [{
type: 'time',
display: false, // preventing labels from being displayed
max: 20
}],
yAxes: [{
ticks: {
maxTicksLimit: 3
}
}]
}
}
and here is our first data settings:
this.data = {
labels: this.labels, // current time as label
datasets: [
{
label: me._messageService.translate('chart-label-buy'),
data: this.buyingData,
fill: false,
borderColor: "#2453db",
lineTension: 0,
borderWidth: 1.5,
radius: 0 // removing dot points on chart
},
{
label: me._messageService.translate('chart-label-sale'),
data: this.sellingData,
fill: false,
borderColor: "#f44336",
borderWidth: 1.5,
lineTension: 0,
radius: 0 // removing dot points on chart
},
{
label: me._messageService.translate('chart-label-last-trade'),
data: this.lastPriceData,
fill: false,
borderColor: "#000000",
lineTension: 0,
borderWidth: 1.5,
radius: 0 // removing dot points on chart
}
]
}
and here is the loop which will update the chart:
if(sortedKeysList != null) {
for(let key in sortedKeysList) {
let currentTime: number = sortedKeysList[key];
// just add new points
if(!this.currentTimes.includes(currentTime)) {
let date = new Date(currentTime);
this.labels.push(date);
this.currentTimes.push(currentTime);
this.buyingData.push(this.bestLimitsChart[currentTime].buy);
this.sellingData.push(this.bestLimitsChart[currentTime].sale);
if(this.bestLimitsChart[currentTime].price != 0)
this.lastPriceData.push(this.bestLimitsChart[currentTime].price);
else
this.lastPriceData.push(null);
this.updateChart();
}
}
}
and the picture of chart:
I do not know what is going on. Any helps will greatly appreciated.
I finally found the problem,
for other people facing this issue, you can add unit to your axis:
xAxes: [{
type: 'time',
time: {
displayFormats: {
minute: 'h:mm', // formatting data in labels
},
unit: 'minute' // destroy first and end gaps
},
display: false, // preventing labels from being displayed
}],
similar issue on github:
https://github.com/chartjs/Chart.js/issues/2277#issuecomment-314662961

Chartjs X axis label and tick formatting

I am using Chartjs to display a time series line graph.
My setup is as following...
this.chartSetup = {
type: 'line',
data: {
labels: this.times,
datasets: [{
fill: true,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
label: title,
data: this.vals,
pointRadius: 0,
}]
},
options: {
spanGaps: true,
legend: {
position: 'bottom',
labels: {
boxWidth: 10
}
},
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
stepSize: 6,
unitStepSize: 5
},
type: 'time',
time: {
displayFormats: {
hour: 'h:mm a',
minute: 'h:mm a',
}
}
}],
},
}
};
Chart.defaults.global.animation.easing = "easeOutBounce";
this.chart = new Chart(this.canvas.nativeElement, this.chartSetup);
My chart looks like the following screen shot
The data is Date for the X axis (Labels), and just numbers for the Y.
The time data goes from 6am to 6pm (12 hours worth)
I have a couple of issues here all relating to the X axis label formatting.
The initial 6am label is being cut off
How can I change the X axis label rotation (so perhaps by 90 degrees will fix the truncated first value)
My data goes to 6pm, but it is showing an extra X axis value (7pm). Can I get rid of this?
Thanks in advance for any suggestions here.
What worked for me was setting
autoskip: true
autoSkipPadding: 30
in the tick configuration.
1,2 - set minRotation = 90
3 - set Max value on your x axes

Categories