Forced re-layout (reflow) when canvas has no fixed width/height
- Dominant language
- JavaScript
- Stars
- 67.7k
- Forks
- 11.9k
- Avg merge
- 7h 39m
- Merged PRs (30d)
- 5
Description
### Expected behavior
Chart.js should trigger at most one forced reflow (ideally zero) upon initialization.
### Current behavior
When the `` has no `width` and `height` attributes, Chart.js currently causes multiple [forced reflows](https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing) upon initialization, probably due to interspersed reading and writing of DOM and CSS properties. This causes major slowdowns when there are many charts on a page.
Here's a devtools screenshot, showing the reflows as purple "Layout" bars:
### Reproducible sample
https://codepen.io/joliss/pen/XWwvrJL
### Optional extra steps/info to reproduce
1. Open the CodePen above
1. In Chrome devtools's Performance tab, press the ⟳ reload icon
1. Open the Frame section
1. In the flame graph, zoom in on the `main` function call to see the re-layouts
### Possible solution
- Chart.js could perhaps batch all DOM/CSS reads and writes, so that we first do all the reads, then all the writes (see the section [Avoid layout thrashing](https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing?utm_source=devtools#avoid_layout_thrashing)). This should bring the number of reflows down to one.
- For use cases where a single page has many charts, this will still slow down page load, because every chart causes a reflow, for O(n) reflows in total.
To accommodate this use case, I'd suggest it might be worthwhile for Chart.js provide a mechanism to run the "read" phase (where we determine the size using `getComputedStyle`) separately from the "write" phase (where we set DOM/CSS attributes and draw the chart). That way, as a developer, I can batch-run the read phase on all my charts, and then batch-run the write phase on all of them, bringing the number of reflows back down to one.
This mechanism doesn't need to be super obvious to access. I think as long as it's documented on the [Performance](https://www.chartjs.org/docs/master/general/performance.html) documentation page in a section like "Avoiding reflows when you have many charts", most people who need it will find it. The Performance page was the thing I checked first when I tried to solve this.
My current workaround is passing [`canvas.transferControlToOffscreen()`](https://www.chartjs.org/docs/master/general/performance.html#parallel-rendering-with-web-workers) instead of `canvas` to Chart.js, which causes all DOM/CSS writes to be ignored. This does mean that I have to do the [sizing logic](https://github.com/chartjs/Chart.js/blob/0ca1b6ef85b4668f28681f1114cc5527360e2ec9/src/helpers/helpers.dom.ts#L216-L248) manually in my own code.
### Context
_No response_
### chart.js version
v4.4.3
### Browser name and version
Chrome 126.0.6478.127
### Link to your project
_No response_
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.