JavaScript-画像ロード関数でオブジェクトにプロパティを設定し、プロパティが外部関数の外側に設定されていない

StackOverflow https://stackoverflow.com/questions/2798103

  •  04-10-2019
  •  | 
  •  

質問

JavaScriptが私には意味がない場合があります。X/Yタイルに基づいて写真モザイクを生成する次のコードを検討してください。各モザイク画像がダウンロードされたら、.DONEプロパティをTrueに設定しようとしていますが、何らかの理由で常に間違っています。何が間違っていますか?

var tileData = [];

function generate()
{
    var image = new Image();
    image.onload = function()
    {
        // Build up the 'tileData' array with tile objects from this Image

        for (var i = 0; i < tileData.length; i++)
        {
            var tile = tileData[i];

            var tileImage = new Image();
            tileImage.onload = function()
            {
                // Do something with this tile Image
                tile.Done = true;
            };
            tileImage.src = tile.ImageUrl;
        }
    };
    image.src = 'Penguins.jpg';

    tryDisplayMosaic();
}

function tryDisplayMosaic()
{
    setTimeout(function()
    {
        for (var i = 0; i < tileData.length; i++)
        {
            var tile = tileData[i];

            if (!tile.Done)
            {
                tryDisplayMosaic();
                return;
            }
        }

        // If we get here then all the tiles have been downloaded
        alert('All images downloaded');
    }, 2000);
}

今、何らかの理由で .Done 上のプロパティ tile オブジェクトは、明示的に内部に設定されているにもかかわらず、常に偽です tileImage.onload = function(). 。そして、私が置いたので、この関数が呼び出されることをあなたに保証することができます alert() その中に電話してください。今のところ、それは無限のループ呼び出しの中に閉じ込められています tryDisplayMosaic() 常に。

また、すぐにループを配置する場合 tryDisplayMosaic() 呼び出され、そのループで設定します .Done = true, 、 それから .Done プロパティは真実です alert('All images downloaded'); 呼び出されます。

役に立ちましたか?

解決

上部ループの変数「タイル」は、作成した「オンロード」関数のすべてによって共有されます。言い換えれば、まったく同じ単一変数です。これを試して、それぞれに独自の変数を与える:

tileImage.onload = (function(myTile) {
  return function()
        {
            // Do something with this tile Image
            myTile.Done = true;
        };
})(tile);

なぜそうなの? CやJavaとは異なり、そのようなループ内で「タイル」を宣言する いいえ ループ本体のステートメントブロックにスコープします。 JavaScriptでスコープを与える唯一のものは関数本文です。

他のヒント

私が見ているのは、あなたがループ内で閉鎖を作成することです。 よくある間違い. 。これを回避するために、すべてのロードイベントを処理し、すべての画像がロードされた後に関数を実行するループの外側で、より一般的な閉鎖を作成できます。

// This array must be filled somewhere...
var tileData = [];

var ImageLoader = function(){
    var numOfImages = 0;
    var numLoaded   = 0;
    var callBack    = function(){};

    function imageLoaded(){
        numLoaded++;
        if(numLoaded===numOfImages){
            // All images are loaded, now call the callback function
            callBack.call(this);
        }
    }

    function init(numberOfImages, fn){
        numOfImages = numberOfImages;
        callBack = fn;
    }

    return {
        imageLoaded: imageLoaded,
        init: init
    };
}();

function tryDisplayMosaic(){
    alert('All images downloaded');
}

function generate(){
    // (1) Set the number of images that are to be loaded and (all tiles + penguin)
    // (2) Set the function that must be called after all images are loaded
    ImageLoader.init(tileData.length+1, tryDisplayMosaic);

    // Load this Penguins image
    var image = new Image();
    image.src = 'Penguins.jpg';
    image.onload = ImageLoader.imageLoaded;

    // Go through each image and load it
    for (var i=0; i<tileData.length; i++) {
        image = new Image();
        image.src = tileData[i].ImageUrl;
        image.onload = ImageLoader.imageLoaded;
    }
}

このコードはあなたのコードとはまったく異なりますが、私の意見ではかなりきれいです。ロードする必要がある画像の数と、すべての画像がロードされた後に実行する必要がある関数を設定する必要があります。

注:私はそれをテストしていません、私はこれについてよくわかりません .call(this) パート、おそらくフォーラムが間違っている場合に役立つ可能性があります。

コードをテストしましたが、正常に動作します。さらに改善したので、内部のロードされた画像へのポインタを保存します tileData アレイ、参照 jsbin 実用的な例。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top