chartjs / Chart.js

Simple HTML5 Charts using the <canvas> tag
https://www.chartjs.org/
MIT License
64.85k stars 11.93k forks source link

"responsive" option problem since v4 #11005

Open ouqu5 opened 1 year ago

ouqu5 commented 1 year ago

Expected behavior

Chart should be resized when window size changes. (now it resizes only when scaling to smaller resolution)

Current behavior

I am using "responsive" option to resize chart based on it's parent container size. Everything works while scaling window size down, but when it comes to changing window resolution to larger one that doesn't work.

Reproducible sample

https://codepen.io/ouqu5/pen/ZEjEPqm

Optional extra steps/info to reproduce

  1. Resize window to mobile/tablet (chart resizes properly)
  2. Resize it back to full screen (chart is not resized)

Possible solution

No response

Context

No response

chart.js version

4.1.1

Browser name and version

No response

Link to your project

No response

igomonteiro commented 1 year ago

Hi ouqu5, have you tried to set the option maintainAspectRatio to false? And also, you will probably need to set the width and height in the parent container, not in the canvas element.

Read this docs about responsiveness: Responsive charts, read the important note about responsive charts and also check this example which is full responsive.

ouqu5 commented 1 year ago

I know that maintainAspectRatio fixes this problem but it should work event with this option enabled. I've followed all steps in docs so my chart setup is 100% valid.

Example you've send works because it's using chart.js 3.5.1 version while problem occurs only from version 4 of this library.

igomonteiro commented 1 year ago

@ouqu5 the example also works with the latest version

legege commented 1 year ago

There is indeed an issue with version 4.x. When reducing size of container, it works. But not when increasing. Last working version 3.9.1.

ouqu5 commented 1 year ago

@igomonteiro example works only because of maintainAspectRatio: false. Try enabling it, as it is by default.

jonbuda commented 1 year ago

seeing this issue as well, with 4.1.1. in most cases. I would think this should work regardless of what maintainAspectRatio is set to.

JordanXA commented 1 year ago

Any update on this? Still appears to be an issue in 4.2.0

ipax77 commented 1 year ago

From my experience you have to define the height of the container, too, now. maintainAspectRatio respects container height.

JordanXA commented 1 year ago

By setting an aspect-ratio on the containing div I was able to get the same responsiveness for graphs using an aspect ratio. This confuses me because it seems like maintainAspectRatio should control the size of the canvas because if I wanted to size the container I would just size the container in the first place.

pavelspichonak commented 1 year ago

Have this issue as well.

ZLevine commented 1 year ago

I've had this issue as well—I have found a workaround, however. I'm using MUI specifically.

As long as the chart has a sibling that consumes space inside its parent, auto-resizing works. As soon as the chart sits alone in a parent, it will resize down but not up.

This does not work:

  return (
    <Box>
      <Chart
        type="bar"
        datasetIdKey="name" // the dataset property to use as the unique identifier for the data set. See: https://react-chartjs-2.js.org/docs/working-with-datasets/
        options={options}
        data={data}
        plugins={[legendExtraMargin]}
      />
    </Box>

This does not work:

  return (
    <Box>
      <div />
      <Chart
        type="bar"
        datasetIdKey="name" // the dataset property to use as the unique identifier for the data set. See: https://react-chartjs-2.js.org/docs/working-with-datasets/
        options={options}
        data={data}
        plugins={[legendExtraMargin]}
      />
    </Box>

This does work:

  return (
    <Box>
      <div>&nbsp;</div>
      <Chart
        type="bar"
        datasetIdKey="name" // the dataset property to use as the unique identifier for the data set. See: https://react-chartjs-2.js.org/docs/working-with-datasets/
        options={options}
        data={data}
        plugins={[legendExtraMargin]}
      />
    </Box>

YMMV but hopefully this workaround helps. Obviously, being forced to display something is not ideal, but you could adjust the size of the rendered element to mitigate. Interestingly, making it very small (max-height: 1px, font-size: 1px, or line-height: 0.1) causes the resize to animate very slowly ¯\(ツ)\/¯

athisun commented 1 year ago

I was able to fix this without setting an exact container height by defining an aspect-ratio instead. The default aspect ratio for Chart.js is 2, so doing this restores Chart.js 3 behaviour:

<div class="chart-container" style="aspect-ratio: 2">
    <canvas id="chart"></canvas>
</div>
pavelspichonak commented 1 year ago

I would suggest just to fix it instead of using workarounds. This is maybe not critical but important issue.

dan-ville commented 1 year ago

Bumping this issue.

If I pass an onResize function to the options array of a chart component, like onResize: () => {console.log('Resizing chart')}, I am experiencing that the function fires when scaling the chart down, but it doesn't fire when the viewport scales up.

regnaio commented 1 year ago

I was able to fix this without setting an exact container height by defining an aspect-ratio instead. The default aspect ratio for Chart.js is 2, so doing this restores Chart.js 3 behaviour:

<div class="chart-container" style="aspect-ratio: 2">
    <canvas id="chart"></canvas>
</div>

@athisun 's workaround worked for me

Also onResize() is never being called for me with responsive: true...

On version 4.2.1

hairbo commented 1 year ago

I created a duplicate ticket for this issue (I didn't see this ticket prior to creating that one). I created a couple of Stackblitz demos to show the behavior working in 3.9.1 and not working in 4.3.0. I figured I'd cross-post them here:

Here's the 3.9.1 version, where responsiveness works: https://stackblitz.com/edit/angular-9rtbka

...And here's the 4.3.0 version, where responsiveness doesn't work (I also tried with 4.2.1, and got the same behavior): https://stackblitz.com/edit/angular-bcncvn

As noted above, responsiveness works when the browser width shrinks, but the chart doesn't expand as the browser width grows.

Hope this helps! Thanks!

t9luke commented 1 year ago

For me the event was only being triggered when my viewport increased in size, and the height property never seemed to update, only the width.

I was able to fix the issue with with this workaround:

canvas {
  width: 100% !important;
  height: 100% !important;
}

For what it's worth, I'm using Vue.js and including my charts with PrimeVue. The version of chart.js is v4.3.0.

If you're using this fix then the styles need adding globally.

akleimrey commented 1 year ago

I'll throw my hat into this. I have the problem, I - for the life of mine - can not get it to shrink. At all. The example demo seems to work with all relative values, but when I do use percentages in my Angular (+ Tailwind) project, it does not.

<!-- This works! -->
<div class="chart-container w-[80vw] h-[300px]">
    <canvas #chart [id]="chartId" aria-label="Chart" role="img">
        <p>Chart could not be loaded</p>
    </canvas>
</div>

However...

<!-- This works! -->
<div class="chart-container w-[100%] h-[300px]">
    <canvas #chart [id]="chartId" aria-label="Chart" role="img">
        <p>Chart could not be loaded</p>
    </canvas>
</div>

Does not.

athisun commented 1 year ago

I - for the life of mine - can not get it to shrink

@akleimrey This is likely since the new resize behaviour prioritizes maintaining the container height. Have you tried the above method of setting an aspect ratio instead of a height and width? https://github.com/chartjs/Chart.js/issues/11005#issuecomment-1475697112

akleimrey commented 1 year ago

I - for the life of mine - can not get it to shrink

@akleimrey This is likely since the new resize behaviour prioritizes maintaining the container height. Have you tried the above method of setting an aspect ratio instead of a height and width? #11005 (comment)

@athisun I did but to no result. But in a perfect "Rubber Duck" problem-solving moment, I got a bit closer to my problem. My problem is the flexbox, but I can't simply replace my solution. It basically looks currently like this:

<div id="main" class="flex flex-col min-h-[100%] dark:bg-dark-background dark:text-background">
    <app-header></app-header>
    <div id="content" class="lg:w-[75rem] mx-auto">
        <router-outlet>
        </router-outlet>
    </div>
    <app-footer></app-footer>
</div>

And my dashboard page looks rather simple like that:

<app-map></app-map>
<app-chart [plugins]="plugin" chartType="HORIZONTAL_BAR"></app-chart>
<app-chart [plugins]="plugin2" chartType="BAR"></app-chart>

With a single chart component just looking like this:

<div class="chart-container w-[90%] h-[300px]">
    <canvas #chart [id]="chartId" aria-label="Chart" role="img">
        <p>Chart could not be loaded</p>
    </canvas>
</div>

So, the div with the id main is mostly used to at least occupy 100% space but is also used to always at the minimum have the footer at the bottom, without having to define positions. If the content is larger, the page grows nicely. I'm going through solutions to make this work, but apparently, there is no single simple solution ever.

hairbo commented 1 year ago

None of these are permanent solutions, though, right? We have a clear regression in behavior from 3.9 to 4.x when it comes to how responsive charts behave (in particular, when the browser increases in width). There are no indications in the documentation that this is a breaking change--if there were a documented best practice going forward (e.g., some kind of different CSS on the canvas or parent tag), then I think we could all happily upgrade. So until we hear otherwise, this just feels like a bug that needs to get addressed at the library level, rather than library consumers figuring out workarounds which may or may not work.

If I've missed something, and there really is documentation that this is a breaking change and we need to invoke our charts in a different fashion, I retract my above statement.

agm1984 commented 1 year ago

For me the event was only being triggered when my viewport increased in size, and the height property never seemed to update, only the width.

I was able to fix the issue with with this workaround:

canvas {
  width: 100% !important;
  height: 100% !important;
}

For what it's worth, I'm using Vue.js and including my charts with PrimeVue. The version of chart.js is v4.3.0.

If you're using this fix then the styles need adding globally.

This worked for me using PrimeVue 3.25.0 and chart.js 4.3.0.

I am also observing the fact that the options.onResize function is only called when the container gets smaller; it doesn't fire when the container gets larger.

I am also observing the onResize function getting called occasionally in an infinite loop when the container gets smaller. I put a console.log in there and sometimes it logs 100s of times in a few seconds; it seems to catch itself if you continue resizing, but something occurs to make it decide to call onResize infinitely.

elinake commented 1 year ago

I have same behavior with css table-layout: fixed

https://angular-chart-js-nhvr2v.stackblitz.io

When the view grows, charts grow accordingly, when view goes smaller, chart size doesn't change correctly. When outside change is triggered, ie. do onblur from input, chart sizes are correct again. What's more, if I try the workaround of setting canvas size, the behavior changes to the opposite, increasing doesn't work and decreasing does.

I tried to replicate my use case as closely as possible, but it is kind of extreme behavior, so maybe this is not the best example for the problem, but leaving it here anyway in case it helps. It is very stable solution though in the environment I use it, the decreasing size is the only problem.

porteron commented 1 year ago

I have the same problem. When responsive and maintaining aspect ratio the chart does not properly resize on expand. Collapsing works fine.

KillerJulian commented 1 year ago

When the view grows, charts grow accordingly, when view goes smaller, chart size doesn't change correctly.

🐛 Same issue. You have to reload the page after an enlargement to get the correct size of the chart again.

📖 I using chart.js@4.3.0, vue-chartjs@5.2.0 and nuxt@3.5.3.

teyang-lau commented 1 year ago

Same issue. The chart scales down when page size reduces but does not properly expand when page size increases again. I have to reload the page to get back the correct size of the chart.

Example is shown here: https://www.teyanglau.com/ (on the nested radial chart)

hairbo commented 1 year ago

This still appears to be a problem in 4.4. Is there any word at all on the status of this issue? I don't think I've seen any of the library maintainers chime in on this thread.

hairbo commented 1 year ago

@etimberg Apologies for pinging you, but it would be great if you or one of the other maintainers could chime in on this issue. This seems like a clear regression from 3.9.x to 4.x.x, and it's preventing (at least) those of us on this thread from upgrading to 4.x of the library. I can't find anywhere in the docs that indicates this is a breaking change which involves some modification to how we invoke ChartJS for responsiveness.

Thanks!!

sergei-krylov commented 1 year ago

Had the same issue, and for me this proposal solved it - https://github.com/chartjs/Chart.js/issues/11243 So one thing to do was to use aspect-ratio in chart container:

Using the aspect-ratio property it's possible to make a chart fit nicely within the parent container's width instead of the window size.

And what actually fixed the scaling issue when you resize the window to expand it - is to use overflow: hidden:

overflow: hidden is also needed otherwise the height won't change and won't trigger a redraw.

<div class="chart-container" style="position: relative; width: 100%; aspect-ratio: 2/1; overflow: hidden;">
    <canvas id="chart"></canvas>
</div>

That sounds like some sort of css issue, but still it should be addressed in some way by the Chart.js (as a fix, or in docs).

All credits goes to @PeterN !

bmueller-sykes commented 1 year ago

ChartJS also has an aspect ratio attribute. Do you need to set both in order for this to work? Also, do things work correctly if you change the aspect ratio after load?

hairbo commented 1 year ago

@sergei-krylov it looks like that...works? Here's a Stackblitz:

https://stackblitz.com/edit/angular-dhvdfm

That works on a Mac in Chrome, Safari, Edge, and Firefox for me. (Testing was very limited, though)

The one thing to note is that, at least for me, the css aspect-ratio needs to match the aspectRatio option passed into ChartJS. Is that your experience as well? And have you tried changing the aspect ratio dynamically (e.g. when screen size crosses a particular break point, change aspect ratio)?

If this works, then that's great, but it still bothers me that none of the maintainers have chimed in on this front. Without any sort of official word from them that this is the way, I'm somewhat hesitant to upgrade to the 4.x version of the library. This could just be a hack that's masking a more serious issue with the library.

sergei-krylov commented 1 year ago

Do you need to set both in order for this to work?

For that way of doing things - no, you don't need to set aspect ratio in chart options, and even if you do, it seems ignored (at least in my case). Can set it only for container, or match it for both chart and container.

Also, do things work correctly if you change the aspect ratio after load?

Yes.

And have you tried changing the aspect ratio dynamically (e.g. when screen size crosses a particular break point, change aspect ratio)?

I'm changing this "dynamically", but not depends on screen size. In my case I need the chart to stay with fixed aspect ratio no matter the screen size. And only change the aspect when it changes by clicking a button (it's a bit more complicated, but could be just that). And it works well.

What makes it work is having the aspect-ratio in container style, and adding the overflow: hidden to container as well, that's the key thing that triggers the chart to redraw when scaling down.

I am also observing the fact that the options.onResize function is only called when the container gets smaller; it doesn't fire when the container gets larger.

(👆 mentioned previously in the thread) This is true for me without the overflow: hidden. And onResize is fired properly when it's added.

Actually, that's not truly a fix, but more a workaround. You control the aspect ratio not through Chart.js itself by passing the aspect ratio value to the chart options, you control it by setting it to the container instead, so container controls its size. And overflow: hidden forces the container to resize when you update container's aspect ratio css value, so in turn it also triggers a resize for the chart. At least that's how I understood what's happening there 😄


Another different thing is that having aspect ratio in container allows you to display and resize all types of the charts in the same way. Otherwise, the default aspect ratio for bar chart is 2, for example. So if you set aspect ratio as 2 for all of your charts - the bar ones will result in 4 actually and will still be displayed twice wider (or twice as low as others) in comparison to pie or other charts. That was the thing for me to also consider and "fix" - I was need all types of charts to be displayed and behave in the same way. It differs and changes the standard behavior and probably a thing to consider if you go that way of handling the aspect ratio.

hairbo commented 1 year ago

@sergei-krylov thanks for the reply. In the Stackblitz I linked above: https://stackblitz.com/edit/angular-dhvdfm

...I do need aspectRatio set in both places. if I remove it from the ChartJS config, the chart won't take up 100% of the available width. But if that is really all it takes for this to be a stable solution, that would be pretty great. I still don't trust it, though...

Index-s commented 1 year ago

Hello fellas, this issue seems to still be there, also on the official documentation examples.

lwalter-ax commented 11 months ago

We're almost to the one year mark of this issue getting opened. Does anyone have any insight into whether a fix for this will be prioritized? Thanks!

eironross commented 11 months ago
style="aspect-ratio: 2"

This helped. I was reading through the documentation but no luck. Glad I found this. Thank you

ZLevine commented 11 months ago
style="aspect-ratio: 2"

This helped. I was reading through the documentation but no luck. Glad I found this. Thank you

Not the best solution since it will force the canvas area to 2:1 aspect ratio for all charts and breakpoints, which is probably not what you want.

The best solution I’ve found so far that isn’t a design tradeoff is to make sure there’s always a sibling element (like an empty div) before or after the canvas element. If you have a React component wrapper or something, it’s easy to ensure this always happens.

eironross commented 11 months ago
style="aspect-ratio: 2"

This helped. I was reading through the documentation but no luck. Glad I found this. Thank you

Not the best solution since it will force the canvas area to 2:1 aspect ratio for all charts and breakpoints, which is probably not what you want.

The best solution I’ve found so far that isn’t a design tradeoff is to make sure there’s always a sibling element (like an empty div) before or after the canvas element. If you have a React component wrapper or something, it’s easy to ensure this always happens.

I will also try to look into this. Thank you

gianantoniopini commented 11 months ago

I had the same problem. Using chart.js (version 4.4.1), vue, vue-chartjs and bootstrap. Applying the workaround from @athisun worked for me. So I'm now defining the aspect-ratio on the chart parent container: I changed the parent div from <div class="row"> to <div class="row ratio" style="--bs-aspect-ratio: 50%"> . Hopefully this issue will be fixed in the Chart.js library in the future, then this workaround should not be required anymore.

robinwilson16 commented 9 months ago

Using a Blazor Web Assembly app I couldn't get any of these solutions to resize the chart back to being larger once the browser window is made larger again. The canvas has a hardcoded width and height added to it which changes if you make the window smaller but remains the same if you make the window larger again. If you edit the canvas width and height in the built in browser dev tools or just remove them (they are repeated in there again in the style tag) then it goes back to being the full size. So it's hardly responsive with the hardcoded size. Then I realised the bug is even present on the examples on the ChartJS website such as the bar chart here: https://www.chartjs.org/docs/latest/charts/bar.html

Make the window small so the bar chart goes smaller then make the window full screen and the chart remains small.

I have found 2 fixes which trigger when the window is resized to either refresh the page or modify the canvas tag.

In the end I went for this which worked for me and means you get the nice little chart animation as it loads: https://stackoverflow.com/a/37008766/915426

In case the linked article goes here is the JS for it:

var resizeTimeout;
window.addEventListener('resize', function(event) {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(function(){
    window.location.reload();
  }, 1500);
});

The timeout part is there so that if dragging a window to resize you don't end up with too many requests to refresh.

IamRewt commented 9 months ago

Using version 4.4.1 in a blazor web app (.net 8) and had the same issue. @athisun 's solution worked for me as well.

@robinwilson16 I got it working with the aspect-ratio workaround, the scss looks like this:

.chart-container {
    width: 100%;
    padding: 10px;
    aspect-ratio: 2;

    canvas {
        width: 100%;
    }
}
rehovicova commented 8 months ago

Chart.js 4.2 - Can't believe this is still not fixed...

I am using it in react-charjs-2 and @ZLevine solution "fixed" it for me. Although I agree this makes the enlargement animation kind of choppy and slow...

ax-jkriese commented 7 months ago

Over a year with this issue, it's preventing us from upgrading to v4 - is there a plan to work on this?

hairbo commented 7 months ago

@etimberg could you please give us some kind of update on this issue? Those of us on this ticket have been unable to update to 4.x because of this issue, and (unless we have all missed something) it's a serious enough regression that we cannot upgrade until this is addresses. Much appreciated in advance!

athisun commented 7 months ago

Respectfully, repeatedly pinging the maintainers is poor practice. If there are no contributions to be made, consider upvoting the issue and subscribing to the thread instead of commenting, since it unnecessarily notifies everyone in the thread and makes it harder for newcomers to find the existing workarounds above. There are plenty of technical people in this discussion that could assist the maintainers by investigating further. I'll start:

Based on the release notes, I believe the breaking change was introduced in Release v4.0.0-alpha.1, in file src/helpers/helpers.dom.js (now helpers.dom.ts, with these 2 related Pull Requests:

I'd like to experiment with reverting these 2 commits locally and checking if the original behaviour returns, but I haven't the time right now. If it does resolve the issue, then we could look at adding extra unit tests and amending the logic to pass all tests.

If anybody else is interested in experimenting, that's where I'd start.

hairbo commented 7 months ago

@athisun you're not wrong at all. My apologies. I have both upvoted and followed this issue (for nearly a year, I think), so partially it's just frustration making me do it. I'll also say that the issue at hand seems so severe that I don't understand how anybody could use the 4.x release in a modern web environment where nearly everything is responsive. And that's why I'd like the maintainers to weigh in--maybe there's something deeper at play, or maybe it's just missing documentation, since there is a workaround in this thread that for all I know is the right way to do things moving forward. Kudos to you for looking at the PRs, though.

skinsshark commented 6 months ago

in case this solution is helpful for others, @wfortin and i found that since the canvas is struggling to rerender even given changes to its parent container through react code, we can utilize css to force dimension updates. the docs mention needing a parent wrapper exclusive to the canvas and with relative positioning, so if we give the canvas absolute positioning, it will be able to update itself accordingly (because your parent container knows it's own layout ie. through flexbox etc)

const ChartWrapper = styled.div`
  position: relative;
  height: 100%;

  // forces canvas to rely on its parent container's dimensions
  canvas {
    position: absolute;
  }
`;
dew-dr0p commented 5 months ago

I don't know if i'm doing something wrong, but I seem to get the exact opposite of this issue. The chart resizes up to expand and fill available space, but doesn't resize down leading to an overflow, which I must either allow or hide affecting the layout of the page.

I'm using chart.js 4.4.2 and react-chartjs-2 5.2.0

I would like anybody to pls help me know, if it's the same issue here, or this is just peculiar to me.

YgorAlves commented 5 months ago

@dew-dr0p I had the same problem as you, @skinsshark `s answer worked for me, a wrapper div with those css of his answer.

Another thing that worked as a temporary solution before i found this canvas css, was putting another div wrapper with height 99% and width 99%, strangely it worked too.

aykaramba commented 5 months ago

I think some engineering will need to be done to resolve this permanently. I have had to resolve to either using the 2.5.0 library and figuring out the syntax for charting for such an old version or 3.9.1 with some fiddly styling in the container div to make it work ( eg. style="position: relative; width: 100%; aspect-ratio: 2/1; overflow: hidden;")

What can we do to help the team get this resolved?