Chart.js
Chart.js copied to clipboard
All chart examples are decreasing in size when pixelRatio is non-terminating value
Expected behavior
When triggering some hover event in a chart, the expected behavior is just to keep the real size.
Current behavior
The charts are shrinking in size, similar to what was reported in #10951.
Reproducible sample
https://www.chartjs.org/docs/latest/samples/area/line-boundaries.html
Optional extra steps/info to reproduce
- install Zoom for Google Chrome
- visit https://www.chartjs.org/docs/latest/samples/area/line-boundaries.html
- enter
302.0639419555664to Zoom extension
https://user-images.githubusercontent.com/28837028/230451928-86a9830a-ab97-47bf-88b3-a0b9959e7092.mp4
Possible solution
Limiting the value of pixelRatio to discrete values.
Context
On my Xperia 1 II running Chrome, I have noticed that whenever I visit a website that utilizes Chart.js, the charts are consistently shrinking in size. Upon setting "Brake on > attribute modifications" in the Chrome developer tools on the shrinking canvas element, I observed that the values stored in the variables within the retinaScale function are gradually decreasing.
Initially, I suspected a bug in the heatmap graph on Misskey, a recently popular distributed social networking service in Japan. However, I realized that the service was using Chart.js v4.2.1 (after #10971 was merged), which leads me to suspect that the issue lies with Chart.js itself.
chart.js version
v4.2.1
Browser name and version
Google Chrome 111.0.5563.147(Official Build)
Link to your project
No response
Anyone played with this to find any hotfix possible?
I've successfully reproduced this issue with Pixel 6a and the official samples. It seems that some non-default system-wide zoom triggers it, because I can't reproduce it on secondary profile with default system-wide zoom.
Is there any workaround?
I've investigated this a bit:
- It seems that setting an integer devicePixelRatio to Graph doesn't fix it.
- When the container has a fixed height (in px, but probably also in some other way), it seems to solve the issue.
- I've configured
height: 100pxand the canvas stopped atheight: 99px. This might point to the root of the issue: Seemingly, Graph.js reduces the height of the canvas (probably due to Math.floor call in the PR https://github.com/chartjs/Chart.js/pull/10971 ), which reduces the height of the outer div, which causes further resizing…
Key conditions for triggering the bug
Aside of a specific devicePixelRatio (and probably responsive: true), it seems that it also depends on the container height:
- Fixed height is OK. If the container's height doesn't depend on the inner canvas height, the loop is probably broken. Maybe the canvas is 1px (or so) smaller than it was intended, but it works in general.
- max-height without height is not OK, as the container can shrink.
- no height is also not OK, as the container can shrink.
- min-height should theoretically allow the shrinking loop until min-height is reached. Not tested.
The same probably applies to width, except that block objects usually take all the available width, which prevents this issue from being caused by width.
Workaround
Just set height of the container. If you hesitate:
- You might want to do it anyway, as it allocates space. If you have a place and progressively/asynchronously render a plot there, the container will potentially resize and move other items on the page unless you specify it's width&height.
- By default, Chart.js uses a constant aspect ratio. You might just want to do the same for the container.
- If you use Bootstrap, it already has containers with fixed aspect ratio, so they might be what you are looking for: https://getbootstrap.com/docs/5.2/helpers/ratio/
So far, the workaround looks OK.
Hi, I got fix to handle diminishing issue. add style: display : inline-block and remove the width and height of the canvas stops the chart from diminishing.
how i fixed chart in my react app: Before:
<Doughnut
options={options}
width={350}
height={340}
/>
After:
<Doughnut
style={{ display: "inline-block" }}
options={options}
//remove width and height
/>