Failed to execute 'drawImage' on 'CanvasRenderingContext2D'

16,482

You have a load, onready, callback mechanism looks like.

So i would do as a main function to start all:

assets = ['images/water-block.png',
          'images/stone-block.png',
          'images/grass-block.png'
         ];
var then = Date.now();
reset();
resources.onReady(main);
resources.load(assets);

even if resources.get() would carry on the loading of image, that is asyncronous by nature, you would have the error anyway because drawimage expect the resource to be there, not be loaded and then drawn.

Share:
16,482
Janice
Author by

Janice

Updated on June 27, 2022

Comments

  • Janice
    Janice almost 2 years

    I received the following error:

    Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap)'

    I saw reference to the same error here but the implementation was different from mines. This is a game and it renders some images but still gives this error. Here is my code:

    This is the line where chrome states the error is:

    for (row = 0; row < numRows; row++) {
        for (col = 0; col < numCols; col++) {
            /* The drawImage function of the canvas' context element
             * requires 3 parameters: the image to draw, the x coordinate
             * to start drawing and the y coordinate to start drawing.
             * We're using our Resources helpers to refer to our images
             * so that we get the benefits of caching these images, since
             * we're using them over and over.
             */
             ctx.drawImage(resources.get(rowImages[row]), col * 101, row * 83);
            }
    }
    

    It's the ctx.drawImage(resources.get(rowImages[row]), col * 101, row * 83).

    This is the full function render(). The images are contained in the following array:

    function render() {
            /* This array holds the relative URL to the image used
             * for that particular row of the game level.
             */
            var rowImages = [
                    'images/water-block.png',   // Top row is water
                    'images/stone-block.png',   // Row 1 of 3 of stone
                    'images/stone-block.png',   // Row 2 of 3 of stone
                    'images/stone-block.png',   // Row 3 of 3 of stone
                    'images/grass-block.png',   // Row 1 of 2 of grass
                    'images/grass-block.png'    // Row 2 of 2 of grass
                ],
                numRows = 6,
                numCols = 5,
                row, col;
    
            /* Loop through the number of rows and columns we've defined above
             * and, using the rowImages array, draw the correct image for that
             * portion of the "grid"
             */
            for (row = 0; row < numRows; row++) {
                for (col = 0; col < numCols; col++) {
                    /* The drawImage function of the canvas' context element
                     * requires 3 parameters: the image to draw, the x coordinate
                     * to start drawing and the y coordinate to start drawing.
                     * We're using our Resources helpers to refer to our images
                     * so that we get the benefits of caching these images, since
                     * we're using them over and over.
                     */
                     ctx.drawImage(resources.get(rowImages[row]), col * 101, row * 83);
                }
              }
    
    
            renderEntities();
        }  //END RENDER
    

    resources is a separate file resources.js that creates a cache for the images, the code follows in case this helps:

    (function() {
        var resourceCache = {};
        var loading = [];
        var readyCallbacks = [];
    
        /* This is the publicly accessible image loading function. It accepts
         * an array of strings pointing to image files or a string for a single
         * image. It will then call our private image loading function accordingly.
         */
        function load(urlOrArr) {
            if(urlOrArr instanceof Array) {
                /* If the developer passed in an array of images
                 * loop through each value and call our image
                 * loader on that image file
                 */
                urlOrArr.forEach(function(url) {
                    _load(url);
                });
            } else {
                /* The developer did not pass an array to this function,
                 * assume the value is a string and call our image loader
                 * directly.
                 */
                _load(urlOrArr);
            }
        }
    
        /* This is our private image loader function, it is
         * called by the public image loader function.
         */
        function _load(url) {
            if(resourceCache[url]) {
                /* If this URL has been previously loaded it will exist within
                 * our resourceCache array. Just return that image rather than
                 * re-loading the image.
                 */
                return resourceCache[url];
            } else {
                /* This URL has not been previously loaded and is not present
                 * within our cache; we'll need to load this image.
                 */
                var img = new Image();
                img.src = url;
                img.onload = function() {
                    /* Once our image has properly loaded, add it to our cache
                     * so that we can simply return this image if the developer
                     * attempts to load this file in the future.
                     */
                    resourceCache[url] = img;
    
                    /* Once the image is actually loaded and properly cached,
                     * call all of the onReady() callbacks we have defined.
                     */
                    if(isReady()) {
                        readyCallbacks.forEach(function(func) { func(); });
                    }
                };
    
                /* Set the initial cache value to false, this will change when
                 * the image's onload event handler is called. Finally, point
                 * the images src attribute to the passed in URL.
                 */
                resourceCache[url] = false;
    
            }
        }
    
          function get(url) {
            return resourceCache[url];
        }
    
        /* This function determines if all of the images that have been requested
         * for loading have in fact been completly loaded.
         */
        function isReady() {
            var ready = true;
            for(var k in resourceCache) {
                if(resourceCache.hasOwnProperty(k) &&
                   !resourceCache[k]) {
                    ready = false;
                }
            }
            return ready;
        }
    
        /* This function will add a function to the callback stack that is called
         * when all requested images are properly loaded.
         */
        function onReady(func) {
            readyCallbacks.push(func);
        }
    
        /* This object defines the publicly accessible functions available to
         * developers by creating a global Resources object.
         */
        window.resources = {
            load: load,
            get: get,
            onReady: onReady,
            isReady: isReady
        };
    })();
    

    Chrome also listed two other sections under the same error:

    var main = function () {
        var now = Date.now();
        var delta = now - then;
    
        update(delta / 1000);
        render();
    
        then = now;
    
        //Request to do this again ASAP
        requestAnimationFrame(main);
    }
    

    The error is on the render call render();

    and on the last line of my file which calls main() as follows:

    // Let's play this game!
    var then = Date.now();
    reset();
    main();
    
    • Kaiido
      Kaiido over 8 years
      you are never loading your images, hence resources.get(rowImages[row]) can't return anything, hence drawImage complains.
    • Janice
      Janice over 8 years
      I move some code around and loaded the images first so that helped. Thanks
  • Janice
    Janice over 8 years
    okay do you know of a resource that shows the syntax for doing this asynchronously? Thanks
  • AndreaBogazzi
    AndreaBogazzi over 8 years
    did you try to adapt my pseudo code? i wrote it reading the library 'resources' you posted.
  • Janice
    Janice over 8 years
    yes AndreaBogazzi I have a similar implementation now, not exactly in that order but the weird thing is I can see all my images in Internet explorer but in chrome I only see the background images weird right. I always thought Chrome is the superior browser. The error I'm getting now is: Uncaught ReferenceError: enemy is not defined Here is a code snippet from the area where I defined enemy.
  • Janice
    Janice over 8 years
    var Eimage = new Image(); Eimage.onload = function() { // Enemies our player must avoid var Enemy = function () { // Variables applied to each of our instances go here, // we've provided one for you to get started // The image/sprite for our enemies, this uses // a helper we've provided to easily load images //this.sprite = 'images/enemy-bug.png'; ctx.drawImage(Eimage, 0, 202); } var enemy = new Enemy(); ... } Eimage.src = "images/enemy-bug.png";
  • AndreaBogazzi
    AndreaBogazzi over 8 years
    Please post a new question with more details if you need help. Make a fiddle if possibile. If this answer helped you to figure out what was the problem accept it, or post your own answer to this question
  • AndreaBogazzi
    AndreaBogazzi over 8 years
    You should click something near the 0 of my answer in the top left corner, a green check appears, if you do not find, nevermind, is just for not make other people erite answer for a solved problem.