简体   繁体   中英

drawImage on HTML5 canvas is not working

I'm creating a 'catch falling items' game, and have successfully drawn the 'catcher' image and the 'falling items' images onto the canvas. But I'd like to also show an image (x.png) when a falling image is not caught and hits the base of my html5 canvas. The generateX function is called from the animate function and when a falling image hits the base of the canvas. The sound.play() is working. The console.logs in the generateX function log the following - 2,3 | 1,3 | 1,3 - so I know that the image is not completely loaded the first time this function runs. Everything seems to be working EXCEPT for the actual image displaying. I tried to structure it in ways that other threads have suggested, but nothing is working. Any help is much appreciated!

generateX: function() {
    console.log('inside generateX func');
    var imgX = new Image();
    imgX.src = 'assets/x.png';
    function drawX() {
        console.log(3);
        counter+=20;
        context.drawImage(imgX, xIcon.x + counter, xIcon.y, xIcon.width, xIcon.height);
        xArr.push(imgX);
        console.log('xArr', xArr);
    }
    if (imgX.complete) {
        console.log(1);
        drawX();
    } else {
        console.log(2);
        imgX.onload = drawX;
    }
        helper.checkMisses();
}

animate: function() {
    var sound;
    if (continueAnimating) {
        requestAnimationFrame(helper.animate);
    }
    for (var i = 0; i < surveys.length; i++) {
        var survey = surveys[i];
        if (helper.isColliding(survey, dinoSaurus)) {
            sound = new Audio("audio/coinSound.mp3");
            sound.play();
            score += 5;
            helper.resetSurvey(survey);
        }
        survey.y += survey.speed;
        // if the survey is below the canvas,
        if (survey.y > canvas.height) {
            sound = new Audio("audio/buzzerSound.mp3");
            sound.play();
            helper.generateX();
            helper.resetSurvey(survey);
        }
    }
    // redraw everything
    helper.drawAll();
}

--

resetSurvey: function(survey) {
    // randomly position survey near the top of canvas
    survey.x = Math.random() * (canvas.width - surveyWidth);
    survey.y = 40 + Math.random() * 30;
    survey.speed = (0.55 + Math.random()) * 0.9;
}

Try loading your image earlier

If I understand your situation correctly, I think the issue is that you need to load the image earlier so that it is easily ready by the time you want to draw it, rather than waiting to load it only inside generateX . The way you are currently coding it, you are loading the image ( imgX.src = ... ) and then immediately trying to draw it. True, you are checking if the loading is complete and, if not, trying again shortly after. However, a better strategy is to load the image much earlier, eg during game initialization.

Demo

The code snippet below demonstrates the difference that this makes. It loads 2 different images. The only difference between the two is that one (on the left) is loaded ahead of time and the other (on the right) is only loaded inside generateX . The former image displays properly the 1st time through the game cycle while the latter image is missing the 1st time and only displays properly the 2nd time through.

 function loadLeftImageWellBeforeRunningGenerateX() { // correct timing imgs[0] = new Image(); imgs[0].src = 'http://placehold.it/200x100.png'; } function loadRightImageInsideGenerateX() { // incorrect timing imgs[1] = new Image(); imgs[1].src = 'http://placehold.it/100x50.png'; } function generateX() { loadRightImageInsideGenerateX(); log('--inside generateX func'); imgs.forEach(function(imgX, num) { // same as your original code, just done for 2 different images if (imgX.complete) { log("----image #" + num + " (" + side[num] + "); " + 1 + ", ie image complete"); drawX(imgX, num); } else { log("----image #" + num + " (" + side[num] + "); " + 2 + ", ie image not complete"); imgX.onload = drawX(imgX, num); } }); } function drawX(imgX, num) { // same as your original code, just allows placement of 2 images side-by-side log("----image #" + num + " (" + side[num] + "); " + 3 + ", ie drawing image (" + prefix[num] + "successfully)"); context.drawImage(imgX, num * 150 + 10, 10, 100, 50); if (num === 1) {prefix[1] = "";} } var imgs = [], numClicks = 0, side = ["left", "right"], prefix = ["", "un"], button = document.querySelector("button"), canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'); context.fillStyle = "yellow"; context.fillRect(0, 0, 300, 150); // as far as game logic goes, the only important lines below are marked with arrows // the rest is just "fluff" to make the demo more understandable to the user button.addEventListener("click", function() { numClicks += 1; if (numClicks === 1) { log("You must clear your browser's recent cache every time you run this snippet " + "in order for it to demonstrate the problems and solutions " + "associated with loading images in this code."); button.innerHTML = "Clear browser cache and click here again to start game initialization"; } else if (numClicks === 2) { loadLeftImageWellBeforeRunningGenerateX(); // <----- *** log("Game initializes. No images should yet be visible, " + "but image #0 (left) should be loading/loaded now, ie ahead of time. " + "Image #1 (right) is not yet loading/loaded. Click again to 'play game'."); button.innerHTML = "Do NOT clear the browser cache and click here again to start playing game"; } else { button.innerHTML = "Do NOT clear the browser cache and click here again to continue playing game"; if (numClicks === 3) { log("Game begins. Images are required for the first time. Only the pre-loaded left image shows " + "even though loading both is attempted."); } else { log("Game continues. On second and subsequent re-draws, both images are now available and visible."); } generateX(); // <----- *** } }); function log(msg) { document.body.appendChild(document.createElement("p")).innerHTML = msg; } 
 p { margin: 0.2em; } 
 <canvas id="myCanvas" width="300" height="80"></canvas> <div> <button>Click here to begin demo</button> </div> 

Your browser's cache can hide this bug

By the way, if and when you try to debug this kind of thing, be aware that many/most/all browsers cache images, making it potentially difficult to replicate this kind of bug if you're not aware of what's going on. For example, you might run your buggy code and the image is missing the first time through the game cycle. You study your code, make a change to try to fix the bug and run the code a second time. Voila, it looks like the problem is fixed because the image now appears the first time through the game cycle. However, it may, in fact, only be showing up in that first cycle not because the bug is fixed but because the browser is able to use the cached image even in the first game cycle, eliminating the need to load it from its source. In short, if you're trying to debug an issue like this where you suspect the timing of file loading is important but the bug only appears sometimes, try clearing your browser's recent cache to see if that makes the bug more apparent.

To clear a browser's cache:

  • Firefox (for Mac or Windows, v44...): click "History" / click "Clear Recent History..." / click to open "Details" if necessary / check "Cache" / click "Clear now"
  • Chrome (for Mac, v48...) "Chrome" (left menu item) / "Clear Browsing Data..." / select "Cached images and files" / click "Clear browsing data"
  • Chrome (for Windows, v48...) click customize-and-control icon (at the right) / click "More tools..." / click "Clear browsing data..." / click "Clear browsing data"
  • Internet Explorer (for Windows, v11...) click "Tools" / click "Delete browsing history" / select "Download history" / click "Delete"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM