1
\$\begingroup\$

I tried to draw Mario Sprites in my HTML Canvas, but it always went blank. Anybody knows why?

var canvas = document.getElementById("screen");
var ctx = canvas.getContext("2d");
var mario;
var marioSprite;
var tiles;
var speedX = 1;

//marioSprite Specification
mario = {
    //target picture specification X & Y, Width & Height
    px:276,
    py: 44,
    pwidth: 16,
    pheight: 16,
    //drawing on Canvas specification X & Y, Width & Height
    cx: 16,
    cy: 116,
    cwidth:16,
    cheight:16,
}


//draw Mario in Canvas
function drawMarioSprite(){
    marioSprite = new Image();
    marioSprite.src = 'file:///C:/Users/MASTONO ALI/Desktop/img/characters.gif';
    marioSprite.addEventListener('load', e => {
    ctx.drawImage(marioSprite, 
        mario.px, mario.py, mario.pwidth, mario.pheight, 
        mario.cx, mario.cy, mario.cwidth, mario.cheight);
    });
    this.position = function() {
        this.mario.cx += speedX;       
    }
    position();
}//drawMarioSprite() end

//drawTiles and tilesLoop
function drawTiles() {
    tiles = new Image();
    tiles.src = 'file:///C:/Users/MASTONO ALI/Desktop/img/tiles.png';
    tiles.addEventListener('load', e => {
        tilesLoop();
    });
}
function tilesLoop(){
    for (let x = 0; x < 25; x++){
        for (let y = 11; y < 13; y++){
            ctx.drawImage(tiles, 
                0, 0, 26, 26, 
                x*12, y*12, 20, 20);
        }
    }
}//drawTiles() end


function draw(){
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawMarioSprite();
    drawTiles();
    window.requestAnimationFrame(draw);
}

window.requestAnimationFrame(draw);

\$\endgroup\$
1
\$\begingroup\$

You probably don't want to call the drawing code inside the 'load' event handler, since for that to happen more than once, you would need to load the image multiple times which you also don't want to do. See the docs

You should load the images once at startup and then draw them from the pre-loaded data.

In this case, since you do the drawing code repeatedly with requestAnimationFrame, once the image actually loads it will be drawn correctly, if you just remove the load eventlisteners and just directly call the drawing code.

That is, change

    marioSprite.addEventListener('load', e => {
    ctx.drawImage(marioSprite, 
        mario.px, mario.py, mario.pwidth, mario.pheight, 
        mario.cx, mario.cy, mario.cwidth, mario.cheight);
    });

to

    ctx.drawImage(marioSprite, 
        mario.px, mario.py, mario.pwidth, mario.pheight, 
        mario.cx, mario.cy, mario.cwidth, mario.cheight);

and

    tiles.addEventListener('load', e => {
        tilesLoop();
    });

to

    tilesLoop();
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ One of the most annoying parts when doing game development in HTML+JavaScript is that you can never rely on any asset being actually loaded. Which is why I usually create a Sprite class which is both responsible for loading and drawing an image, and when the image didn't load yet, the draw function just does nothing. Another option is to use a preloader which loads all the assets and to not start rendering until the onload-callbacks of all assets were called. \$\endgroup\$ – Philipp Jul 31 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.