1

I'm still new to this whole canvas things and there's something I have a problem with, namely, saving the content inside the canvas as image. Here's my fiddle

var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
};

var link = document.createElement('a');
link.innerHTML = 'Save';
link.href = document.getElementById('canvas').toDataURL();

link.addEventListener('click', function(e) {
  link.download = "imagename.png";
}, false);

document.body.appendChild(link);
<canvas id="canvas" width="300" height="300"></canvas>
    

While the save dialog appears just fine, the saved image is empty, like it doesn't capture the content at all.

Any help is appreciated, moreso if it's accompanied by on how/why my code doesn't work and your code will. Thanks.

Edit: Forgot to link the fiddle I base my code from here The difference is that that fiddle saves the drawing in the canvas while what I wrote is merely saving a static image from another source.

Durga
  • 15,263
  • 2
  • 28
  • 52
nana77
  • 15
  • 6
  • Possible duplicate of [How To Save Canvas As An Image With canvas.toDataURL()?](https://stackoverflow.com/questions/10673122/how-to-save-canvas-as-an-image-with-canvas-todataurl) – Fred Gandt Aug 08 '17 at 03:26
  • I have tried that one (and many others) to no avail. Maybe because my understanding is still lacking. Thanks for reminding me still. – nana77 Aug 08 '17 at 03:40
  • 1
    A few problems : You are drawing an crossOrigin image. This will taint your canvas, blocking its export methods. Also, `image.onload` is asynchronous. This means that you will actually draw your image, after the synchronous part of your code has finished executing, this means that at the time you call `toDataURL` there is still nothing drawn on your canvas. You have to call the export part only after your image has been drawn. – Kaiido Aug 08 '17 at 05:42
  • @nana77 You have to set href inside `onload` of image and it will be working. check my answer below. – Yogen Darji Aug 08 '17 at 05:59

2 Answers2

0

You need to set image href inside of onload and use img.crossOrigin = "anonymous" just to avoid cross origin error.

var img = new Image();
img.crossOrigin = "anonymous";
img.src = 'http://farm5.static.flickr.com/4005/4706825697_c0367e6dee_b.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

img.onload = function() {
  ctx.drawImage(img, 0, 0, 300, 300);
  link.href = document.getElementById('canvas').toDataURL();
};

var link = document.createElement('a');
link.innerHTML = 'Save';
//link.href = document.getElementById('canvas').toDataURL();

link.addEventListener('click', function(e) {
  link.download = "imagename.png";
}, false);

document.body.appendChild(link);
<canvas id="canvas" width="300" height="300"></canvas>
Yogen Darji
  • 3,230
  • 16
  • 31
0

There are two issues.

The first is the image's origin. On JSFiddle you'll get an Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

  • As mentioned in another answer, Base64 encoding the image then setting img.src = 'data:image/jpeg;base64,/...'; resolves this problem.
  • Another solution: run a web server locally, and source the image by its location on the server, e.g. img.src = './rhino.jpg';.

Even after that's handled, the downloaded image will be "empty," as before. This is because of the order of execution of your code. link.href = canvas.toDataURL(); executes before the image's 'load' event fires, so the canvas is empty at the point you're setting the data.

  • Placing that line inside the img.onload function ensures that the link's download data is set only after the image has loaded. This approach is convenient, because it allows for the <a> tag to behave with default behavior once the href attribute is set (e.g. pointer on hover, underlined text by default).
  • Placing that line, instead, inside the link's 'click' event handler incidentally works, but you lose the default link behavior mentioned above.

Here's an example of the working JS, assuming the image file is on the server:

var img = new Image();
img.src = './rhino.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var link = document.createElement('a');
link.innerHTML = 'Save';

img.onload = function() {
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
  link.href = canvas.toDataURL();
};


link.addEventListener('click', function(e) {
  link.download = "imagename.png";
}, false);

document.body.appendChild(link);

And a JSFiddle, using the Base-64 encoded image.

Trojan
  • 2,256
  • 28
  • 40
  • Thanks for the answer. Gotta ask, though, how do you generate that oh-so-long base-64 encoded image? – nana77 Aug 08 '17 at 06:57
  • I used https://www.base64-image.de, which I found by searching "base 64 encode image." – Trojan Aug 08 '17 at 19:20