15

I'm working with Chart.js and want to convert a line chart to a PNG. The problem is that the image always downloads with a transparent background, which is not what I need. I tried many options, nothing really worked.

And suggestions?

Tot Zam
  • 8,406
  • 10
  • 51
  • 76
HXH
  • 348
  • 1
  • 3
  • 11

4 Answers4

17

Here's a solution that works perfectly, also when saving to an image file (like png). I'm blatantly copying this from @etimberg at the chartjs issue tracker.

Chart.plugins.register({
  beforeDraw: function(chartInstance) {
    var ctx = chartInstance.chart.ctx;
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, chartInstance.chart.width, chartInstance.chart.height);
  }
});

This should go before your chart in the source.

Mark
  • 18,730
  • 7
  • 107
  • 130
  • Works like a charm! Thanks! – FizxMike Jul 06 '17 at 15:36
  • 1
    @Mark, This solution definitely works. But if you add a console.log statement in your beforeDraw function, I expect you will see, as I did, that it is getting called many times. Perhaps before every datapoint is drawn. That bothered me. So I kept searching and pieced together a solution for "underfilling" the rectangle with an afterRender handler to get the same effect in code that is only run once. Please see: https://stackoverflow.com/a/53946660 – Anne Gunn Dec 27 '18 at 16:41
4

Here's one way to get a fully-opaque version of your ChartJS:

Wait until the chart is fully animated out and complete. You can do this by adding the onAnimationComplete property to the chart.

In the onAnimationComplete function:

  • Create an in-memory temporary canvas of equal size as your chart.
  • Fill the temp canvas with white
  • drawImage the ChartJS canvas over the white-filled temp canvas
  • Create an image from the temp canvas.

Here's how that might be done:

var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx).Line(lineChartData, {
  responsive: true,
  onAnimationComplete:function(){
      var tcanvas=document.createElement('canvas');
      var tctx=tcanvas.getContext('2d');
      tcanvas.width=ctx.canvas.width;
      tcanvas.height=ctx.canvas.height;
      tctx.fillStyle='white';
      tctx.fillRect(0,0,tcanvas.width,tcanvas.height);
      tctx.drawImage(canvas,0,0);
      var img=new Image();
      img.onload=function(){
          document.body.appendChild(img);
      }
      img.src=tcanvas.toDataURL();
  }
});

Here's example code and a Demo:

var randomScalingFactor = function(){ return Math.round(Math.random()*100)};

var randomColorFactor = function(){ return Math.round(Math.random()*255)};

var lineChartData = {
  labels : ["January","February","March","April","May","June","July"],
  datasets : [
    {
      label: "My First dataset",
      fillColor : "rgba(220,220,220,0.2)",
      strokeColor : "rgba(220,220,220,1)",
      pointColor : "rgba(220,220,220,1)",
      pointStrokeColor : "#fff",
      pointHighlightFill : "#fff",
      pointHighlightStroke : "rgba(220,220,220,1)",
      data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
    },
    {
      label: "My Second dataset",
      fillColor : "rgba(151,187,205,0.2)",
      strokeColor : "rgba(151,187,205,1)",
      pointColor : "rgba(151,187,205,1)",
      pointStrokeColor : "#fff",
      pointHighlightFill : "#fff",
      pointHighlightStroke : "rgba(151,187,205,1)",
      data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
    }
  ]

}

var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx).Line(lineChartData, {
  responsive: true,
  onAnimationComplete:function(){
    var tcanvas=document.createElement('canvas');
    var tctx=tcanvas.getContext('2d');
    tcanvas.width=ctx.canvas.width;
    tcanvas.height=ctx.canvas.height;
    tctx.fillStyle='white';
    tctx.fillRect(0,0,tcanvas.width,tcanvas.height);
    tctx.drawImage(canvas,0,0);
    var img=new Image();
    img.onload=function(){
      document.body.appendChild(img);
    }
    img.src=tcanvas.toDataURL();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<h4>ChartJS line chart</h4>
<div style="width:30%">
  <div>
    <canvas id="canvas" height="450" width="600"></canvas>
  </div>
</div>
<h4>Fully opaque chart as image</h4>
markE
  • 102,905
  • 11
  • 164
  • 176
3

Why don't you try to use css? A canvas element is just like a normal HTML element right? So you can either set it's background colour or set it's background to an image using css.

$('#myChart').css('background-color', 'rgba(0, 0, 0, 0)'); // this worked for me but I'm guessing you can set a background image using css like you normally do.
Chillz
  • 199
  • 5
  • @Mark that's unfortunate. I'll try and find a better solution. – Chillz Oct 27 '16 at 15:14
  • I found the solution on the chartjs issue tracker, but forgot to post back here. I've added an answer now that works with pngs! – Mark Oct 27 '16 at 19:18
0

You can solve this pretty simply, if you're willing to change the code in chart.js.

There's a function in chart.js called clear. You can find the clear function by searching for clearRect. The clear function is called every time chart.js redraws the chart, so that's where we'll set the background. Right below the line with clearRect, add the following lines.

...
chart.ctx.clearRect(0,0,chart.width,chart.height);

// Add these 3 lines
chart.ctx.rect(0, 0, chart.width, chart.height);
chart.ctx.fillStyle="#fff"; // Put your desired background color here
chart.ctx.fill();
...
jastr
  • 881
  • 1
  • 9
  • 19