JavaScript-画像ロード関数でオブジェクトにプロパティを設定し、プロパティが外部関数の外側に設定されていない
-
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 実用的な例。