I have a chartjs linechart diagram to show the sales of different products on a range of dates. The user can select a date range (for example from 2015-12-01 to 2015-12-10) to view the sales per day and thats fine and its working.
But if the user selects only one day (range from for example 2015-12-01 to 2015-12-01), he gets the correct diagram, but it doesn't look good:
As you can see, the points are stick to the y-axis. Is there a possibility, to center the points on the diagram?
Thats how it should look like:
Instead of hardcoding the labels and values with blank parameters, use the offset property.
const options = {
scales: {
x: {
offset: true
}
}
}
Documentation: https://www.chartjs.org/docs/latest/axes/cartesian/linear.html#common-options-to-all-cartesian-axes
You can check the length of your labels (or data) arrays and add dummy non-renderable points to the left and right by using empty string labels and null value, like so
var chartData = {
labels: ['', "A", ''],
datasets: [
{
fillColor: "rgba(255, 52, 21, 0.2)",
pointColor: "#da3e2f",
strokeColor: "#da3e2f",
data: [null, 20, null]
},
{
fillColor: "rgba(52, 21, 255, 0.2)",
strokeColor: "#1C57A8",
pointColor: "#1C57A8",
data: [null, 30, null]
},
]
}
Fiddle - https://jsfiddle.net/pf24vg16/
Wanted to add to the above answer and say that I got a similar effect on a time series scatter plot using this:
if (values.length === 1) {
const arrCopy = Object.assign({}, values);
values.unshift({x: arrCopy[0].x - 86400000, y: null});
values.push({x: arrCopy[0].x + 2 * 86400000, y: null});
}
That only handles for a single point, however. To add in functionality for multiple points, I did the following:
const whether = (array) => {
const len = array.length;
let isSame = false;
for (let i = 1; i < len; i++) {
if (array[0].x - array[i].x >= 43200000) {
isSame = false;
break;
} else {
isSame = true;
}
}
return isSame;
}
if (values.length === 1 || whether(arr[0])) {
const arrCopy = Object.assign({}, values);
values.unshift({x: arrCopy[0].x - 86400000, y: null});
values.push({x: arrCopy[0].x + 2 * 86400000, y: null});
}
You might notice I'm just subtracting/adding a day in milliseconds into the x values. To be honest, I was just having the worst of times with moment.js and gave up haha. Hope this helps someone else!
Note: my code has a tolerance of 43200000, or 12 hours, on the time. You could use moment.js to compare days if you have better luck with it than I did tonight :)
For your specific problem, try to modify the options->scales->xAxes option like so:
options: {
title: {
display: true,
text: 'mytitle1'
},
scales: {
xAxes: [{
type: 'linear',
ticks: {
suggestedMin: 0,
suggestedMax: (11.12*2),
stepSize: 1 //interval between ticks
}
}],
More info at: Chart JS: Ignoring x values and putting point data on first available labels
Related
I would like to present a bar chart using Apex Charts.
The graphs shows some items, the more items the longer you can do some stuff. The items are presented in the Y axis. The X axis should represent months in a year, therefore I want it to be fixed between 0 and 12.
With the following options, it skips some numbers in the x axis:
var options = {
series: [{ data: [3,4,6,7,8.5] }],
chart: { type: 'bar', height: 250 },
plotOptions: {
bar: { borderRadius: 4, horizontal: true }
},
dataLabels: { enabled: false },
xaxis: {
categories: [2,3,5,6,7],
min: 0,
max: 12,
},
yaxis: {
labels: { formatter: function (val) { return val + ' items'; } },
},
title: {
text: 'Chart title', floating: true, align: 'center',
style: { color: '#444' }
}
};
If I omit the min/max in the xaxis, it works fine but the 12 months are not fixed as required.
In the example the 9 is skipped, but the actual bars are scalled correctly
I am loading the library using de direct script include:
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
Here you can find the official example/docs: https://apexcharts.com/javascript-chart-demos/bar-charts/basic/
Have I overlooked something? Or is it a bug?
Setting tickAmount to 12 seems to fix the problem, but I does not behave correctly with other numbers. (if I use 13 then it inserts two 6 tiks instead of inserting decimals)
I previously had a scatter graph in Chart.js 2 with the zoom plugin, in which I just had to add data and change the xAxis ticks' min/max to see a nice animated horizontal scroll from my old xAxis range to my new xAxis range.
For some reason I now use Chart.js 3.24 and the zoom plugin 1.1.1. Now the same graph with nearly the same options became quite ugly when animated:
newly added points e.g. at position (X,Y) have an animation going from (X,0) to (X,Y), instead of appearing directly at (X,Y).
when a point from a dataset is at the same position than a line from another dataset, they move at a different speed.
if many points and line are added, sometimes the lines are only shown after the "scrolling animation" ended.
sometimes a point appear at its correct final position before the "scrolling animation" even started.
The only solution I found was to disable animation when updating my graph, then use window.requestAnimationFrame to manually pan the graph myself with the zoom plugin.
Are you aware of a better/simpler recommended way to achieve this ?
Thanks in advance,
My old Chart.js options:
let zoom_options = {
pan: {
enabled: true,
mode: 'x',
rangeMin: { x: 0, y: null},
rangeMax: { x: null, y: null}
},
zoom: {
enabled: true,
drag: false,
mode: 'x',
rangeMin: { x: 0, y: null },
rangeMax: { x: null, y: null },
speed: 0.1
}
};
var ctx = graph.getContext('2d');
this._chartjs = new Chart(ctx, {
type: 'scatter',
data: {
datasets: []
},
options: {
legend: {
//display: false
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
}]
},
plugins: {
zoom: zoom_options // https://github.com/chartjs/chartjs-plugin-zoom
}
}
});
My old line options :
{
label: name,
showLine: false,
fill:false,
spanGaps: false, // I have some NaN values in my datasets.
backgroundColor: color.points_borders,
borderColor: color.line,
borderWidth:5,
pointRadius:5,
pointBorderWidth:2,
pointBorderColor: color.points_borders,
pointBackgroundColor: color.points,
data: []
}
My old update function:
this._chartjs.options.scales.xAxes[0].ticks.min = min;
this._chartjs.options.scales.xAxes[0].ticks.max = length;
this._chartjs.update();
I have this chart:
...which is displaying exactly how I want it to with one exception... The data in the bars is for between the two times in the x axis... so all the labels need shifting to lie on the grid lines, not between them as default for a bar chart. So the red and blue bar is data between 8:00 and 9:00. I hope I've explained that clearly enough.
I'm trawling through the Chart.js docs and it just doesn't seem like this is possible! I know I could change my labels to be, for example, 8pm - 9pm, but that seems a much more visually clunky way of doing it. Is there a way anyone know of achieving this? Ideally there would be another '12am' on the last vertical grid line too.
You can draw the tick lables at the desired position directly on to the canvas using the Plugin Core API. It offers number of hooks that may be used for performing custom code. In below code snippet, I use the afterDraw hook to draw my own labels on the xAxis.
const hours = ['00', '01', '02', '03', '04', '05', '06'];
const values = [0, 0, 0, 0, 10, 6, 0];
const chart = new Chart(document.getElementById('myChart'), {
type: 'bar',
plugins: [{
afterDraw: chart => {
var xAxis = chart.scales['x-axis-0'];
var tickDistance = xAxis.width / (xAxis.ticks.length - 1);
xAxis.ticks.forEach((value, index) => {
if (index > 0) {
var x = -tickDistance + tickDistance * 0.66 + tickDistance * index;
var y = chart.height - 10;
chart.ctx.save();
chart.ctx.fillText(value == '0am' ? '12am' : value, x, y);
chart.ctx.restore();
}
});
}
}],
data: {
labels: hours,
datasets: [{
label: 'Dataset 1',
data: values,
categoryPercentage: 0.99,
barPercentage: 0.99,
backgroundColor: 'blue'
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'HH',
unit: 'hour',
displayFormats: {
hour: 'Ha'
},
tooltipFormat: 'Ha'
},
gridLines: {
offsetGridLines: true
},
ticks: {
min: moment(hours[0], 'HH').subtract(1, 'hours'),
fontColor: 'white'
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>
I would like to show y as time and x and volume of something
I basically if it takes 8.5 secs to do say 10 000 volume or 1000 000
I would like to have time on the x axis and volume on the y axis.
Time is not relative to a date, its time as in ticks, so its 0 start.
struggling to find a gd example to work from,
I have done quick POC's but seems like there should be and easier example to base my work from.
aka i believe i need to do some calculation to get the ranges, but even if i had the ranges unsure which chart type and setting would be best fit.
I want to display labels to the bottom displaying time in ms/s
[0,100,200,400, 800, 10000, 15000] ms - could then try some formatting options for divide by 1000 for secs.
strarted https://jsfiddle.net/co43r6ef/ but again seems like wrong direction.
also toying with but also seems iffy also wrong direction, is anyone else able to point me to an example... i can leverage.
to clarify it should start at 0 and move forward in time along x to the peek.
So when looking at the chart u can look at the peek and under it with a line going down it should be the time taken.
// var labels = []
// var tickms = 325362132 / 10000;
// for (let i = 0; i < tickms ; i++) {
// if (i%1000 === 0) {
// labels.push(i)
// }
// }
var ctx = $("#canvas")[0].getContext("2d");
var data = {
datasets: [
{
label: "Scatter Dataset",
// labels: labels,
data: [
{ x: 0, y: 0},
{ x: 10, y: 1000 },
{ x: 20, y: 2000 },
{ x: 30, y: 3000 },
{ x: 40, y: 6000 },
{ x: 50, y: 8000 },
{ x: 1066, y: 10000 }
]
}
]
};
var myLineChart = new Chart(ctx, {
type: "line",
data: data,
options: {
scales: {
xAxes: [
{
scaleLabel: {
display: true
},
}
]
}
}
});
You should be fine when you add
options: {
scales: {
xAxes: [{
type: 'linear'
}]
}
}
Here's an example: https://jsfiddle.net/1sxrtcw5/1/
How to align zeros on chart with multi axes, if there are both positive and negative values in dataset?
I want zeroes to be on the same line.
I dont like this:
Graph image
link to jsfiddle
new Chart(canvas, {
type: 'line',
data: {
labels: ['1', '2', '3', '4', '5'],
datasets: [{
label: 'A',
yAxisID: 'A',
data: [-10, 96, 84, 76, 69]
}, {
label: 'B',
yAxisID: 'B',
data: [-2, 3, 5, 2, 3]
}]
},
options: {
scales: {
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
}, {
id: 'B',
type: 'linear',
position: 'right',
}]
}
}
});
actualy, The example on the official web page has the same problem. Looks so messy.
The answer above is great if you know the ranges in advance and can specify min and max for each ticks object in advance. If, like me, you're trying to produce the effect with generated data, you'll need something that computes these values for you. The following does this without recourse to hard-coded values. It does generate ugly max / min values - but there's another fix for that: Hide min and max values from y Axis in Chart.js. It may also generate less than ideal spacing in which case a simple fudge factor applied to the min and max values may be used.
function scaleDataAxesToUnifyZeroes (datasets, options) {
let axes = options.scales.yAxes
// Determine overall max/min values for each dataset
datasets.forEach(function (line) {
let axis = line.yAxisID ? axes.filter(ax => ax.id === line.yAxisID)[0] : axes[0]
axis.min_value = Math.min(...line.data, axis.min_value || 0)
axis.max_value = Math.max(...line.data, axis.max_value || 0)
})
// Which gives the overall range of each axis
axes.forEach(axis => {
axis.range = axis.max_value - axis.min_value
// Express the min / max values as a fraction of the overall range
axis.min_ratio = axis.min_value / axis.range
axis.max_ratio = axis.max_value / axis.range
})
// Find the largest of these ratios
let largest = axes.reduce((a, b) => ({
min_ratio: Math.min(a.min_ratio, b.min_ratio),
max_ratio: Math.max(a.max_ratio, b.max_ratio)
}))
// Then scale each axis accordingly
axes.forEach(axis => {
axis.ticks = axis.ticks || { }
axis.ticks.min = largest.min_ratio * axis.range
axis.ticks.max = largest.max_ratio * axis.range
})
}
You can do this by setting the ticks option on your axes to control the max, min, and stepSize. To make the zeros align, you need to make the axes symmetric. stepSize is optional if you want the gridlines to be aligned as well.
In your example:
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
ticks : {
max: 100,
min: -50,
stepSize: 50
}
}, {
id: 'B',
type: 'linear',
position: 'right',
ticks : {
max: 6,
min: -3,
stepSize: 3
}
}]
See updated fiddle: https://jsfiddle.net/x885kpe1/1/