JavaScript - définition de la propriété sur les objets en fonction de la charge de l'image, la propriété non définie une fois en dehors de la fonction

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

  •  04-10-2019
  •  | 
  •  

Question

Parfois, JavaScript ne me fait pas sens, considérez le code suivant qui génère une mosaïque de photos basée sur x / y tuiles. Je suis en train de définir une propriété .done true une fois chaque image mosaïque a été téléchargé, mais il est toujours faux pour une raison quelconque, ce que je fais mal?

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);
}

Maintenant, pour une raison quelconque la propriété .Done sur l'objet tile est toujours faux, même si elle est explicitement être définie sur true à l'intérieur tileImage.onload = function(). Et je peux vous assurer que cette fonction ne soit appelée parce que je l'ai placé un appel alert() à l'intérieur. En ce moment, il est juste coincé dans une boucle infinie appelant tryDisplayMosaic() constamment.

Aussi, si je place une boucle juste avant tryDisplayMosaic() est appelé, et dans cet ensemble I boucle .Done = true, alors la propriété .Done est vrai et alert('All images downloaded'); sera appelé.

Était-ce utile?

La solution

La variable « tuile » dans la boucle supérieure est partagée par chacun des fonctions « onload » vous créez; la même variable unique, autrement dit. Essayez ceci, de donner à chacun sa propre variable:

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

Pourquoi est-ce ainsi? Parce que contrairement à C ou Java, déclarant « tuile » dans la boucle comme cela ne pas en font scope au bloc de déclaration du corps de la boucle. La seule chose qui vous donne la portée en Javascript est un corps de la fonction.

Autres conseils

Ce que je vois est que vous créez des fermetures à l'intérieur des boucles, qui est une commune erreur . Pour éviter cela, vous pouvez créer une fermeture plus générale, en dehors de la boucle, qui gère tous les événements de charge et exécute une fonction après toutes les images sont chargées:

// 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;
    }
}

Ce code est totalement différent de votre code, mais il est beaucoup plus jolie à mon avis. Il faut que vous définissez le nombre d'images que vous devez charger et une fonction qui doit être exécutée après que toutes les images sont chargées.

Note: Je n'ai pas testé, je ne suis pas sûr de cette partie .call(this), peut-être le forum pourrait aider si elle est incorrecte

.

J'ai testé mon code et il fonctionne très bien. De plus, je me suis amélioré, alors vous enregistrez un pointeur sur l'image chargée à l'intérieur du tableau de tileData, consultez JSBin pour un exemple de travail.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top