JavaScript - Impostare proprietà su oggetti in funzione del carico Immagine, proprietà non di una volta la funzione di fuori

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

  •  04-10-2019
  •  | 
  •  

Domanda

A volte JavaScript non ha senso per me, si consideri il seguente codice che genera un mosaico di foto sulla base di x / y piastrelle. Sto cercando di impostare una proprietà .done true una volta ogni immagine mosaico è stato scaricato, ma è sempre falsa per qualche ragione, che cosa sto sbagliando?

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

Ora, per qualche motivo la proprietà .Done sull'oggetto tile è sempre false, anche se è in modo esplicito viene impostata su true tileImage.onload = function() all'interno. E posso assicurarvi che questa funzione venga chiamato perché ho messo una chiamata alert() al suo interno. In questo momento è solo bloccato all'interno di un ciclo infinito chiamando tryDisplayMosaic() costantemente.

Anche se ho posto un ciclo appena prima tryDisplayMosaic() si chiama, e in quel set .Done = true ciclo I, quindi proprietà .Done è vero e alert('All images downloaded'); verrà chiamato.

È stato utile?

Soluzione

La variabile "piastrella" nel ciclo superiore è condiviso da tutti una delle funzioni "onload" create; la stessa variabile singola, in altre parole. Prova questo, per dare a ciascuno la propria variabile:

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

Perché è così? Perché a differenza di C o Java, dichiarando "tile" all'interno del ciclo del genere non rendono ambito al blocco di istruzioni del corpo del ciclo. L'unica cosa che ti dà ambito in Javascript è un corpo della funzione.

Altri suggerimenti

Quello che vedo è che si crea all'interno di chiusure loop, che è un comune errore . Per evitare questo, si potrebbe creare una chiusura più generale, al di fuori del ciclo, che gestisce tutti gli eventi di carico ed esegue una funzione, dopo tutte le immagini vengono caricate:

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

Questo codice è totalmente diverso dal codice, ma è una più carina molto a mio parere. Si richiede di impostare il numero di immagini che è necessario caricare e una funzione che deve essere eseguita dopo tutte le immagini sono caricate.

Nota: non l'ho provato, io non sono sicuro di questa parte .call(this), forse il forum potrebbe aiutare se non è corretto

.

Ho testato il mio codice e funziona benissimo. Inoltre ho migliorato, in modo da risparmiare un puntatore per l'immagine caricata all'interno della matrice tileData, vedi JSBin per un esempio di lavoro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top