trying to get minIndex and maxIndex from x-axis-0 - chart.js

I'm attempting to capture data as shown in this question Chart.js get array of currently visible points on graph and am getting most of the way there. However, in inspecting the properties of my x-axis-0, I'm not locating minIndex or maxIndex as detailed in the thread. I do have max and min properties, but those do not appear to work for slicing, as far as my attempts have gone. Curious if those more expert in chartjs can help me figure out the minIndex and maxIndex variables.
editing for more detail:
So I think maybe the issue is that in the link provided, the example is using categorical data and in my own example I have the xaxis as time data? I tried jsfiddle and I see minIndex and maxIndex on the category type whereas I don't see those in my time type axis.
I see the following when I do a console log of the x-axis-0
n {id: "x-axis-0", type: "time", options: {…}, ctx:
CanvasRenderingContext2D, chart: en, …}
bottom: 475
chart: en {id: 0, ctx: CanvasRenderingContext2D, canvas: canvas#myChart.chartjs-render-monitor, config: {…}, width: 1110, …}
ctx: CanvasRenderingContext2D {canvas: canvas#myChart.chartjs-render-monitor, globalAlpha: 1, globalCompositeOperation: "source-over", filter: "none", imageSmoothingEnabled: true, …}
fullWidth: false
height: 15.76
hidden: false
id: "x-axis-0"
labelRotation: 0
left: 35.781484375
longestLabelWidth: 0
longestTextCache: {normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif: {…}}
margins: {left: 35.781484375, right: 3, top: 0, bottom: 0}
max: 1614922685400.2363
maxHeight: 237.5
maxWidth: 1074.218515625
min: 1614922568591.0784
minSize: {width: 1074.218515625, height: 15.76}
options: {display: true, position: "bottom", offset: false, gridLines: {…}, scaleLabel: {…}, …}
paddingBottom: 0
paddingLeft: 3
paddingRight: 3
paddingTop: 0
position: "bottom"
right: 1107
ticks: []
top: 459.24
type: "time"
weight: 0
width: 1071.218515625
_adapter: rn {options: {…}}
_endPixel: 1107
_gridLineItems: [ticksLength: 0, borderValue: 459.5]
_labelItems: []
_labelSizes: {first: {…}, last: {…}, widest: {…}, highest: {…}}
_layers: ƒ ()
_length: 1071.218515625
_majorUnit: undefined
_maxLabelLines: 0
_offsets: {start: 0, end: 0, factor: 1}
_reversePixels: false
_startPixel: 35.781484375
_table: (2) [{…}, {…}]
_ticks: []
_ticksToDraw: []
_timestamps: {data: Array(3601), datasets: Array(2), labels: Array(3616)}
_unit: "hour"
__proto__: n
edit 2:
I created a jfiddle for this https://jsfiddle.net/d489uesh/ Just for experimenting I did a console log for the value of minIndex and it appears to only be 0 or 1, and it does not work at all if I use time as the type of axis. The end goal of all of this is to use the array of retrieved y-values to calculate an average for the drag highlight zoom selection, I feel like I'm so close, so appreciate any assistance!

As you have mentioned, console log of the x-axis-0 does not have the min and max index. I was working in a project that requires almost identical solution to yours hence had to find a way around.
I added the moment.min.js and added time type to x-axis because drag zoom doesn't seem to work without it:
type: 'time',
time: { parser: 'HH:mm:ss', displayFormats: { second: "HH:mm:ss" } },
I got the first and last value from chart.scales["x-axis-0"].ticks after zoom and got the desired min and max index from chart.data.labels and the rest was easy.
var data = chart.data.labels;
var l = chart.scales["x-axis-0"].ticks;
l = l.length -1;
var min = data.indexOf(chart.scales["x-axis-0"].ticks[0]);
var max = data.indexOf(chart.scales["x-axis-0"].ticks[l]);
document.getElementById("vv").innerText = JSON.stringify(chart.data.datasets[0].data.slice(min, max +1));
Here is the jsfiddle:
https://jsfiddle.net/ModeNashid/84zuqwLa/3/
Note that I did not check the versions of any of the resources that you have included in your jsfiddle so there might be some warnings.

Related

Chartjs: Set minimum value for zoom on drag and proper user feedback

I am using Chartjs 4.0.1 and chartjs-plugin-zoom 2.0.0 and my chart look like this:
I have set the drag option to be enabled so the user can draw a rectangle to zoom in. Also I have set the zoom mode to 'x'. So the user can only zoom in on the x axis but not on the y axis.
Now I want to limit how far the user can zoom in, to a timespan of one month. I have managed to do that when using the mousewheel to zoom in. But I dont know how to achive the same when using the drag option. I have it configured like this:
drag:{
enabled: true,
backgroundColor:'rgba(180,180,180,0.4)',
threshold: 25,
}
The threshold seems to be my best option to a limit. However that is in pixels and it only says how wide the drawn rectangle has to be for a zoom to occur.
I am already using the onZoomStart callback to check how far the chart is zoomed in and based on that decide if the user can zoom in even more. But apparently that callback is only executed when zooming by mousewheel but not when dragging. So I think I would need to be able to set the threshold of the drag object dynamically. Does anyone know how to do that?
Also I was wondering, is it possible to change the border color of the rectangle when dragging to show the user if it is big enough for a scroll to occur?
The standard solution seems to be to set a limits:{x:{minRange:...}} option. It took me a while to realise where that option should be inserted.
Below is a code snippet with some data resembling yours and a minRange set to 90 days (so I can skip adjusting the tick interval).
Also, there's a hack that changes the color of the drag rectangle to red if the interval is less than the 90 days. It can easily be adapted to completely reject the zoom for less than the desired interval, instead of the current standard behavior which is to adjust (extend) the interval until it is equal to minRange.
The same in this fiddle.
const nPoints = 400,
t0 = Date.parse("2018-06-02T00:00:00Z"),
dt = 2.5*365/nPoints*24*3600*1000;
const data = Array.from(
{length: nPoints},
(_, i)=>({
"timestamp":(t0+dt*i),
value: 80*Math.sin(i*Math.PI/nPoints)+2*Math.random()
})
);
let mouseMoveHandler = null;
chart = new Chart(document.getElementById("myChart"), {
type: 'line',
data: {
datasets: [{
label: "Count",
//pointStyle: false,
pointRadius: 2,
showLine: true,
fill: true,
tension: 0,
borderColor: '#aa6577',
//pointRadius: 4,
//pointBorderWidth: 1,
//pointBackgroundColor: '#7265ce',
data: data
}]
},
options: {
parsing: {
xAxisKey: 'timestamp',
yAxisKey: 'value'
},
spanGaps: false,
responsive: false,
scales: {
x: {
bounds: 'ticks',
type: 'time',
time: {
unit: 'month',
},
title: {
display: false,
text: 'time'
},
ticks: {
display: true,
color: '#cecece'
}
},
y: {
type: 'linear',
display: true,
min: -10,
max: 140,
ticks: {
autoSkip: true,
color: '#cecece'
},
grid:{
color: ctx => ctx.tick.value === 0 ? '#000' : '#ddd',
lineWidth: ctx => ctx.tick.value === 0 ? 3 : 1,
},
title: {
display: false,
text: 'Count',
align: 'end'
},
}
},
plugins:{
legend:{
display: false
},
zoom: {
zoom: {
drag: {
enabled: true,
backgroundColor:'rgba(180,180,180,0.4)',
},
mode: 'x',
onZoomStart({chart, event}){
const x0 = chart.scales.x.getValueForPixel(event.clientX);
if(event.type==="mousedown"){
mouseMoveHandler = function(e){
if(
Math.abs(chart.scales.x.getValueForPixel(e.clientX) - x0) <
chart.options.plugins.zoom.limits.x.minRange
){
chart.options.plugins.zoom.zoom.drag.backgroundColor = 'rgba(255,180,180,0.4)';
}
else{
chart.options.plugins.zoom.zoom.drag.backgroundColor = 'rgba(180,180,180,0.4)';
}
};
chart.canvas.addEventListener("mousemove", mouseMoveHandler);
chart.canvas.addEventListener("mouseup", function(){
if(mouseMoveHandler){
chart.canvas.removeEventListener("mousemove", mouseMoveHandler);
mouseMoveHandler = null;
}
}, {once: true});
}
},
onZoomComplete({chart}){
if(mouseMoveHandler){
chart.canvas.removeEventListener("mousemove", mouseMoveHandler);
mouseMoveHandler = null;
}
document.querySelector('#zoom').innerText = chart.getZoomLevel().toFixed(1)+'x';
document.querySelector('#xSpan').innerText =
Math.round((chart.scales.x.max-chart.scales.x.min)/24/3600/1000)+'days';
}
},
limits:{
x: {
minRange: 90 * 24* 3600 * 1000
}
}
}
}
}
});
document.querySelector('#resetZoom').addEventListener('click', function(){chart.resetZoom();});
document.querySelector('#xSpan').innerText = Math.round((chart.scales.x.max-chart.scales.x.min)/24/3600/1000)+'days';
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.0.1/chart.umd.min.js"
integrity="sha512-HyprZz2W40JOnIBIXDYHCFlkSscDdYaNe2FYl34g1DOmE9J+zEPoT4HHHZ2b3+milFBtiKVWb4sorDVVp+iuqA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/2.0.0/chartjs-plugin-zoom.min.js"
integrity="sha512-B6F98QATBNaDHSE7uANGo5h0mU6fhKCUD+SPAY7KZDxE8QgZw9rewDtNiu3mbbutYDWOKT3SPYD8qDBpG2QnEg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js">
</script>
<canvas id="myChart" style="height:500px; width: 90vw"></canvas>
<button id="resetZoom">Reset zoom</button> <br>
zoom: <span id="zoom">1x</span><br>
X axis span: <span id="xSpan"></span>

ApexCharts skipping X axis ticks

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)

Chart.JS 2 automatic horizontal scroll upon changes on xAxis range not working anymore on Chart.JS 3?

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();

ApexCharts bubble - Change quarter background color

I am using ng-apexcharts, and I want to change only quarter of the background to another color like in the following example:
enter image description here
Currently using annotations with offestY, but we cant understand what is the offestY value and how we can make it 50% of the graph size.
Code example:
chart: {
type: 'bubble',
height: auto,
...
},
annotations: {
xaxis: [
{
x: 50,
x2: 100,
fillColor: '#f15252',
opacity: 0.1,
offsetY: -70, // How to calculate this value to be exactly 50% offest?
}
],
},
thanks!
I found a solution, maybe it will help someone:
events: {
mounted: (chart, options) => {
const offsetX = options.globals.gridWidth / 2;
this.chart.addYaxisAnnotation({
y: 5,
y2: 10,
fillColor: '#f15252',
opacity: 0.1,
offsetX,
});
},
}

Decimal in x-axis

I'm trying to create a line chart with chart.js where the data in a dataset contains points:
data: [{x:0,y:5},{x:2.1,y:4.3},{x:3.9,y:3}]
The x values of those points contain decimals. The resulting chart looks like this:
Instead of showing the second data point at (2.1 | 4.3) it is drawn at (1 | 4.3)!
Can someone point me into the right direction on this one?
The code to reproduce this behavior:
<body>
<div>
<canvas id="myChart" width="400" height="200"></canvas>
</div>
<script>
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
datasets: [{
label: 'some label',
data: [{x:0,y:5},{x:2.1,y:4.3},{x:3.9,y:3}],
borderColor: 'red',
fill: false,
lineTension: 0
}]
},
options: {
scales: {
xAxes: [{
ticks: {
max: 10,
min: 0,
stepSize: 0.1
}
}],
yAxes: [{
ticks: {
max: 6,
min: 0,
stepSize: 1
}
}]
}
}
});
</script>
</body>
For the y-axis, using the stepSize variable of the ticks option does the trick. Doing the same for the x-axis is a little more complex but not impossible.
The first issue is that your x-axis labels are at integer intervals so the chart has nowhere to position the decimal values than at the closest integer. This can be fixed by specifying all the values in the desired intervals (ie: 0.1, 0.2,...,3.0) in the labels.
Then, making use of the autoSkip and maxTicksLimit, you can hide the labels not needed and so create the chart seen in your image, but with the points in the correct position.
I made a working version of this using your code in this fiddle. Hope this helps!