20

In a client-side standalone JS application, I'm trying to make it so I can call toDataURL() on a canvas on which I've drawn some images specified by a URL. Ie I can input into a textbox the url to any image (hosted on, say, imgur) that I want to draw on the canvas, click a "draw" button and it will draw on the canvas. The end user should be able to save their final render as a single image, for this I'm using toDataURL().

Anyway, until they actually fix that annoying "operation is insecure" error (gee, you're going to tell the end user what they can and can't do with their own data?) I followed a workaround that said to add the image to the DOM and set its crossOrigin property to "Anonmyous" and then draw it to the canvas.

Here's a full working simplified version of my code (but in reality there will be many more features):

<!DOCTYPE html5>
<html>
<head>
<style>
#canvas {border:10px solid green;background-color:black;}
#imgbox {border:2px solid black;}
</style>
</head>
<body>
<canvas id="canvas" width=336 height=336></canvas>
<br><br>
<input size=60 id="imgbox">
<input type="submit" value="Draw" onclick=draw()>
<script>
function draw() {
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var img = new Image();
    img.src = document.getElementById("imgbox").value;
    img.crossOrigin = "Anonymous";
    context.drawImage(img, 40, 40);
}
</script>
</body>
</html>

Without the img.crossOrigin = "Anonymous"; line, I could input http://i.imgur.com/c2wRzfD.jpg into the textbox and click draw and it would work. However as soon as I added that line, the whole thing broke and it won't even be drawn to the canvas at all.

What do I need to change to fix this? I really need to be able to implement the functionality for the end user to save their final image and it's extremely annoying that the people who wrote the html5 spec purposely introduced this bug.

Joey
  • 10,504
  • 16
  • 39
  • 54
  • 1
    For me, my problem was that I had `crossOrigin` accidentally spelled as all lowercase `crossorigin`. – Ryan Mar 14 '20 at 19:29

3 Answers3

42

You must set the CORS request before the src - just swap the lines into:

img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;

You will also need to add an onload handler to the image as loading is asynchronous:

img.onload = function() {
    context.drawImage(this, 40, 40);
    // call next step in your code here, f.ex: nextStep();
};
img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;
  • Wait, now it only works with some images. Images from imgur work but some random images from google image search still won't draw to the canvas. For example, http://icons.iconarchive.com/icons/yellowicon/game-stars/256/Mario-icon.png won't get drawn at all. Any idea why? – Joey Apr 17 '14 at 02:10
  • 6
    @Joey you can only *request* CORS usage, the server may refuse the request which will make the image loading fail. You need to check which servers allow and which doesn't. For those who won't allow CORS use just remove the request (crossOrigin line). For images that you can't load using CORS request you need to copy them to your own server (same as page) or move them to another server which allow cors use. (hope that made sense) –  Apr 17 '14 at 02:16
  • Ugh, alight. Suppose I convert the image to a base64 string and then create a new image using that as the data URL? Can the client enable cors for that new image? – Joey Apr 17 '14 at 02:30
  • @Joey a data-uri doesn't need cors but in order to extract a data-uri it the original image draw to canvas must fulfill cors (also if loaded by httpxmlrequests).. is no escape :) –  Apr 17 '14 at 02:36
  • 1
    How does it know whether the original image was cors after I put it in base64 and draw it to the canvas? – Joey Apr 17 '14 at 04:47
  • @Joey there is no safe way to check this. If the image didn't support cors you will get a security error (code 18) when you try to extract pixels using either toDataURL() or getImageData(). You can catch that error - or make sure the images are in the same origin as your page (which always works) –  Apr 17 '14 at 07:58
  • 2
    I don't quite understand. If I programmatically (using javascript) generated a base64 string that, if converted to a png image would just be random pixels, can I write that to the canvas without getting a cors error? – Joey Apr 18 '14 at 02:52
  • 2
    I'm puzzled by this `img.crossOrigin = "Anonymous";` which would seem to be needed by browsers that throw a security error when it thinks there is something cross-domain-ish going on. Whether or not this line is included, Chrome has no problem displaying and linking to/downloading file data from a `toDataURL("image/png");` function, whereas Safari 9.x fails in all cases without exception. And this is all on the same domain. – cbmtrx Nov 28 '15 at 13:45
  • Thank you so much, you saved my day. Couldn't have noticed that the positioning of these attributes could actually break it on a browser. Very weird bug in chrome, worked fine in firefox though. – Minkesh Jain May 29 '19 at 08:17
3

When the server requires authorization to access the images the value should be:

img.crossOrigin = "Use-Credentials";

Otherwise the browser will give up after receiving HTTP 401.

Dzmitry
  • 1,351
  • 1
  • 9
  • 6
0

If your image disappears after setting cross origin to anonymous it means your server doesn't allow cross origin. If you're using amazon s3 to serve your images, you need to enable public access to your bucket, and then add cross origin policy (from templates). After that adding cross origin "anonymous" should work.

ruleboy21
  • 5,510
  • 4
  • 17
  • 34