highcharts / highcharts

Highcharts JS, the JavaScript charting framework
https://www.highcharts.com
Other
12.04k stars 3.63k forks source link

Alignment of chart's inner plot for rendering n x m charts in a grid #21297

Open nishikantparmariam opened 4 months ago

nishikantparmariam commented 4 months ago

Description of the feature

We're trying to render our charts in an n x m grid and share say y-axis / x-axis / both similar to Matplotlib Subplots. We've added synchronized zooming feature following this blog example. We want that whenever y-axis of charts in a row is shared, only first chart's y-axis is shown.

Problem

Reproducer - https://jsfiddle.net/ac3zo5Lp/5/

image

Charts can have different no. of series legends (that may/may not come in a single line), some charts could have say x-titles (could be multi-lined as well) and some not, axis labels could be rotated, some charts could have subtitles etc.

Due to this the charts aren't aligned properly making the whole axis sharing feature unusable. We can notice that chartWidth and chartHeight of both the charts are same but their plot areas are different due to different margins.

Library related to the feature

Highcharts

Proof of Concept/Live example for the feature

Solution - https://jsfiddle.net/ac3zo5Lp/4/

image

We wrote some logic that re-adjusts charts after an initial render using chart.plotLeft and other properties. We can notice that now chartWidths became different but plotWidths and plotHeights became same to align them as per our requirement of axis sharing.

Problem

Our charts are large and chart.update on each chart could take ~200ms, so if someone renders 15 charts, they need to wait 3s more that hampers user experience. We need to sometimes pay 60-70% (of total initial charts rendering time) additional cost just for alignment fixes.

Is there a way to heuristically approximate what plotLeft, plotTop, plotWidth and plotHeight will be based on chart config before render so we can add margins, height and width in config and then render charts.

Open for any ideas!

TorsteinHonsi commented 4 months ago

Thanks for sharing the idea!

I assume you're aware of the chart.margin option? It lets you hard code margins for all charts, so they are guarantee to align. What it does not do is to adjust to the greatest labels between charts, so that for example a long label in chart B would adjust the margin in chart A.

So I assume that want you want is an option or plugin to keep margins synchronized between chart instances.

nishikantparmariam commented 4 months ago

So I assume that want you want is an option or plugin to keep margins synchronized between chart instances.

Yes, solution like that should work for us, given total extra time is not that much.

nishikantparmariam commented 4 months ago

Hi @TorsteinHonsi, can you please provide any ETA for this? Alignment is a heavily requested feature by our users. We're interested in a plugin that aligns all charts with minimal overhead as compared to chart.update.

Thanks again!

hubertkozik commented 4 months ago

Hi @nishikantparmariam! Here's a simple plugin that adds an event fired when the chart is loaded: https://jsfiddle.net/BlackLabel/Lexf1szt/

The plugin is inside IIFE, so if you want to use it in your project you can copy-paste the IIFE.

nishikantparmariam commented 4 months ago

Hi @hubertkozik - thanks for checking! This still does chart.update that is a problem for us as posted originally.

For now, we have been thinking to render heavy charts (say with display set as none) with dummy (minimal) data, get margins and then render actual charts. We're open to other solutions that does better than using chart.update.

TorsteinHonsi commented 4 months ago

@nishikantparmariam I'll have a look and see if I can work the dynamic updating problem.

TorsteinHonsi commented 4 months ago

@nishikantparmariam Here's what I've got so far:

Fiddle: https://jsfiddle.net/highcharts/etx2znh0/ PR: https://github.com/highcharts/highcharts/pull/21322

Please report back if it's working for you, or what we could improve. Some thoughts:

nishikantparmariam commented 4 months ago

Thanks @TorsteinHonsi for checking this! I was testing this solution with large data for comparing if it is better than chart.update solution.

I tried (with just adjustment of only plotTop for now), it draws the first chart incorrectly when N > 5 and max plotTop does not seem to be applied - https://jsfiddle.net/8fu34ajx/, can you please check?

image

Is it also possible to adjust chartWidth/chartHeight using this approach? If yes, how?

I am interested in testing the performance of the solution to comment further. Thanks!

TorsteinHonsi commented 4 months ago

@nishikantparmariam Your case works if I add a line chart.isDirtyBox = true: https://jsfiddle.net/highcharts/2mvfyjxs/

Is it also possible to adjust chartWidth/chartHeight using this approach? If yes, how?

Do you mean the full chart width and chart height, or just the plot area (plotLeft, marginRight)?

nishikantparmariam commented 4 months ago

Your case works if I add a line chart.isDirtyBox = true

Thanks! This works!

Do you mean the full chart width and chart height,

Yes, the full chart width and height - asking because in original comment - https://github.com/highcharts/highcharts/issues/21297#issue-2340170835, I also had to adjust chart width because one chart had y-labels for keeping the plot area same.

I tried testing performance of this and observed that if the last chart is deciding chart (e.g. has most margins) then we are redrawing every previous chart that seems to take some time - https://jsfiddle.net/frqg0n4s/1/

image