Canvas Draw Image issue on firefox, works well in chrome
Asked Answered
L

1

5

I will assume this is some sort of compatibility issue. Everything works perfectly in chrome, but in firefox the <canvas> simply doesn't draw anything.

function drawStage(stageNum) {
    var cap = canvasArray.length; //keeps the canvasElements
    var i;
    var stageImages = images["stage" + stageNum];
    var stageDimensions = imageDimensions["stage" + stageNum];
    //console.log("Cap is: " + cap);


    for (i = 0; i < cap; i++) {
        var canvas = document.getElementById(canvasArray[i]);
        var canvasContext = canvas.getContext("2d");
        var image = document.getElementById(stageImages[i]);

        canvasContext.clearRect(0, 0, 1280, 1280);
        canvasContext.drawImage(image, stageDimensions[i][0], stageDimensions[i][1], stageDimensions[i][2], stageDimensions[i][3]);
        //document.getElementById(zIndexes[i][0]).style["z-index"] = zIndexes[i][stageNum];
        //console.log(document.getElementById(zIndexes[i][0]).id);
    }
}

EDIT: here is dropbox link for "test" code-sample that I made, much simpler, 1 image, 1 canvas, 1 div, similar (practically same) script, that is still not working in firefox, yet works in chrome. If you can solve the issue there, then this will be solved as well.

UPDATE: Arrays are below, per request, they are simple imput of different elements/coordinates. I assure you though, there is no issue within the arrays themselves, everything works nice in chrome. Something about the DrawImage() method from the function is causing the problem I'd say. (further explanation below the arrays)

images is a (global) array of ids for the <img> elements.

    var images = {
    stage1: ["character1Base", "character2Base", "character3Base", "character4Base", "character5Base", "character6Base", "character3BotImg"],
    stage2: ["character1Sit", "character2Base", "character3Base", "character4Base", "character5Base", "character6Base", "character3BotImg"],
    stage3: ["character1Sit", "character2Drink", "character3Base", "character4Base", "character5Base", "character6Base", "character3BotImg"],
    stage4: ["character1Sit", "character2Drink", "character3Base", "character4Base", "character5Base", "character6Doll", "character3BotImg"],
    stage5: ["character1Sit", "character2Drink", "character3Eat", "character4Base", "character5Base", "character6Doll", "character3EatBot"],
    stage6: ["character1Sit", "character2Stand", "character3Eat", "character4Base", "character5Base", "character6Doll", "character3EatBot"],
    stage7: ["character1Sit", "character2Stand", "character3Eat", "character4Sit", "character5Base", "character6Doll", "character3EatBot"],
    stage8: ["character1Sit", "character2Stand", "character3Eat", "character4Sit", "character5Sit", "character6Doll", "character3EatBot"],
    stage9: ["character1Sit", "character2Eat", "character3Eat", "character4Sit", "character5Sit", "character6Doll", "character3EatBot"],
    stage10: ["character1Drink", "character2Eat", "character3Eat", "character4Sit", "character5Sit", "character6Doll", "character3EatBot"]
};

imageDimensions is a (global) array of well, dimensions, posX, posY, sizeX & sizeY.

    var imageDimensions = {
    stage1: [[0, 0, 233, 485], [0, 0, 153, 407], [20, 0, 220, 200], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 0, 220, 180]],
    stage2: [[105, 35, 180, 440], [0, 0, 153, 407], [20, 0, 220, 200], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 0, 220, 180]],
    stage3: [[105, 35, 180, 440], [0, 0, 153, 407], [20, 0, 220, 200], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 0, 220, 180]],
    stage4: [[105, 35, 180, 440], [0, 0, 153, 407], [20, 0, 220, 200], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 0, 220, 180]],
    stage5: [[105, 35, 180, 440], [0, 0, 153, 407], [3, 7, 226, 180], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 9, 220, 180]],
    stage6: [[105, 35, 180, 440], [0, 0, 172, 490], [3, 7, 226, 180], [0, 150, 505, 210], [0, 0, 315, 391], [0, 0, 480, 435], [1, 9, 220, 180]],
    stage7: [[105, 35, 180, 440], [0, 0, 172, 490], [3, 7, 226, 180], [70, 0, 341, 380], [0, 0, 315, 391], [0, 0, 480, 435], [1, 9, 220, 180]],
    stage8: [[105, 35, 180, 440], [0, 0, 172, 490], [3, 7, 226, 180], [70, 0, 341, 380], [18, 72, 305, 422], [0, 0, 480, 435], [1, 9, 220, 180]],
    stage9: [[105, 35, 180, 440], [0, 0, 173, 473], [3, 7, 226, 180], [70, 0, 341, 380], [18, 72, 305, 422], [0, 0, 480, 435], [1, 9, 220, 180]],
    stage10: [[105, 32, 162, 440], [0, 0, 173, 473], [3, 7, 226, 180], [70, 0, 341, 380], [18, 72, 305, 422], [0, 0, 480, 435], [1, 9, 220, 180]]
};

Explanation: The function is meant to draw on screen in different elements, characters that belong to these elements. They are drawn in canvas (will later be animated in canvas as well). The stageNum which is the only parameter the function takes determines what will be drawn.

Anyone know what the problem is? (I've read a few similar posts but issue in all of them was layerX, layerY of the mouse position, however I am not using mouseover / mousein / mouseout here at all, still elements are not drawn.

Ledeen answered 10/1, 2016 at 15:2 Comment(7)
Could you provide the arrays you are passing and the relevant HTML?Chancemedley
Sure I will be adding them to the main post in a moment, sorry, was afk for dinner.Ledeen
I cant remember, did you say you used window.onload to start? If you did not then there is the possibility that the images have not loaded. Also try putting if(canvas === null) and if(image === null) to make sure that getElementById is finding the elements you are looking for.Chancemedley
I did use the window.onload to start but since then I've changed it to load 5 seconds later, to avoid that kind of error. Images are loaded, 100% since I've put their reference in the document and I can see them being there, they just do not get drawn on canvas (I stacked them one on top of the other on the side to make sure they are loaded). According to the alerts/consol.log I am getting the div, just drawing is not working for some reason.Ledeen
I have used ctx.drawImage(document.getElementById("imageID"),0,0) on firefox (Developers edition 45.0a2 (2016-01-08)) and it works fine. So it must be something to do with the page as I can see nothing wrong with your code.Chancemedley
I just made an entire new page with only 1 image, 1 canvas and the same script used to draw it as with this code, doesn't work with firefox, works in chrome. Image, canvas and the drawImage method all use the same parameters, 200x450. Didn't work so I really don't know wtf... canvasContext.drawImage(image, 0, 0, 200, 450);Ledeen
javascript code, html, css and image files from the "test example" I made can be found on my dropboxlink (next 24 hours): dropbox.com/s/8zb2fudbjmuual2/test.rar?dl=0Ledeen
S
11

Your problem is that the image your are trying to draw are svg images, and that these svg documents have relative width and height attributes.

The browser can't set a height nor a width to the image it has to draw, and hence it can't render it to the canvas. (It is able to do an estimation in the document, since it can be relative to something, but not in the canvas).

So the solution is to set absolute width and height attributes in your svg files,

Or, more complicated, to first draw it into an <iframe> or an <object>, then draw a serialized version where you'll have set these attributes.

function initialize() {
    var canvas = document.getElementById("char1Canvas");
    var canvasContext = canvas.getContext("2d");
    var image = document.getElementById("char1Img");
    resizeSVG(image, function(e){
        canvasContext.clearRect(0, 0, 1280, 1280);
        canvasContext.drawImage(this, 0, 0);
    });
};

var resizeSVG = function(svgImg, callback){
    // create an iframe
    var iframe = document.createElement('iframe');
    // so we don't see it
    iframe.height = 0;
    iframe.width = 0;
    iframe.onload = function(){
        var doc = iframe.contentDocument;
        var svg = doc.querySelector('svg');
        // get the computed width and height of your img element
        // should probably be tweaked
        var bbox = svgImg.getBoundingClientRect();
        // if it's a relative width
        if (svg.width.baseVal.unitType !== 1) {
            svg.setAttribute('width', bbox.width);
        }
        // or a relative height
        if (svg.height.baseVal.unitType !== 1) {
            svg.setAttribute('height', bbox.height);
        }
        // serialize our updated svg
        var svgData = (new XMLSerializer()).serializeToString(svg);
        var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
        // create a new Image Object that ill be draw on the canvas
        var img = new Image();
        img.onload = callback;
        img.src = svgURL;
        // remove the iframe
        document.body.removeChild(iframe);
    };
    iframe.src = svgImg.src;
    document.body.appendChild(iframe);
}
Semifinal answered 11/1, 2016 at 2:56 Comment(6)
Thanks for an extensive answer. I ran with your code, appears to have an error on var doc = iframe.contentDocument; line, goes as follows: > Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "null" from accessing a frame with origin "null". Protocols, domains, and ports must match. However putting absolute values inside the .svg files made it work. It is weird though that chrome knows how to compute them based on the values inside the <img> tag while firefox doesn't.Ledeen
To add (lacking character space above), I've tried surrounding the <img> tag with a <div> that has width/height since the svg is set to 100% 100%, but that doesn't work either, it has to be manually edited in the svg. For me this is an acceptable solution since I will be using the static sizes when drawing but it might be worth it to see how to use the suggested function to compute them because if one SVG is to be used multiple times with different sizes, it has to be multiple svg images, sadly.Ledeen
Yes, to access the iframe, you probably have to use it on a server (be it localhost). I think that on file:// system it will fail. For an example, just replace your code in script.js with this block and it should work (on a server)Semifinal
Will try once i get access to the servers again, home atm. Thanks for all the help!Ledeen
My <svg> element had width="100%" height="100%". You are a genius for knowing that Firefox (only) requires those to be present and changed to absolute numbers!Bouchard
Re: "So the solution is to set absolute width and height attributes in your svg files," - That saved me a couple of hours. Many thanks. +1.Mainsail

© 2022 - 2024 — McMap. All rights reserved.