apexcharts / apexcharts.js

📊 Interactive JavaScript Charts built on SVG
https://apexcharts.com
MIT License
14.46k stars 1.31k forks source link

yaxis min max values cause line chart to misalign from grid #3974

Closed Darhagonable closed 8 months ago

Darhagonable commented 1 year ago

Description

Steps to Reproduce

  1. Create simple line chart with data [5, 8, 12, 9, 11, 14, 10]
  2. Set yaxis min max values to 1 and 15
  3. Watch how the peak x6 doesn't match up with the grid line of y14 but its value is 14. Similarly with x4 doesn't line up with y9.

Expected Behavior

Value of 9 and 14 to align with the yaxis gridlines of 9 and 14.

Actual Behavior

Value of 9 is underneath the y9 line and value 14 is step above the y14 line

Screenshots

image

Reproduction Link

https://codepen.io/Darhagonable/pen/jOXPEMP

geigertom commented 1 year ago

Min-Values on the xaxis also cause horizontal bars to not to be drawn precisely, the bar overlaps the grid on the left side: grafik whereas they are drawn correctly without the min-values: grafik

https://codepen.io/geigertom/pen/JjwYwWW

DanielJMHoffman commented 1 year ago

I have the same issue with range charts (timelines), when providing min/max values (thanks for opening this, as I had no idea it had to do with the min/max values).

As soon as you use the hand tool to move the chart, it suddenly moves the bars (or x-axis) to the correct position to align properly: image

And after dragging with the hand tool:

image

rocso commented 10 months ago

I believe the problem is that the grid lines are not drawn as per the labeling or, more succinctly, the values that represent the grid line are rounded to show as the label. The graph is drawn correctly. If you increase the decimals of the labeling from 0 to 1 or 2, you'll see the problem. The grid line at 14 is actually 13.something.

Further down you see that the labels go: 1 2 4 5 7 etc. I.e. the increments displayed with decimals set to 0 are uneven.

The strategy for drawing grids appears to be back to front: the axis range needs to be determined to fit the data series then the grid lines need to be determined at values that match the label values as shown. At the moment it appears to be: grid lines at position = (max - min)/tickAmount, then the labels are set to round(position, decimals).

Also, note: if the data series is all integers, the labels are forced to decimalsInFloat = 0, hence the inclusion of the 9.0001 value in the example data below. If that is intended then it is even more important that the grid lines also be integers in that case.

Here's the JS:

var options = { chart: { type: 'line' }, series: [{ data: [5, 8, 12, 9.0001, 11, 14, 10] }], yaxis: { tickAmount: 10, decimalsInFloat: 2, max: 15, min: 1 } }

var chart = new ApexCharts(document.querySelector("#chart"), options);

chart.render();

And here's the result:

e64t4lfd

rocso commented 10 months ago

The following appears to solve the OP issue if they will accept that the supplied min and max (and tickAmount if included) will be treated as advisory:

var options = {
  chart: {
    type: 'line'
  },
  series: [{
    data: [5, 8, 12, 9.01, 11, 14, 10]
  }],
  yaxis: {
    max: 15,
    min: 1,
    tickAmount: 9,
    decimalsInFloat: 0,
    forceNiceScale: true
  }
}

var chart = new ApexCharts(document.querySelector("#chart"), options);

chart.render();

yohz652z

Here's the proof that the ticks accurately reflect the labels:

nxluo90g

Darhagonable commented 10 months ago

The following appears to solve the OP issue if they will accept that the supplied min and max (and tickAmount if included) will be treated as advisory:

var options = {
  chart: {
    type: 'line'
  },
  series: [{
    data: [5, 8, 12, 9.01, 11, 14, 10]
  }],
  yaxis: {
    max: 15,
    min: 1,
    tickAmount: 9,
    decimalsInFloat: 0,
    forceNiceScale: true
  }
}

var chart = new ApexCharts(document.querySelector("#chart"), options);

chart.render();

yohz652z

Here's the proof that the ticks accurately reflect the labels:

nxluo90g

In my real life use case all graphs are dynamic. With different spans. Ideally Apexcharts would automatically calculate nice round ticks and a fitting tickAmount by default. Was looking into how that would be done but seems pretty complicated/hard.

rocso commented 10 months ago

In my real life use case all graphs are dynamic. With different spans. Ideally Apexcharts would automatically calculate nice round ticks and a fitting tickAmount by default. Was looking into how that would be done but seems pretty complicated/hard.

I'm here because I had an issue with duplicate labels and labels that were obviously not matching the tick lines as drawn. I'm also displaying dynamic graphs that often have small floating ranges, so no min or max specified (I'm using the apexgraphs card in Home Assistant to chart nearly constant room temperature and now have it working well enough I think).

Setting "forceNiceScale: true" by default would make it as automatic is it's probably possible to make it, but that would affect existing users, so I would not expect it to change. According to code comments, the algorithm used came from this discussion: https://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis

rosco54 commented 8 months ago

@junedchhipa This issue is resolved in revisions 3.46.0, and now 3.47.0.

Darhagonable commented 8 months ago

@rosco54 Awesome! Thank you for fixing this!