Chart.js canvas resize
Asked Answered
K

19

109

In (Android WebView HTML5 canvas error) i posted a question regarding plotting graphs using Graph.js library. The problem i have now is that if i call the function to plot the graph multiple times, the canvas resizes every time. Each time the graph is redrawn to the same canvas, its size also changes. I also tried setting the size of the canvas but without success.

What could be the reason? Why does the canvas resize every time?

Kultur answered 7/11, 2013 at 22:1 Comment(0)
D
218

I had a lot of problems with that, because after all of that my line graphic looked terrible when mouse hovering and I found a simpler way to do it, hope it will help :)

Use these Chart.js options:

// Boolean - whether or not the chart should be responsive and resize when the browser does.

responsive: true,

// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container

maintainAspectRatio: false,
Devereux answered 2/9, 2014 at 8:57 Comment(5)
Thanks @Devereux for the answer. For me height & width attributes weren't getting applied to the canvas, incase anyone ends up here looking for a solution for trying to resize the canvas try setting responsive to true. *Chart.defaults.global.responsive = false *Marsland
Dont forget that for this method to work properly, you need to place the canvas in a <div> and set the height and/or width on said <div> instead of the canvas.Raimundo
These flags are now by default set to true, so you don't need to touch them: chartjs.org/docs/latest/general/responsive.htmlFibrovascular
Along with this, set the width and height of canvas also. Then it works fine. ThanksSturtevant
In my case calling the update method of the chart worked better; I had to turn off responsive for other reasons, so couldn't use this otherwise fine solution. +1 +alt:answer :-)Laminate
D
67

What's happening is Chart.js multiplies the size of the canvas when it is called then attempts to scale it back down using CSS, the purpose being to provide higher resolution graphs for high-dpi devices.

The problem is it doesn't realize it has already done this, so when called successive times, it multiplies the already (doubled or whatever) size AGAIN until things start to break. (What's actually happening is it is checking whether it should add more pixels to the canvas by changing the DOM attribute for width and height, if it should, multiplying it by some factor, usually 2, then changing that, and then changing the css style attribute to maintain the same size on the page.)

For example, when you run it once and your canvas width and height are set to 300, it sets them to 600, then changes the style attribute to 300... but if you run it again, it sees that the DOM width and height are 600 (check the other answer to this question to see why) and then sets it to 1200 and the css width and height to 600.

Not the most elegant solution, but I solved this problem while maintaining the enhanced resolution for retina devices by simply setting the width and height of the canvas manually before each successive call to Chart.js

var ctx = document.getElementById("canvas").getContext("2d");
ctx.canvas.width = 300;
ctx.canvas.height = 300;
var myDoughnut = new Chart(ctx).Doughnut(doughnutData);
Dialyser answered 15/2, 2014 at 13:0 Comment(2)
I came across this error:Uncaught ReferenceError: doughnutData is not definedPresentation
Err. yes, that is just a placeholder variable there, as I simply don't know what data you were attempting to graph.Dialyser
A
29

This works for me:

<body>
    <form>
        [...]
        <div style="position:absolute; top:60px; left:10px; width:500px; height:500px;">
            <canvas id="cv_values"></canvas>

            <script type="text/javascript">
                var indicatedValueData = {
                    labels: ["1", "2", "3"],
                    datasets: [
                        {
                            [...]
                        };

                var cv_values = document.getElementById("cv_values").getContext("2d");
                var myChart = new Chart(cv_values, { type: "line", data: indicatedValueData });
            </script>
        </div>
    </form>
</body>

The essential fact is that we have to set the size of the canvas in the div-tag.

Annamaeannamaria answered 30/8, 2016 at 11:27 Comment(1)
agree! just place the canvas inside a div, then make sure that the option for 'responsive' is NOT false (it's true by default). Then just apply the sizing rule to the div.Facing
N
24

In IOS and Android the browser hides the toolbar when you are scrolling, thereby changing the size of the window which inturn lead chartjs to resize the graph. The solution is to maintain the aspect ratio.

var options = { 
    responsive: true,
    maintainAspectRatio: true
}

This should solve your problem.

Nombles answered 28/1, 2015 at 6:1 Comment(0)
C
14

I had to use a combination of multiple answers here with some minor tweaks.

First, it is necessary that you wrap the canvas element within a block-level container. I say to you, do not let the canvas element have any siblings; let it be a lonely child, for it is stubborn and spoiled. (The wrapper may not need any sizing restrictions placed on it, but for safety it may be good to have a max-height applied to it.)

After assuring that the previous conditions are met, when initiating the chart, make sure the following options are used:

var options = { 
    "responsive": true,
    "maintainAspectRatio": false
}

If you want to adjust the height of the chart, do so at the canvas element level.

<canvas height="500"></canvas>

Do not try to deal with the child in any other manner. This should result in a satisfyingly, properly laid-out chart, one that stays in its crib peacefully.

Clemence answered 13/2, 2018 at 16:8 Comment(0)
E
6

As jcmiller11 suggested, setting the width and height helps. A slightly nicer solution is to retrieve the width and height of the canvas before drawing the chart. Then using those numbers for setting the chart on each subsequent re-draw of the chart. This makes sure there are no constants in the javascript code.

ctx.canvas.originalwidth = ctx.canvas.width;
ctx.canvas.originalheight = ctx.canvas.height;

function drawchart() {
    ctx.canvas.width = ctx.canvas.originalwidth;
    ctx.canvas.height = ctx.canvas.originalheight;

    var chartctx = new Chart(ctx);
    myNewBarChart = chartctx.Bar(data, chartSettings); 
}
Exquisite answered 5/5, 2014 at 13:59 Comment(1)
Sorry, could you explain how this works? For example I open a page then canvas has one size, but then if I resize the window and canvas will have a different size (and presumably I don't need the original size because it was for the previous size of the window)?Burnedout
K
5

Chart.js -- Version 4.3.0

The simplest way to setup chart with responsiveness on resize is:-

const ctx = document.getElementById('chart');
    
const handleResize = (chart) => {
chart.resize();
}

new Chart(ctx, {
   type: 'bar',
   data: {
       labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
       datasets: [{
         label: '# of Votes',
         data: [12, 19, 3, 5, 2, 3],
         borderWidth: 1
       }]
   },
   options: {
      responsive: true,
      onResize: handleResize,
      maintainAspectRatio: false
   }
});
.chart-parent{
   position: relative;
   width: 90vw;
   height: 90vh;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>

<div class="chart-parent">
   <canvas id="chart"></canvas>
</div>

Note that:- I use width: 90vw and height 90vh just as an example. You can adjust it as you want.

The Boilerplate Template is used from Chart.js Documentation.**

Kruller answered 5/6, 2023 at 11:27 Comment(3)
when I tried this with Chart.js v4.3.0, I got stack overflow, which makes sense because your code is calling chart.resize() within onResize() and it goes into the loop.Thermosphere
@Thermosphere Share your code. The code, I mentioned is perfectly working. You can test it. you are getting stack overflow because of any other reason. Please share your code.Kruller
makes no sense ...Juvenescent
C
4

I had a similar problem and found your answer.. I eventually came to a solution.

It looks like the source of Chart.js has the following(presumably because it is not supposed to re-render and entirely different graph in the same canvas):

    //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
if (window.devicePixelRatio) {
    context.canvas.style.width = width + "px";
    context.canvas.style.height = height + "px";
    context.canvas.height = height * window.devicePixelRatio;
    context.canvas.width = width * window.devicePixelRatio;
    context.scale(window.devicePixelRatio, window.devicePixelRatio);
}

This is fine if it is called once, but when you redraw multiple times you end up changing the size of the canvas DOM element multiple times causing re-size.

Hope that helps!

Cowes answered 6/2, 2014 at 19:25 Comment(0)
S
2

I was having the same problem. I was able to solve it by setting option:

responsive: false,
maintainAspectRatio: true,
showScale: false,

And in css, set the width of the container div the same as the canvas:

    #canvasContainer { 
      width: 300px;
    }
    
    canvas {
      width: 300px;
    }
Spillage answered 25/7, 2019 at 13:33 Comment(0)
P
1

If anyone is having problems, I found a solution that doesn't involve sacrificing responsiveness etc.

Simply wrap your canvas in a div container (no styling) and reset the contents of the div to an empty canvas with ID before calling the Chart constructor.

Example:

HTML:

<div id="chartContainer">
    <canvas id="myChart"></canvas>
</div>

JS:

$("#chartContainer").html('<canvas id="myChart"></canvas>');
//call new Chart() as usual
Philtre answered 25/5, 2017 at 14:42 Comment(0)
F
1

I tried to Resize Canvas using jQuery but it din't work well. I think CSS3 is the best option you can try on, if you want on hover zooming at certain level.

Following hover option from other codepan link:

.style_prevu_kit:hover{
    z-index: 2;
    -webkit-transition: all 200ms ease-in;
    -webkit-transform: scale(1.5);
    -ms-transition: all 200ms ease-in;
    -ms-transform: scale(1.5);   
    -moz-transition: all 200ms ease-in;
    -moz-transform: scale(1.5);
    transition: all 200ms ease-in;
    transform: scale(1.5);
}

Follow my codepan link:

https://codepen.io/hitman0775/pen/XZZzqN

Flournoy answered 20/2, 2018 at 5:22 Comment(0)
C
1

Here is the official dokumentation: https://www.chartjs.org/docs/latest/general/responsive.html

Detecting when the canvas size changes can not be done directly from the canvas element. Chart.js uses its parent container to update the canvas render and display sizes. However, this method requires the container to be relatively positioned and dedicated to the chart canvas only. Responsiveness can then be achieved by setting relative values for the container size (example):

<div class="chart-container" style="position: relative; height:40vh; width:80vw">
    <canvas id="chart"></canvas>
</div>
Clockwise answered 2/3, 2021 at 12:28 Comment(0)
G
0

I had the same kind of scaling issue's using Angular CLI. Was able to get it working by removing this line from the index.html:

<script src="node_modules/chart.js/dist/Chart.bundle.min.js"></script>

and then in the angular-cli.json file, in the scripts section, using this:

"scripts": ["../node_modules/chart.js/dist/Chart.bundle.min.js"]

Source: mikebarr58

Guienne answered 8/5, 2017 at 9:6 Comment(0)
S
0

I tried multiple answers on this thread and what worked for me was that (Note I am using reactjs), checking my previous props passed into the chart component. When I was resizing my DOM, the chart was getting re-drawn with empty data.

componentDidUpdate(prevProps: ChartProps) { if(prevProps.data !== props.data) renderChart(); }
Sansen answered 12/5, 2021 at 14:3 Comment(0)
L
0

The accepted - responsive:true, maintainAspectRatio:false - did not work for my scenario. But I found we can simply call the update method on the chart.

So I found myself tweaking values inside matchMedia listeners .. like so:

myRadarChart.options.scales.r.pointLabels.font.size = "1rem";
myRadarChart.update();
Laminate answered 15/7, 2021 at 14:30 Comment(0)
D
0

For me it is working with max-height:none (chart.js v3.9.1 and ng2-charts v3.1.2):

<div *ngIf="shartData">
   <canvas style="max-height:none" baseChart [type]="'pie'"
      [data]="shartData"
      [options]="chartOptions">
   </canvas>
</div>

Without max-height the chart height becomes "1". The same is relevant when using chart.js directly without ng2-charts:

<div *ngIf="shartData">
   <canvas style="max-height:none" id="chartID">
   </canvas>
</div>
let ctx = <HTMLCanvasElement>document.getElementById("chartID");
let chart = new Chart(ctx, {
   type: 'pie',
   data: shartData,
   options: chartOptions
});
Dredger answered 3/11, 2022 at 13:17 Comment(0)
T
0

For me, in Chart.js v4.3.0, clearing height of the canvas did a trick in this way, that chart immediately refreshed itself and set new height matching the current size of the container.

My chart was living in a flex container:

<div id="container" class="h-100 d-flex flex-column">

    <div style="width:100%; flex-grow: 1">
        <canvas style="width:100%; height:100%"></canvas>
    </div>

    <div class="w-100">
        // here I had contents (buttons) that were wrapping down 
        // when size of the browser was reduced, so height of this div was increasing
    </div>

</div>

When chart initializes, it adds height/width to the canvas, e.g.:

<canvas style="width: 797px; height: 233px;" width="797" height="233"></canvas>

So basically something like this:

$(window).on("resize",
    () => {
        // this makes the ChartJs resize itself to fit the new height of the container
        $("#container canvas").css("height", "").attr("height", "");
    });

was causing the ChartJs to recalculate its size:

<canvas style="width: 696px; height: 233px;" width="696" height="233"></canvas>

Note, I did not have to call any of .update(), .resize() etc. to achieve this.

My settings were:

responsive: true;
maintainAspectRatio: false;
Thermosphere answered 3/8, 2023 at 16:22 Comment(0)
B
-3

Add div and it will solve the problem

<div style="position:absolute; top:50px; left:10px; width:500px; height:500px;"></div>
Blink answered 15/5, 2019 at 5:25 Comment(0)
P
-5
 let canvasBox = ReactDOM.findDOMNode(this.refs.canvasBox);
 let width = canvasBox.clientWidth;
 let height = canvasBox.clientHeight;
 let charts = ReactDOM.findDOMNode(this.refs.charts);
 let ctx = charts.getContext('2d');
 ctx.canvas.width = width;
 ctx.canvas.height = height;
 this.myChart = new Chart(ctx);
Planar answered 31/1, 2016 at 11:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.