jtblin / angular-chart.js

Reactive, responsive, beautiful charts for AngularJS using Chart.js: http://jtblin.github.io/angular-chart.js
Other
2.68k stars 761 forks source link

Chart increase size each redraw #84

Open Oldwo1f opened 9 years ago

Oldwo1f commented 9 years ago

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)

boonkerz commented 9 years ago

Same Issue here

jtblin commented 9 years ago

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?

boonkerz commented 9 years ago

with responsive=false it works

JeroenJK commented 9 years ago

@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?

luccascorrea commented 9 years ago

I am having the same problem.

ysoffner commented 9 years ago

Same Issue

jtblin commented 9 years ago

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

jirihelmich commented 9 years ago

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?

alanblake commented 8 years ago

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?

jtblin commented 8 years ago

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

alanblake commented 8 years ago

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.

jirihelmich commented 8 years ago
            chartJsProvider.setOptions({
                responsive: true,
                maintainAspectRatio: false
            });

was what I needed.

soyto commented 8 years ago

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

farrago commented 8 years ago

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.

Chuckv01 commented 8 years ago

@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.

Dashue commented 8 years ago

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?

tulga-orosoo commented 8 years ago

Thank you @jirihelmich

benramadan commented 8 years ago

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

lrossy commented 8 years ago

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

dangji commented 8 years ago

@farrago does not work on firefox

Jiia commented 7 years ago

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

djleonskennedy commented 7 years ago

What about ChartJs 2.0 ?

jtblin commented 7 years ago

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

On Tue, Oct 25, 2016 at 7:32 PM, Yuriy notifications@github.com 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 .

FrancescoCarraro commented 7 years ago

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

shivabhusal commented 6 years ago

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"), {
jcg31 commented 6 years ago

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')

edin0x commented 6 years ago

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.

jasonarg commented 6 years ago

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().

gabrielpramos commented 5 years ago

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' }, }

Seabizkit commented 3 years ago

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

mdadil09 commented 3 months ago

Just change the responsive to true it will adjust automatically

responsive = true;