Cacious
Cacious

Reputation: 165

Why is HTML5 drawImage not drawing images on canvas?

I have tried adding an eventListener to draw the image only after the image has loaded. I have also tried placing the event listener before and after setting the image src but still nothing seems to work.

I just cant seem to figure out exactly what's causing this. Can someone explain please.

import React from 'react';
import {useEffect, useState} from 'react';

function Main() {
    //initiate state
    const [ plane, setPlane ] = useState( new Image() );
    const [ star, setStar ] = useState( new Image() );
    const [ bird, setBird ] = useState( new Image() );
    const [ parachute, setParachute ] = useState( new Image() );
    const [ cloud, setCloud ] = useState( new Image() );
    
    //draw the game animation
    let draw = () => {
        const canvas = document.getElementById('canvas');
        const context = canvas.getContext('2d'); //context

        //set background color
        context.fillStyle = '#74b9ff';
        //context.fillRect(0, 0, canvas.width, canvas.height);

        //load images
        plane.src = 'images/plane.png';
        plane.onload = drawImage( context);
        star.src = 'images/star.png';
        bird.src = 'images/bird.png';
        parachute.src = 'images/parachute.png';
        //cloud.src = 'images/cloud.png';
        // setPlane( prevState => { return {...prevState, src: 'images/plane.png' }  } );
        // setStar( prevState => { return {...prevState, src: 'images/star.png' }  } );
        // setBird( prevState => { return {...prevState, src: 'images/bird.png' }  } );
        // setParachute( prevState => { return {...prevState, src: 'images/parachute.png' }  } );
        // setCloud( prevState => { return {...prevState, src: 'images/cloud.png' }  } );
        
        

    }

    let drawImage = ( context ) => {
        context.drawImage(plane, 0, 0, 50, 50);
    }

    useEffect( () => draw(), [] );

    return (
        <div className="container">
            <canvas width='400px' height='400px' id='canvas'></canvas>
        </div>
    );
}

export default Main;

Upvotes: 0

Views: 842

Answers (1)

obscure
obscure

Reputation: 12891

It might not be obvious but your issue is caused by the following line:

plane.onload = drawImage( context);

If written that way, it simply doesn't do what you might think. The onload event can be assigned a callback function - which as the name implies - fires a function as soon as something finished loading. If you append parentheses (), it will actually execute the function right away.

That means it will try to draw the image before it even finished loading.

What you need to to is pass a reference to your function by it's name only.

plane.onload = drawImage;

The problem in your case is that you want to pass a parameter. This can't be done - at least not in this way.

What you can do however is making the context a property of the Image object itself.

Something like:

let draw = () => {
    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d'); //context

    context.fillStyle = '#74b9ff';

    plane.context=context;
    plane.onload = drawImage;        
    plane.src = 'images/plane.png';
}

let drawImage = ( e ) => {
  e.target.context.drawImage(plane, 0, 0, 50, 50);
}

Upvotes: 1

Related Questions