简体   繁体   English

如何解决HTML5 Canvas竞赛条件?

[英]How do I solve HTML5 Canvas Race Conditions?

I have discovered some race conditions with drawing an HTML5 canvas programmatically in JavaScript . 我已经发现了一些使用JavaScript编程绘制HTML5 canvas竞赛条件。 These occur when trying to modify an image before it is loaded, and I have been able to fix most of them using the technique: 在尝试在加载图像之前修改图像时会发生这些情况,我已经能够使用以下技术修复其中的大多数问题:

var myImage = document.createElement('img');
myImage.onload = function(){
    console.log("image src set!");
};
myImage.src="img/foobar.png";

I am doing this operation for one of several images, and am having a strange thing happen. 我正在为几个图像之一执行此操作,并且发生了一件奇怪的事情。 Basically, the image is drawing to the canvas before it is loaded. 基本上,图像是在加载之前绘制到画布上的。 I even tried to hack-around the issue by using a boolean value to specify if the image has been drawn yet. 我什至试图通过使用布尔值指定是否已绘制图像来解决问题。 Here is my code with the output. 这是我的输出代码。 To understand the context, this code is part of a function that is called every second to update the canvas. 为了理解上下文,此代码是函数的一部分,该函数每秒被调用以更新画布。

if (!at_arrow_black)//one of two images used to show an arrow on the canvas
{
    at_arrow_black = document.createElement('img');
    at_arrow_black.onload = function() {
        if (foregroundColor === "black")//which image to draw depends on the foreground color
        {
            console.log("black load");
            context.drawImage(at_arrow_black, canvas.width*.775, canvas.height*.66, canvas.width*.075, font*4/5);
        }
    };
    at_arrow_black.src = "img/at_arrow.png";
}
else
{
    if (foregroundColor === "black")
    {

        if (hasDrawnArrow)//this starts as false
        {
            console.log("1. black draw");
            context.drawImage(at_arrow_black, canvas.width*.775, canvas.height*.66, canvas.width*.075, font*4/5);
        }
        else
        {
            logDebug("2. black draw");
            hasDrawnArrow = true;
        }
    }
}

This results in the canvas first drawing one arrow, then the other, at the first iteration of this loop (and in slightly different places). 这导致画布在此循环的第一次迭代中(在稍有不同的位置)首先绘制一个箭头,然后绘制另一个箭头。 The output I get: 我得到的输出:

2. black draw
black load
1. black draw

This is the expected output - but why does the canvas draw the image anyways? 这是预期的输出-但是为什么画布仍然会绘制图像? Is this a race condition of some sort? 这是某种竞赛条件吗? What can I do to fix it? 我该如何解决?

If you're loading more than 1 image, you will probably want to build yourself an image loader so all images are loaded before you start working with them. 如果要加载多个图像,则可能需要构建一个图像加载器,以便在开始使用它们之前加载所有图像。

Here is an example: 这是一个例子:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

  var canvas1=document.getElementById("canvas1");
  var ctx1=canvas1.getContext("2d");
  var canvas2=document.getElementById("canvas2");
  var ctx2=canvas2.getContext("2d");

  var imageURLs=[];
  var imagesOK=0;
  var imagesFailed=0;
  var imgs=[];
  imageURLs.push("http://upload.wikimedia.org/wikipedia/commons/d/d4/New_York_City_at_night_HDR_edit1.jpg");
  imageURLs.push("http://www.freebestwallpapers.info/bulkupload//20082010//Places/future-city.jpg");
  loadAllImages();

  function loadAllImages(){
      for (var i = 0; i < imageURLs.length; i++) {
        var img = new Image();
        imgs.push(img);
        img.onload = onLoad; 
        img.onerror = onFail;
        img.src = imageURLs[i];
      }      
  }

  var imagesAllLoaded = function() {
    if (imagesOK+imagesFailed==imageURLs.length ) {
       // all images are processed
       // ready to use loaded images
       // ready to handle failed image loads
       ctx1.drawImage(imgs[0],0,0,canvas1.width,canvas1.height);
       ctx2.drawImage(imgs[1],0,0,canvas2.width,canvas2.height);

    }
  };

  function onLoad() {
    imagesOK++;
    imagesAllLoaded();
  }

  function onFail() {
    // possibly log which images failed to load
    imagesFailed++;
    imagesAllLoaded();
  };   


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas1" width=300 height=300></canvas><br/>
    <canvas id="canvas2" width=300 height=300></canvas>
</body>
</html>

Here's the JSFiddle . 这是JSFiddle

I know there is already an accepted answer, but here is an alternative anyway. 我知道已经有一个可以接受的答案,但是无论如何这是一个替代方案。

You need to wait until all your images are loaded. 您需要等待,直到所有图像都加载完毕。 I use an image loader that sets a flag when all images are loaded so my script can then continue. 我使用图像加载器在加载所有图像时设置标志,以便我的脚本可以继续。

Image Loader 图像加载器

function ImageLoader(sources, callback) 
{
    var images = {};
    var loadedImages = 0;
    var numImages = 0;

    // get num of sources
    for (var src in sources) {
        numImages++;
    }

    for (var src in sources) {
        images[src] = new Image();
        images[src].onload = function() {

            if (++loadedImages >= numImages) {
                callback(images);
            }
        };
        images[src].src = sources[src];
    }
}

Usage 用法

    // *** The images you want to laod
    var sources = {
        background: 'images/bg.png',
        assets: 'images/assets.png',
    };

   // *** What you want to happen once all images have loaded...      
   ImageLoader(sources, function(images) {   

        // *** Example of what I usually do
        _images = images;
        _setImagesReady = true;
    });

    // *** Access the image by specifying it's property name you defined. eg:
    ctx.drawImage(_image.background, 0, 0);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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