chart.js time x-axis - start ticks at specific time - chart.js

In chart.js 3 I have a 'time' x-axis. I'm using a callback in ticks to show hours since a specific time like so:
ticks: {
display: true,
callback: function (value, index, ticks) {
var tickMS = ticks[index].value;
var diffMS = tickMS - _this.startMS;
var diffHour = diffMS / 1000 / 60 / 60;
return diffHour;
}
}
This works fine, however the ticks displayed are fractions of an hour like this:
How can I set the ticks to start at _this.startMS so that the ticks displayed are on the hour (since _this.startMS) showing 8, 16 etc. rather than 7.8889...?

you can add a min property like so:
scales: {
x: {
min: _this.startMS
}
}

Answering my own question - for my purposes, setting min: didn't work, so instead I've disabled the scale and used the annotations plugin to draw my own

Related

Chart.js: Limit maximal Zoom In

I'm using chart.js to display a chart with x-values from 1e3 up to 1e11. I already figured out that I can limit the Zoom Out by setting limits.x.min = 1e3 and limits.x.max = 1e11.
But I failed at limiting the Zoom In. By looking at the documenation, I thought minRange would be the way to go, but it didn't seem to work like this:
plugins: {
zoom: {
zoom: {
wheel: {
enabled: true,
speed: 0.1
},
mode: "x"
},
limits: {
x: {
min: 9.0e2, // I know I could achieve almost the same with "default"
max: 1.1e11,
minRange: 1e3
}
}
}
}
I thought that minRange specifies the range of the minimal displayed x-value to the maximal displayed x-value, therefore I've set it to 1e3 and expected e.g. the Zoom In to stop at 4e3 and 5e3.
Did I misunderstand the concept of minRange?
Versions are: chart.js: 4.1.1, chartjs-plugin-zoom: 2.0.0

How to align ticks label in chartJS 3.x?

is possible with chartJS chenge the align position of tick labels for scaleY and for scaleX?
In the image attached i want 300,150,60, 0 on top of the grid line and 9:56 aligned on the right of the first tick.
I try to modify the code in this answer, but without success.
After some research i adapted the method showned here to works with ChartJS 3.x:
plugins: [{
afterDraw: function (c) {
var yScale = c.scales['y'];
yScale.ticks.forEach(function (o, i) {
// top tick label indent
var yT_O = yScale.getPixelForTick(i) - 9;
// left tick label indent
var xT_O = c.width - 35;
c.ctx.fillStyle = "#707070"
c.ctx.fillText(o.label, xT_O, yT_O);
});
}
}]
Actually i'm not sure this is the best plugin event where do that.
I also had to put "display: false" on the ticks options in order to hide the originals tick labels.

Draw a moving vertical line representing the current date in amcharts gantt chart?

I am using amCharts4 and is able to plot a vertical line on my Gantt chart representing the current date time as described in the following : Draw a vertical line representing the current date in amcharts gantt chart?. However, how do I make the vertical line move every sec on the chart based on the current time? Here is the code :
var range = dateAxis.axisRanges.create();
range.date = new Date("2020-10-29 07:04:56");
range.grid.stroke = am4core.color("red");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;
You can add a setInterval that runs every second to update the date on your range, like this:
// First, create your range marker
var range = dateAxis.axisRanges.create();
range.grid.stroke = am4core.color("red");
range.grid.strokeWidth = 2;
range.grid.strokeOpacity = 1;
range.date = new Date();
// Then, update the position of your range marker every second
range.date = new Date(2018, 0, 1, 8, 0, 0, 0);
var timeout = window.setInterval(() => {
range.date = new Date(range.date.getTime() + 1000);
}, 1000);
chart.events.on('beforedisposed', () => {
clearTimeout(timeout);
});
Here is an example
UPDATE
One thing you want to do with the timeout is to also clear it when you no longer need it--- for example, when you dispose the chart or the marker.
I have updated the code above to clear it before the chart is disposed.

Chartjs unexpected visual animation effect when adding data

I have a long array with data that I slice with Javascript in order to display data of different date ranges in my chart. This way the backend only needs to get the data once, and I can the just slice it on the client side.
// All data
var allLabels = [
// data here
];
var allData = [
// data here
];
Then I do:
var labelsCount = allLabels.length;
var dataCount = allData.length;
var updatedLabels;
var updatedData;
if($date_range === 'last_7_days')
{
updatedLabels = allLabels.slice(labelsCount - 7);
updatedData = allData.slice(labelsCount - 7);
}
if($date_range === 'last_30_days')
{
updatedLabels = allLabels.slice(labelsCount - 30);
updatedData = allData.slice(labelsCount - 30);
}
scoreChart.data.labels = updatedLabels;
scoreChart.data.datasets[0].data = updatedData;
scoreChart.update({
duration: 1000,
easing: 'easeInOutExpo'
});
This all works as expected. When switching from 30 to 7 days the points on the right of the 7 days disappear, and the graph scales and grows nicely to the new 7 days x-axis.
The other way around, when you have the graph of 7 days and then switch to 30, produces an ugly visual effect where the first point of the graph sticks to the side, overlaps the new data points and then animates.
After the animation the graph looks as expected, it's just the animation that's ugly. It's a little tricky to explain so hopefully the screenshots help. Green arrows indicate the animation direction. I've set the animation duration to 10s so I can take this screenshot, the red circle highlights the point that starts on the right of the graph and then animates to the left.
I've also tried adding this:
scoreChart.data.labels.pop();
scoreChart.data.datasets[0].data.pop();
scoreChart.update();
and this:
scoreChart.data.labels = [];
scoreChart.data.datasets[0].data = [];
scoreChart.update();
Before the line scoreChart.data.labels = updatedLabels; but that gives the same result.
Another thing I can do is only update the labels. The result is that the chart just zooms on the timeline when changing date ranges, without the nice animation as they have in the example.
You could try to first remove all labels and the data when switching to 'last_30_days'.
if($date_range === 'last_30_days')
{
scoreChart.data.labels = [];
scoreChart.data.datasets[0].data = [];
scoreChart.update({
duration: 500,
easing: 'easeInOutExpo'
});
updatedLabels = allLabels.slice(labelsCount - 30);
updatedData = allData.slice(labelsCount - 30);
}

Chart.JS spacing and padding

Is it possible to get some more space between the chart and the x-axis?
Is it possible to get some more space between the right side of the chart and the end of the canvas area? I want to add some more elements to the canvas right beside the chart but this is not possible because the chart takes the whole canvas width so it would overlap.
Shifting x axis Labels Vertically
The easiest way to do 1. is by adding spaces to your x labels. You can extend your chart type and override your initialize function to do this (increase 30 to something larger if your labels are long to start with anyway)
initialize: function(data){
data.labels.forEach(function(item, index) {
data.labels[index] += Array(Math.max(30 - item.length, 0)).join(" ");
})
Chart.types.Bar.prototype.initialize.apply(this, arguments);
},
Edit : As pointed out in the comments, this causes a horizontal shift as well and the label ends no longer align with the x axis markers.
Since both the x axis and the x labels are drawn in a single function and you have no other variables you can mess around with (safely) this means you'll have to change the actual scale draw function.
Look for a ctx.translate towards the end of the draw function and change it to
ctx.translate(xPos, (isRotated) ? this.endPoint + 22 : this.endPoint + 18);
You'll also have to adjust the endpoint (which drives the y limits) a bit so that the additional y offset doesn't cause the labels to overflow the chart (look for the line adjusting this in the draw override for 2.).
Leaving a gap on the Right Side
To do 2, you override your draw function (in your extended chart) and change xScalePaddingRight. However since this doesn't affect your horizontal grid lines you have to overlay a filled rectangle once your draw is complete. Your complete draw function would look like this
draw: function(){
// this line is for 1.
if (!this.scale.done) {
this.scale.endPoint -= 20
// we should do this only once
this.scale.done = true;
}
var xScalePaddingRight = 120
this.scale.xScalePaddingRight = xScalePaddingRight
Chart.types.Bar.prototype.draw.apply(this, arguments);
this.chart.ctx.fillStyle="#FFF";
this.chart.ctx.fillRect(this.chart.canvas.width - xScalePaddingRight, 0, xScalePaddingRight, this.chart.canvas.height);
}
Original fiddle - https://jsfiddle.net/gvdmxc5t/
Fiddle with modified Scale draw function - https://jsfiddle.net/xgc6a77a/ (I turned off animation in this one so that the endpoint is shifted only once, but you could just hard code it, or add some extra code so that it's done only once)
The 'tickMarkLength' option extends the grid lines outside the chart and pushes the ticks down.
xAxes: [
{
gridLines: {
tickMarkLength: 15
},
}
]
use this for chartjs 2.0
scales: {
xAxes: [{
barPercentage: 0.9,
categoryPercentage: 0.55
}]
Reference
In chartjs v3, there is an "offset" flag that you can set to true. This will create padding.
scales: {
x: {
offset: true,
}
}
If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a bar chart by default.
Documentation