46

I am using Chart.js. I am trying to convert the chart to an image by getting a base 64 string. The tutorial (http://www.chartjs.org/docs/) devotes an entire 1 line on the topic:

The canvas element also allows for saving the contents as a base 64 string, allowing saving the chart as an image.

A canvas element has the method of toDataURL, which returns a base64 string of the image. However, when I do that, the image it renders is just a transparent rectangle with the dimensions of the chart, and it does not include the chart contents.

Here is a fiddle: http://jsfiddle.net/KSgV7/

The "images" in the fiddle are styled with a black border, so you can see where they are supposed to be, since they seem to just be a big transparent block.

Has anyone successfully converted a Chart.js chart to an image?

1

7 Answers 7

42

The chart seem to be async so you will probably need to provide a callback when the animation has finished or else the canvas will be empty.

var options = {
    bezierCurve : false,
    onAnimationComplete: done  /// calls function done() {} at end
};
5
  • 8
    That was exactly it! Thanks! They should quote you in the charts.js documentation.
    – chiliNUT
    Commented Nov 26, 2013 at 0:20
  • I'm trying to use this exactly but i'm getting a transparent image. The fiddle in this post also has a transparent image
    – KoU_warch
    Commented Feb 19, 2015 at 18:49
  • I was able to fix it by adding updating the image src inside the onAnimationComplete callback
    – KoU_warch
    Commented Feb 19, 2015 at 20:31
  • 5
    "The fiddle doesn't work". Its not supposed to work, it's demonstrating the problem I was having.
    – chiliNUT
    Commented Feb 27, 2017 at 16:46
  • 1
    @KoU_warch I was able to fix it by adding updating the image src inside the onAnimationComplete callback -- how did you manage that? Commented Feb 21, 2018 at 13:22
32

Chart.JS API has changed since this was posted and older examples did not seem to be working for me. here is an updated fiddle that works on the newer versions

HTML:

<body>
    <canvas id="canvas" height="450" width="600"></canvas>
    <img id="url" />
</body>

JS:

function done(){
  alert("haha");
  var url=myLine.toBase64Image();
  document.getElementById("url").src=url;
}

var options = {
  bezierCurve : false,
  animation: {
    onComplete: done
  }
};

var myLine = new 
   Chart(document.getElementById("canvas").getContext("2d"),
     {
        data:lineChartData,
        type:"line",
        options:options
      }
    );

http://jsfiddle.net/KSgV7/585/

4
  • This work very well ! Thanks - but the <img> tag is missing in your explanation (not in the hyperlink) and I would find your explanation more complete if you add this <img> tag.
    – schlebe
    Commented Nov 18, 2017 at 7:54
  • I am happy to fix it but did not get what you are referring two?
    – ngalstyan
    Commented Nov 19, 2017 at 14:09
  • when you write document.getElementById("url").src=url; you reference a html widget with "url" id but I don't see any definition of this element in your post and in any other previous posts. This element is described in the included link but not in your post. I propose that you add this element before Javascript code.
    – schlebe
    Commented Nov 20, 2017 at 14:40
  • Thank you sooo much! You helped me a lot! :)
    – Rebar
    Commented Sep 30, 2019 at 15:57
11

First convert your Chart.js canvas to base64 string.

var url_base64 = document.getElementById('myChart').toDataURL('image/png');

Set it as a href attribute for anchor tag.

link.href = url_base64;

<a id='link' download='filename.png'>Save as Image</a>

0
4

You should use the Chartjs API function toBase64Image() instead and call it after the animation is complete. Therefore:

var pieChart, URI;

var options = {
    animation : {
        onComplete : function(){    
            URI = pieChart.toBase64Image();
        }
    }
};

var content = {
    type: 'pie', //whatever, not relevant for this example
    data: {
        datasets: dataset //whatever, not relevant for this example
    },
    options: options        
};    

pieChart = new Chart(pieChart, content);

Example

You can check this example and run it

var chart = new Chart(ctx, {
   type: 'bar',
   data: {
      labels: ['Standing costs', 'Running costs'], // responsible for how many bars are gonna show on the chart
      // create 12 datasets, since we have 12 items
      // data[0] = labels[0] (data for first bar - 'Standing costs') | data[1] = labels[1] (data for second bar - 'Running costs')
      // put 0, if there is no data for the particular bar
      datasets: [{
         label: 'Washing and cleaning',
         data: [0, 8],
         backgroundColor: '#22aa99'
      }, {
         label: 'Traffic tickets',
         data: [0, 2],
         backgroundColor: '#994499'
      }, {
         label: 'Tolls',
         data: [0, 1],
         backgroundColor: '#316395'
      }, {
         label: 'Parking',
         data: [5, 2],
         backgroundColor: '#b82e2e'
      }, {
         label: 'Car tax',
         data: [0, 1],
         backgroundColor: '#66aa00'
      }, {
         label: 'Repairs and improvements',
         data: [0, 2],
         backgroundColor: '#dd4477'
      }, {
         label: 'Maintenance',
         data: [6, 1],
         backgroundColor: '#0099c6'
      }, {
         label: 'Inspection',
         data: [0, 2],
         backgroundColor: '#990099'
      }, {
         label: 'Loan interest',
         data: [0, 3],
         backgroundColor: '#109618'
      }, {
         label: 'Depreciation of the vehicle',
         data: [0, 2],
         backgroundColor: '#109618'
      }, {
         label: 'Fuel',
         data: [0, 1],
         backgroundColor: '#dc3912'
      }, {
         label: 'Insurance and Breakdown cover',
         data: [4, 0],
         backgroundColor: '#3366cc'
      }]
   },
   options: {
      responsive: false,
      legend: {
         position: 'right' // place legend on the right side of chart
      },
      scales: {
         xAxes: [{
            stacked: true // this should be set to make the bars stacked
         }],
         yAxes: [{
            stacked: true // this also..
         }]
      },
      animation : {
         onComplete : done
      }      
   }
});

function done(){
    alert(chart.toBase64Image());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx" width="700"></canvas>

2

You can access afterRender hook by using plugins.

And here are all the plugin api available.

In html file:

<html>
  <canvas id="myChart"></canvas>
  <div id="imgWrap"></div>
</html>

In js file:

var chart = new Chart(ctx, {
  ...,
  plugins: [{
    afterRender: function () {
      // Do anything you want
      renderIntoImage()
    },
  }],
  ...,
});

const renderIntoImage = () => {
  const canvas = document.getElementById('myChart')
  const imgWrap = document.getElementById('imgWrap')
  var img = new Image();
  img.src = canvas.toDataURL()
  imgWrap.appendChild(img)
  canvas.style.display = 'none'
}
1

You can also use the toBase64Image() method setting animation: false

var options = {
    bezierCurve : false,
    animation: false
};

Updated Fiddle

0
0

@EH_warch You need to use the Complete callback to generate your base64:

onAnimationComplete: function(){
    console.log(this.toBase64Image())
}

If you see a white image, it means you called the toBase64Image before it finished rendering.

Not the answer you're looking for? Browse other questions tagged or ask your own question.