angular-chart.js
angular-chart.js copied to clipboard
[BUG] Responsive chart does not resize when container gets smaller
I am not sure if this is an issue with Chart.js or angular-chart so I posted this in both projects...
Expected Behavior
When responsive is set to true, chart should resize whether container size increases or decreases.
Current Behavior
When responsive is set to true, chart only resizes when container size increases and does not resize when it decreases.
Context
I'm using angular-chart with Chart.js and bootstrap:
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<canvas id="base" class="chart-bar"
chart-data="ctrl.chart.data"
chart-labels="ctrl.chart.labels"
chart-colors="ctrl.chart.colors"
chart-dataset-override="ctrl.chart.datasetOverride"
chart-options="ctrl.chart.options">
</canvas>
</div>
</div>
</div>
</div>
Environment
- Angular-chart version: 1.1.1
- Chart.js version: 2.5.0
- Browser name/version: Firefox ESR 45.8.0 / Internet Explorer 11.0.9600.18499 / Chrome 56.0.2924.87
Any solution of this question
I would first try to use $scope.$emit('$resize')
within your controller. This should activate the angular-chart $scope.$on('$resize')
function within the source. You can put a break point here to see if its getting hit.
If that doesnt work then it may have to do with your flex not shrinking the canvas on page resize. Try to set min-width: 0
and see if that fixes your issue, if it does then you can adjust the min-width to be the appropriate size.
Adding the min-width on my canvas container (which was using flex) as well as calling $scope.$emit('$resize')
solved the problem for me.
I am using React, but have been running into this issue too and think this must be a compatibility issue with CSS Grid/CSS Flexbox.
I have a container that is set with max-height: 100%; max-width: 100%; width: 100%; height: 100%;
and as the container grows, my chart expands to fill the extra space. But when I reduce the size of my window, my container (whose height/width is specified in fr
units) will not respect my max-height: 100%; min-height: 100%;
rules. Subsequently, an onResize()
event won't fire because the parent container will maintain the height/width values from when the chart was expanded to it's largest state, and therefor the graph will remain the same size.
Again I am coming from React but believe this issue lies with CSS Grid/CSS Flexbox not respecting the max-height/max-width parameters. A workaround in React is to remove the chart element from the page entirely, and place it back immediately afterwards. This allows the flexible container to resize down to your new height and the chart to fill the contents of the resized flexible container. I am not sure how to go about implementing this in Angular but hope this might shed some light on ways you might go about achieving the same result.
It's been 5mo since the last comment, has there been any official resolution to this issue?
In the resize: function(silent)
the new width and height are calculated based on the size of the chart's container
var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
The problem is that the container will never get to a lower size than the contained canvas.
This is a solution that works for me
resize: function(silent) {
var me = this;
var options = me.options;
var canvas = me.canvas;
var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
// Edited to add aspectratio support
// I set the style as dynamic so that the container can actually shrink
if (isNaN(aspectRatio) || aspectRatio == null) {
canvas.style.width = '100%';
canvas.style.height = '100%';
}
else if (aspectRatio>=1) {
canvas.style.width = '100%';
canvas.style.height = Math.floor(canvas.clientWidth / aspectRatio) + 'px';
} else {
canvas.style.width = Math.floor(canvas.clientHeight * aspectRatio) + 'px';
canvas.style.height = '100%';
}
// I read the new size of the canvas
var newWidth = canvas.clientWidth;
var newHeight = canvas.clientHeight;
if (me.width === newWidth && me.height === newHeight) {
return;
}
canvas.width = me.width = newWidth;
canvas.height = me.height = newHeight;
helpers.retinaScale(me, options.devicePixelRatio);
if (!silent) {
// Notify any plugins about the resize
var newSize = {width: newWidth, height: newHeight};
plugins.notify(me, 'resize', [newSize]);
// Notify of resize
if (me.options.onResize) {
me.options.onResize(me, newSize);
}
me.stop();
me.update({
duration: me.options.responsiveAnimationDuration
});
}
}
As @SpencerDuball noted, this may have something to do with CSS Grid/Flex-Box.
I at least stumbled upon the same problem when using ChartJS inside a Grid.
This is my basic markup:
<div class="grid">
<div class="chart">
<div style="position:relative;width:100%;height:100%">
<canvas id="0df68a4e2b53cf460531e053e9ba128489951b86"></canvas>
</div>
</div>
</div>
Here, div.grid defines a CSS grid.
I managed to make it work by explicitly assigning "overflow: auto" to the div surrounding the Chart.js canvas container:
.chart {
overflow: auto;
}
Hi @jtblin are you able to give any pointers/advice on this issue please? I am using CSS Grid and when resizing the page/grid up the charts grow fine but when resizing back down will break. If you refresh at the smaller resized viewport it works just fine until resizing smaller again.
Repro - CodePen
https://codepen.io/warrenbuckley/pen/VJVRKM
- ✔️ Resize the page & graphs in grid cell items resize correctly
- ❌ Resize the page back down & the graphs stay at size & breaks CSS grid
My Hacky Fix
A hacky fix I got from help of a colleague @fALKENdk was to specify/override the canvas width to be 100%
canvas
{
width: 100% !important;
}
Hacky Canvas Fix - CodePen
https://codepen.io/warrenbuckley/pen/LKMZZx
Is this repository maintained anymore @jtblin as a proper fix would be nice to have please 👍
I did it like below. And it worked perfectly on all resolutions.
.chart-container {
position:relative;
height:200px;
width:200px;
}
/* add responsive css */
canvas
{
width: 100% !important;
height: 100% !important;
}
<div class="chart-container">
<canvas id="chart"></canvas>
</div>
This issue saved me after 3 straight hours of debugging. I'm going to leave some keywords here, so that other people can find it if they have my problem: Bootstrap chart.js not stacking vertically on resize col col-md col-sm row bootstrap 4. Sorry for barging in like this.
Both
canvas {
width: 100% !important;
}
or
.chart-container {
overflow: auto;
}
worked perfectly for me.
Thanks a lot!!
As @SpencerDuball noted, this may have something to do with CSS Grid/Flex-Box.
I at least stumbled upon the same problem when using ChartJS inside a Grid.
This is my basic markup:
<div class="grid"> <div class="chart"> <div style="position:relative;width:100%;height:100%"> <canvas id="0df68a4e2b53cf460531e053e9ba128489951b86"></canvas> </div> </div> </div>
Here, div.grid defines a CSS grid.
I managed to make it work by explicitly assigning "overflow: auto" to the div surrounding the Chart.js canvas container:
.chart { overflow: auto; }
THANK YOU
As @SpencerDuball noted, this may have something to do with CSS Grid/Flex-Box.
I at least stumbled upon the same problem when using ChartJS inside a Grid.
This is my basic markup:
<div class="grid"> <div class="chart"> <div style="position:relative;width:100%;height:100%"> <canvas id="0df68a4e2b53cf460531e053e9ba128489951b86"></canvas> </div> </div> </div>
Here, div.grid defines a CSS grid.
I managed to make it work by explicitly assigning "overflow: auto" to the div surrounding the Chart.js canvas container:
.chart { overflow: auto; }
This one worked for me! Thanks a lot!!
I had this same problem, and none of the solutions here worked for me. I was using Flex and Grid to position all of my elements. The following solution worked for me:
chart = new Chart("chart", {
data,
options: {
maintainAspectRatio: false
}
}
<div class="Graph">
<div>
<canvas id="chart"></canvas>
</div>
</div>
.Graph {
position: relative;
}
.Graph > div {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
This forces the inner div to have no say in the DOM sizing. It just follows the parent div's size and position.
As @SpencerDuball noted, this may have something to do with CSS Grid/Flex-Box.
I at least stumbled upon the same problem when using ChartJS inside a Grid.
This is my basic markup:
<div class="grid"> <div class="chart"> <div style="position:relative;width:100%;height:100%"> <canvas id="0df68a4e2b53cf460531e053e9ba128489951b86"></canvas> </div> </div> </div>
Here, div.grid defines a CSS grid.
I managed to make it work by explicitly assigning "overflow: auto" to the div surrounding the Chart.js canvas container:
.chart { overflow: auto; }
Thank this worked for me too. But I would like to clarify a bit. The trick is to set the grid container's direct child (the grid item) in which the chart.js canvas resides to overflow: auto
.
I had this same problem, and none of the solutions here worked for me. I was using Flex and Grid to position all of my elements. The following solution worked for me:
chart = new Chart("chart", { data, options: { maintainAspectRatio: false } }
<div class="Graph"> <div> <canvas id="chart"></canvas> </div> </div>
.Graph { position: relative; } .Graph > div { position: absolute; top: 0; left: 0; right: 0; bottom: 0; }
This forces the inner div to have no say in the DOM sizing. It just follows the parent div's size and position.
It works for me in Bootstrap 5 (currently in beta).
I fix my issue by setting time function.
setTimeout(() => { //charts here }, 1000);
Unfortunately none of the solutions here work for me. I don't like at all the solution I ended up using, but here it goes (Angular):
this.chartOptions = {
responsive: true,
resizeDelay: 100,
aspectRatio: 1.5,
// ...
}
@HostListener('window:resize', [])
onResize() {
// when the window resize force the chart to resize to smaller sizes (-100 on both width/height worked for my example).
// after the resize delay it will automatically re-adjust. Not having the resize delay will show a very visible flicker
this.chart.resize(this.chart.chart.width - 100, this.chart.chart.height - 100);
}
In my case, I used flex , Angular and my chart was responsive
chart.html
<canvas id="barChart" #barChart></canvas>
chart.scss
:host { display: flex; align-items: center; justify-content: center; flex: 1; overflow: hidden; position: relative; }
#barChart { width: 100% !important; height: 100% !important; }
This is the key, because chart.js explicitly states they are sizing the canvas based on the parent element of the canvas. But if the parent is not forced to shrink, then it won't, meaning whatever size the canvas was stretched/expanded, it will keep the parent also "big" and not let it shrink. Having the parent of the canvas anchored to the edges of the "parent parent" is the key I think. Edit: thus I think this is "by design" as it is a constraint coming from chart.js itself, not any wrapping lib (I had this same issue with the plain js lib not the angular/vue/whatever wrapper)
I had this same problem, and none of the solutions here worked for me. I was using Flex and Grid to position all of my elements. The following solution worked for me:
chart = new Chart("chart", { data, options: { maintainAspectRatio: false } }
<div class="Graph"> <div> <canvas id="chart"></canvas> </div> </div>
.Graph { position: relative; } .Graph > div { position: absolute; top: 0; left: 0; right: 0; bottom: 0; }
This forces the inner div to have no say in the DOM sizing. It just follows the parent div's size and position.
It works for me in Bootstrap 5 (currently in beta).
This solution (which is explained just above here by @seekingtheoptimal ) is the one that worked for me. Thank you @Rollinc2198 you saved the day!
I came up with some 'special' solution for react. That kind work for me
const options = {
responsive: true,
layout: {
padding: 45,
cutoutPercentage: 100,
},
plugins: {
datalabels: {
},
legend: {
display: false
},
tooltip: {
enabled: false
}
}
};
return (
<div>
<Doughnut
style={{
display: 'unset'
}}
data={chartData}
options={options}
/>
</div >
);