Drawing images to canvas with img.crossOrigin = "Anonymous" doesn't work
Asked Answered
T

3

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.

Transfiguration answered 17/4, 2014 at 1:53 Comment(1)
For me, my problem was that I had crossOrigin accidentally spelled as all lowercase crossorigin.Rommel
F
43

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;
Fraudulent answered 17/4, 2014 at 1:55 Comment(9)
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, icons.iconarchive.com/icons/yellowicon/game-stars/256/… won't get drawn at all. Any idea why?Transfiguration
@Transfiguration 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)Fraudulent
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?Transfiguration
@Transfiguration 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 :)Fraudulent
How does it know whether the original image was cors after I put it in base64 and draw it to the canvas?Transfiguration
@Transfiguration 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)Fraudulent
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?Transfiguration
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.Lauzon
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.Viaticum
S
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.

Syrup answered 23/12, 2014 at 14:54 Comment(0)
B
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.

Bomar answered 10/12, 2022 at 6:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.