1

I have two canvas:

· The first one is the canvas I want to export that contains a pattern as a background (filled through js) and some images that the user will put by clicking the canvas.

· The second one is above the first one and is used as a sort of "preview" of the images that will be pasted on the first canvas.

In a nutshell: by mousemoving on the second canvas I see the preview of the image I'll paste, while by clicking (again on the second canvas) I will parse the image on the FIRST canvas.

Ignoring the HTML code which is supposely not the problem, my script is this one:

 $(window).ready(function(){
    $(document).ready(function(){
        $('myCanvas').ready(function(){
            canvasObject = new canvasElement ('myCanvas','2d');
            canvasObject.initSea();
            canvasLayer1 = new canvasElement ('myCanvas2','2d');

            $('#myCanvas2').on("click",function(mouseClickEvent){
                $(this).off('mousemove');
                var position = canvasLayer1.getMouseCoordinates(mouseClickEvent);
                canvasObject.parseImage('elements/objects/wooden_turrets.png',(position.x),(position.y));
            });

            $('#myCanvas2').on("mousemove",function(mouseMoveEvent){
                canvasLayer1.reset();
                var currentPos = canvasLayer1.getMouseCoordinates(mouseMoveEvent);
                canvasLayer1.parseImage('elements/objects/wooden_turrets.png',(currentPos.x),(currentPos.y));
            });

            $('#render').click(function() {
                canvasObject.renderSVG();
            });
        });
    });
});

function canvasElement (id, context){
    this.id = id;
    this.canvas = document.getElementById(id);
    this.context = this.canvas.getContext(context);
}

canvasElement.prototype.parseImage = function (imgurl, x, y) {
    tmpContext = this.context;
    var imgObject = new Image();
    imgObject.onload = function() {
        tmpContext.drawImage(imgObject, x, y);
    };
    imgObject.src = imgurl;
};

canvasElement.prototype.reset = function () {
    tmpContext = this.context;
    // tmpContext.setTransform(1, 0, 0, 1, 0, 0);
    tmpContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
};

canvasElement.prototype.getMouseCoordinates = function (mouseEvent) {
    var area = this.canvas.getBoundingClientRect();
    return {
        x: mouseEvent.clientX - area.left,
        y: mouseEvent.clientY - area.top
    };
};

canvasElement.prototype.initSea = function () {
    var tmpCanvas = this.canvas;
    var tmpContext = this.context;
    var seaImg = new Image();
    seaImg.src = 'elements/land/water.png';
    seaImg.onload = function () {
        var pattern = tmpContext.createPattern(seaImg,"repeat");
        tmpContext.rect(0,0,tmpCanvas.width,tmpCanvas.height);
        tmpContext.fillStyle = pattern;
        tmpContext.fill();
    }

    console.log('filling');
};

canvasElement.prototype.renderSVG = function () {
    var imgObject = this.canvas.toDataURL("image/png");
    window.location = imgObject;
};

Now, everything is fine and is working perfectly, but whenever I'm calling the method renderSVG() which refers to the canvas method toDataURL("image/png"); I get this error:

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

Now, I've checked a couple of questions similar to mine like:

This: toDataURL throw Uncaught Security exception This: Get security error when saving canvas object into an image

But what I'm not getting is why it is throwing a security error since the images ARE on my domain (in this case locally), not in any other domain.

So the questions are:

1) Why is this happening? shouldn't it be exporting without any issue since the images are located on the same domain as the canvas?

2) I've looked forward thinking that, perhaps, using some libraries such as fabric.js would help me, since I've seen a lot of comfort functions but will the same error occur even using any of them?

3) Is there any way to avoid this issue?

Thanks.

Community
  • 1
  • 1
briosheje
  • 7,356
  • 2
  • 32
  • 54

2 Answers2

3

Your local hard drive is considered cross-domain and your canvas is tainted.

This may seem a bit strict, but your local hard drive is where your most sensitive data probably resides.

Temporary fix #1: Move both the image and your web page html to the desktop.

Temporary fix #2: If that doesn't work, temporarily host :

  • open up a free account on dropbox.com
  • put your image(s) in the public folder of your account
  • right-click and get the public link to an image and use that public link in your app
  • when loading the image, set the crossOrigin flag to "anonymous"

Sure fix:

Host both your images and your full web app on the same web server.

For testing, You can install local versions of PHP server or IIS server depending on your O/S.

markE
  • 102,905
  • 11
  • 164
  • 176
  • I've got either a domain or a dropbox space and, moreover, a local PHP environment with XAMPP (which is where I'm trying it from) but it doesn't work. I'll try using a web domain then! – briosheje Mar 19 '14 at 15:45
  • Yep, that's the surest solution. Remember that there are also headers you must set to allow untainted delivery of your images from your server to your client and you will need crossOrigin="anonymous". Be sure you configure your server appropriately. Good luck with your project! – markE Mar 19 '14 at 15:56
0

You have used crossorigin imagedata on your canvas which makes it 'Tainted'. Read about it here.

fzzle
  • 1,466
  • 5
  • 23
  • 28
  • I've already read about cross-origin resources, but I didn't think they could give me any problem using an image I'VE made locally and is stored locally. What I'm wondering if is there is, however, any way to avoid this. – briosheje Mar 19 '14 at 09:44
  • 1
    I'm not sure if I can help you then - you could try to set the `.crossOrigin` attribute to `'anonymous'` on your images. – fzzle Mar 19 '14 at 09:49
  • tried now but doesn't work either. It still popup the same error like if no change was done – briosheje Mar 19 '14 at 09:52