angular-chart.js icon indicating copy to clipboard operation
angular-chart.js copied to clipboard

Chart increase size each redraw

Open Oldwo1f opened this issue 9 years ago • 30 comments

Hi. I am facing the same issue as in 713 and 759 of Chart.js issues. But after a lot of testing, it appears not to be a Chart.js issue.

I give you a simple test case that reproduce the error.

<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/Chart.js/Chart.js"></script>
<script src="/bower_components/angular-chart.js/dist/angular-chart.js"></script>

<body ng-app="app" ng-controller="LineCtrl">
<h1>TEST</h1>
<div style="width:600px;height:400px; margin:50px auto;position:relative;">
<canvas id="line" class="chart chart-line" options="myoptions" data="data" height="300px" labels="labels" 
    legend="true" series="series" click="onClick"></canvas> 
</div>

<a class="toto" href="#" >click to change data</a>
<script type="text/javascript">

angular.module("app", ["chart.js"]).controller("LineCtrl", ['$scope', '$timeout', function ($scope, $timeout) {

  $scope.labels = ["January", "February", "March", "April", "May", "June", "July"];
  $scope.series = ['Series A', 'Series B'];
  $scope.myoptions = {scaleBeginAtZero : true,maintainAspectRatio: false,scaleShowGridLines : false}
  $scope.data = [
    [65, 59, 80, 81, 56, 55, 40],
    [28, 48, 40, 19, 86, 27, 90]
  ];
  $scope.onClick = function (points, evt) {
    console.log(points, evt);
  };

  // Simulate async data update
  $('.toto').click(function () {
    console.log('clicked');
    $scope.data = [
      [90, 48, 40, 19, 86, 27, 90],
      [65, 59, 80, 81, 56, 55, 40]
    ];
    $scope.$apply();
  });
}]);

</script>
</body>

you ll have to install dependencies -----> bower install jquery angular Chart.js angular-chart.js

Can you please see whats happens when graph redraw.( on window resize or on data change i got the same problem)

Oldwo1f avatar Mar 19 '15 01:03 Oldwo1f

Same Issue here

boonkerz avatar Mar 25 '15 21:03 boonkerz

I am not able to repro with pasting your code in jsbin (actually nothing show up). Can you create a valid repro with this jsbin template please?

jtblin avatar Mar 26 '15 08:03 jtblin

with responsive=false it works

boonkerz avatar Mar 26 '15 09:03 boonkerz

@boonkerz You're right, with responsive=false it won't redraw itself bigger everytime, but what if you WANT responsive? On your phone for example, if you open it in portrait view, and then rotate the phone to landscape view, the chart is not sharp anymore. Blurry.

I asked a detailed question about this: http://stackoverflow.com/questions/29819173/strange-angular-chartjs-issue-not-displaying-correctly-in-ionic-app but there are not a lot of answers yet.

Also, when the chart is initially in an ng-hidden element, it's height will be 0.

I hope someone can solve this?

JeroenJK avatar Apr 23 '15 11:04 JeroenJK

I am having the same problem.

luccascorrea avatar May 23 '15 23:05 luccascorrea

Same Issue

ysoffner avatar May 25 '15 20:05 ysoffner

Can you provide a jsbin to repro the issue instead of saying "me too"?

jtblin avatar May 28 '15 05:05 jtblin

I'm not really able to reproduce it on jsbin, I don't know what causes the problem. But here is the setup I have: https://jsfiddle.net/1t0x6qmb/

I would start with the fact that if I set height to 50, the actual height set to canvas via style is about 120px, if I set 150, the actual size is about 360px.

If I then use more data (read I have more items on x axis), the chart seems to double its height. Maybe it holds the ratio and as it grows wider (as its parent element does), it grows also in height?

jirihelmich avatar Jul 29 '15 07:07 jirihelmich

responsive= false also fixed my ever increasing chart height but I'm struggling now to get the dimensions the way I want. What is the correct way to specify the dimensions for a chart?

alanblake avatar Aug 22 '15 10:08 alanblake

Using width and height. Can you provide a jsbin to repro?

jtblin avatar Aug 22 '15 10:08 jtblin

I resolved my issue by setting the canvas height in an external css file rather than inline in the canvas element. Setting height inline would increase by 5px every time the chart was created.

alanblake avatar Aug 22 '15 11:08 alanblake

            chartJsProvider.setOptions({
                responsive: true,
                maintainAspectRatio: false
            });

was what I needed.

jirihelmich avatar Aug 23 '15 20:08 jirihelmich

I have same issue, you can take a look on http://soyto.github.io/#/ranking/Alquima for example. When you resize the window you will how chart have their height increased.

If needed you can access sources on https://github.com/soyto/soyto.github.io

soyto avatar Sep 09 '15 09:09 soyto

I was also having problems getting the chart to fill the height properly, particularly with relative heights (% or vh based) and chart-legend enabled. I thought it might be worth sharing what I found to get proper height-filling results in those cases.

Why does it not work with the defaults?

  1. [chart.js] If maintainAspectRatio is true (the default), the height is essentially width x aspectRatio - i.e. it won't fill the height and will either overflow or be too small depending on your parent element.
  2. [chart.js] If responsive is false (the default), the chart won't resize as the page does. So it again won't track the height as the page height changes.
  3. [chart.js] Resizes the canvas to completely fill the height of the parent div (if maintainAspectRatio is false).
  4. [angular-chart.js] Angular-chart injects a chart-container div, with no styling by default, importantly including no css height, so it is inherited from the parent.
  5. [angular-chart.js] If chart-legend="true" angular-chart injects a chart-legend element for the legend below the chart, which obviously has a non-zero height.
  6. [Both] Items 3 + 4 + 5 = a chart where the legend overflows the height of the parent element (3 fills it, then 5 overflows it)
  7. [Both] Items 3 + 4 + 5 = a chart that keeps growing on every resize (if response is true). Item 5 increases the size of the parent div every time, then on refresh, item 3 fills the new bigger size, then item 5 injects the legend again, etc...

How to fix:

  1. Set maintainAspectRatio to false. Fixes point 1.
  2. Set responsive to true. Fixes point 2
  3. Set css height on parent element to be the overall relative height you want (e.g. 30vh).
  4. Set css height on .chart-container to be a calculated as parentHeight - legendHeight[1]. This reduces the height that chart files (point 3), and leaves space for the legend below (fixing points 6 & 7).

Hope this helps, and please do correct any mistakes in my explanation.

[1] E.g. something like height: calc(30vh - 2.85rem - 5px) seems to work ok (where 30vh is the parent height relative to the viewport, 2.85rem is the line height of the ul in Foundation, and 5px is the default margin-top from angular-chart.css. Note: calc() seems to have similar browser support to canvas so shouldn't be an issue using it here.

farrago avatar Oct 20 '15 11:10 farrago

@farrago THANK YOU SO MUCH! I have been fighting this issue for months now. Your solution finally allows me to have the graphs use the full height available on different devices. Kudos kudos kudos to you.

Chuckv01 avatar Oct 23 '15 15:10 Chuckv01

I'm trying to put charts in a bootstrap col-lg-{{size} where size can be toggled through a select by the user. Should @farrago solution be working in this case?

Dashue avatar Oct 26 '15 12:10 Dashue

Thank you @jirihelmich

tulga-orosoo avatar Apr 10 '16 11:04 tulga-orosoo

@farrago This has made me very happy. First rate debugging! Thank you.

benramadan avatar Apr 13 '16 13:04 benramadan

@farrago thanks for that detailed post. Helped me figure out my issue

lrossy avatar May 13 '16 17:05 lrossy

@farrago does not work on firefox

dangji avatar May 19 '16 15:05 dangji

@farrago's answer doesn't work anymore as Chart.js 2 uses canvas.

Jiia avatar Oct 07 '16 11:10 Jiia

What about ChartJs 2.0 ?

djleonskennedy avatar Oct 25 '16 08:10 djleonskennedy

Chart.js 1.x was also using canvas so not sure this is relevant.

On Tue, Oct 25, 2016 at 7:32 PM, Yuriy [email protected] wrote:

What about ChartJs 2.0 ?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/jtblin/angular-chart.js/issues/84#issuecomment-255971864, or mute the thread https://github.com/notifications/unsubscribe-auth/ABUxrmTrGEdly_RDadMM0ChzJqdz4mMkks5q3b66gaJpZM4DxDgN .

jtblin avatar Oct 25 '16 18:10 jtblin

@farrago thanks for your solution. In my case the item 4 has been not needed to fix the issue.

FrancescoCarraro avatar Jul 28 '17 12:07 FrancescoCarraro

A hack

$('.chartjs-hidden-iframe').remove();
$('#chartjs-canvas').remove();
$('.chart').html('<canvas id="chartjs-canvas" height="300"></div>');
var myLine = new Chart($('#chartjs-canvas')[0].getContext("2d"), {

shivabhusal avatar Aug 03 '17 16:08 shivabhusal

Multiple instances of iframes seem to be the issue. With each redraw chart.js is adding an iframe instance with the class "chartjs-hidden-iframe" to the DOM. Similar to shivabhusal above, with each redraw I am clearing those out.

I put the canvas inside a div, then with each redraw empty it, then appended the canvas back in. $('#canvasdiv').empty; $('#canvasdiv').append('<canvas id="mycanvas"></canvas')

jcg31 avatar Oct 01 '17 14:10 jcg31

I solved this in a different way and would like to share my solution. So I wanted to keep it responsive, and solved it by setting the maintainAspectRatio on true for first render, and on false for each consequent rerender.

Example:

<script>
  import { Bar } from 'vue-chartjs';

  export default {
    extends: Bar,
    name: "bar-chart",
    props: ['chartData'],
    data() {
      return {
        renderData: {
          labels: this.chartData.map(d => d.label),
          datasets: [
            {
              backgroundColor: 'steelblue',
              data: this.chartData.map(d => d.value),
            },
          ]
        },
        renderOptions: {
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            xAxes: [{
              barPercentage: 1.5
            }]
          },
          legend: {
            display: false,
          }
        }
      }
    },
    mounted() {
      this.renderChart(this.renderData, this.renderOptions);
    },
    watch: {
      chartData() {
        this.renderOptions.maintainAspectRatio = true;
        this.renderChart(this.renderData, this.renderOptions);
      }
    }
  }
</script>

This is the least hacky solution that I was able to find.

edin0x avatar Dec 17 '17 22:12 edin0x

I ran into this same problem using vue.js to control updating a chart.js Chart instance. I found this page by doing some googling. A lot of these solutions helped me look in the right direction, thank you all.

However, the real problem for me ( and I suspect many others) turned out to be that I was recreating a whole new chart.js Chart object on data updates and attaching it to the same DOM (or VDOM) element as before, and NOT updating the already made chart.js Chart object using the chart.js Chart object's update method.

Once I used that, I had no doubling of height on each update and I didn't need to fiddle around with any responsive or maintainAspectRatio properties of the chart.js config.

Lesson learned, use Chart.update() on an existing Chart(), even if you stored no reference to the original.

Update documentation: http://www.chartjs.org/docs/latest/developers/updates.html

My code:

Original Chart Instantiation code

 for(let i in this.scdbData.routeData.charts){
           new Chart(document.getElementById(i).getContext("2d"), 
                   this.scdbData.routeData.charts[i].config);
        }

The above code would be called after each time the data was updated.

Updated Chart Instantiation code

        for(let i in this.scdbData.routeData.charts) {
            if (this.scdbData.charts[i]) {
                this.scdbData.charts[i].data.labels = this.scdbData.routeData.charts[i].labels;
                this.scdbData.charts[i].data.datasets = this.scdbData.routeData.charts[i].datasets;
                this.scdbData.charts[i].update();
            }
            else {
                this.scdbData.charts[i] = new Chart(document.getElementById(i).getContext("2d"), 
                      this.scdbData.routeData.charts[i].config);
            }
        }

Now I store a reference to a Chart when created and I check to see if that reference exists. If it does, I update the properties of labels and datasets on the referenced chart and then call update().

jasonarg avatar Dec 29 '17 14:12 jasonarg

I don't know if it's some kind of help, but I had the same problem using the following options: options: { maintainAspectRatio: false, responsive: true, legend: { position: 'bottom' }, }

and worked when I modified to:

options: { maintainAspectRatio: true, responsive: true, legend: { position: 'bottom' }, }

gabrielpramos avatar Jul 05 '19 11:07 gabrielpramos

mine was that i was inserting the containing div again i forgot the check to see if exists

Seabizkit avatar Oct 29 '20 10:10 Seabizkit