Merge Multiple Canvases and Download as Image
Asked Answered
P

1

5

I'm attempting to merge two HTML canvases into a single canvas and then download that as an image. My code is as below:

function downloadCanvas() {
    var bottleCanvas = document.getElementById('bottleCanvas');
    var designCanvas = document.getElementById('editorCanvas');

    var bottleContext = bottleCanvas.getContext('2d');
    bottleContext.drawImage(designCanvas, 69, 50);

    var dataURL = bottleCanvas.toDataURL("image/png");
    var link = document.createElement('a');
    link.download = "bottle-design.png";
    link.href = bottleCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
    link.click();
}

My problem here seems to be the following line:

bottleContext.drawImage(designCanvas, 69, 50);

This is supposed to draw the contents of one canvas onto the other, which it does, but then prevents the latter part of the code from running and downloading the image. When I remove this particular line the download function works fine, but unfortunately only downloads one of the canvases.

My question therefore is either: What am I doing wrong here? or How would I merge two HTML canvases and then download it as an image.

(On another note, my above code for downloading only works well in Chrome - in other browsers I am unable to set the name of the file and set the file extension.)

Parisparish answered 10/4, 2015 at 1:28 Comment(0)
R
9

You are likely encountering a security error caused by drawing an image whose source is cross-domain onto your canvas. Drawing a cross-domain image onto any canvas will "taint" that canvas and disallow context.toDataURL and raise a security error if you attempt to execute toDataURL. This same "tainting" will occur if you drawImage a contaminated canvas onto a non-contaminated canvas.

The fix is to make sure all images you draw on the canvas originate on the same domain as your webpage.

Here is an example of your code working properly when using an image that does not raise the cross-domain security error:

var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/fish.jpg";
function start(){

  var bottleCanvas = document.getElementById('bottleCanvas');
  var designCanvas = document.getElementById('editorCanvas');
  var ctxb=bottleCanvas.getContext('2d');
  var ctxd=editorCanvas.getContext('2d');

  ctxb.drawImage(img,0,0);
  ctxd.fillRect(50,50,50,50);

  downloadCanvas();
}

function downloadCanvas() {
  var bottleCanvas = document.getElementById('bottleCanvas');
  var designCanvas = document.getElementById('editorCanvas');

  var bottleContext = bottleCanvas.getContext('2d');
  bottleContext.drawImage(designCanvas, 69, 50);

  var dataURL = bottleCanvas.toDataURL("image/png");
  var link = document.createElement('a');
  link.download = "bottle-design.png";
  link.href = bottleCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
  link.click();
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<canvas id="bottleCanvas" width=300 height=300></canvas>
<canvas id="editorCanvas" width=300 height=300></canvas>

Satisfying cross-origin security restrictions

You can host your image on a server that already allows cross-origin access to it's images. That's what I do in my example above. Dropbox.com allows you to specify that an image it hosts may be drawn to canvas without "tainting" that canvas.

You can also configure your S3 bucket to allow cross-origin access to your images. This link provides instructions on how to set the response headers to server cross-origin images: http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html

Note that as in my example, if you're using cross-origin images, you must also set the image.crossOrigin='anonymous' flag when you initially create the image object in javascript.

Racer answered 10/4, 2015 at 1:45 Comment(2)
Thanks for this. But is there no way of allowing for one image to be hosted externally? The bottleCanvas uses an image stored locally whereas editorCanvas uses an image stored in an S3 bucket - as it will be different for each user.Parisparish
There's no way to bypass cross-origin security--you must comply with it. If "stored locally" means on your local harddrive (as opposed to a directory in your served website), then that image will "taint" your canvas. S3 images can be made to comply with cross-origin security restrictions, but they do not do so out-of-the-box. I've added to my answer explaining more about this. Good luck with your project.Racer

© 2022 - 2024 — McMap. All rights reserved.