我可以为多个图片的onload调用同步吗?

9
我希望当特定图片加载时能运行一个函数,但我不知道如何等待两个图片都加载完毕再运行函数。我只知道如何连接它们,像下面这样:
Image1 = new Image();
Image1.src = 'image1-link.jpg';

Image2 = new Image();
Image2.src = 'image2-link.jpg';

Image1.onload = function() {
    Image2.onload = function() { ... }
}

缺点是必须等待Image1完全加载后再获取第二个图像。我想尝试这样做:
Image1 = new Image();
Image1.src = 'image1-link.jpg';

Image2 = new Image();
Image2.src = 'image2-link.jpg';

(Image1 && Image2).onload = function() { ... }

编辑:非常感谢Paul Grime和ziesemer的帮助。你们的回答都很好。我认为最佳答案应该给Paul Grime,虽然他的代码更多,但我只需要知道图片的src,而onload函数是内置的。而在ziesemer的回答中,我需要知道src、图片数量,并且需要编写多个onload行。

根据情况不同,两种方法都有其用途。再次感谢你们的帮助。


是的,jQuery 可用。 - Ark-of-Ice
4个回答

11
如果你不能使用类似jQuery的东西,可以创建一个辅助函数,将其传递给你感兴趣的所有图片的“onload”事件。每次调用它时,都会增加计数器或将“src”名称添加到集合中。一旦计数器或集合大小达到您所期望的所有图像的大小,则可以执行您真正想要做的任何工作。
例如:
var imageCollector = function(expectedCount, completeFn){
  var receivedCount;
  return function(){
    if(++receivedCount == expectedCount){
      completeFn();
    }
  };
}();

var ic = imageCollector(2, function(){alert("Done!");});
Image1.onload = ic;
Image2.onload = ic;

11

这个代码片段或许能帮到你。它是一个简单的通用“加载器”。

http://jsfiddle.net/8baGb/1/

以下是代码:

// loader will 'load' items by calling thingToDo for each item,
// before calling allDone when all the things to do have been done.
function loader(items, thingToDo, allDone) {
    if (!items) {
        // nothing to do.
        return;
    }

    if ("undefined" === items.length) {
        // convert single item to array.
        items = [items];
    }

    var count = items.length;

    // this callback counts down the things to do.
    var thingToDoCompleted = function (items, i) {
        count--;
        if (0 == count) {
            allDone(items);
        }
    };

    for (var i = 0; i < items.length; i++) {
        // 'do' each thing, and await callback.
        thingToDo(items, i, thingToDoCompleted);
    }
}

function loadImage(items, i, onComplete) {
    var onLoad = function (e) {
        e.target.removeEventListener("load", onLoad);

        // this next line can be removed.
        // only here to prove the image was loaded.
        document.body.appendChild(e.target);

        // notify that we're done.
        onComplete(items, i);
    }
    var img = new Image();
    img.addEventListener("load", onLoad, false);
    img.src = items[i];
}

var items = ['http://bits.wikimedia.org/images/wikimedia-button.png',
             'http://bits.wikimedia.org/skins-1.18/common/images/poweredby_mediawiki_88x31.png',
             'http://upload.wikimedia.org/wikipedia/en/thumb/4/4a/Commons-logo.svg/30px-Commons-logo.svg.png',
             'http://upload.wikimedia.org/wikipedia/commons/3/38/Icons_example.png'];

loader(items, loadImage, function () {
    alert("done");
});

1

在浏览现有答案时,我惊讶地发现没有人实现基于promise的解决方案,而在我看来,这是最优雅和适合解决此类问题的方法,因此这里提供一个:

// Example array of images
const imageUrls = [
  "https://en.wikipedia.org/static/images/project-logos/enwiki.png",
  "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
  "https://upload.wikimedia.org/wikipedia/en/thumb/4/4a/Commons-logo.svg/30px-Commons-logo.svg.png",
];

// imageUrls.map returns Promise array (because callback function is async), 
// which is immediately passed to Promise.all which accumulates them into one
// promise which will be resolved when all its elements will finish.
Promise.all(
  imageUrls.map(async (imageUrls, i) => {
    const tmpImage = new Image();
    
    // Create synchronization promise
    const syncPromise = new Promise((resolve) => {
    
      tmpImage.onload = (e) => {
        // Handle loaded image
        console.log("Finished loading image: ", i);
        
        // Don't forget to resolve syncPromise
        resolve(); 
      };
      
      tmpImage.src = imageUrls;
    });
    
    // Wait for syncPromise (image loaded)
    await syncPromise;
  })
).then(() => {
  console.log("Finished loading all images");
});


1
我使用 Paul Grime 上面的例子并对其进行了修改,以使其更易于理解,同时仍能满足 OP 的需求。我认为此处的其他答案要么过于复杂,要么不够。希望这段代码更容易上手和应用。我在 each 循环中使用了 underscore,但这很容易更改。
此外,我现在改进了解决方案,使其具有用于 img 错误的回调。如果图像已加载,则将其添加到数组中,否则不会添加。我还将其更改为利用 jquery。
var loadedImages = [],
    imagePaths = [{"src": "myimg.png"}, {"src": "myotherimg.png"}];

loadImages(imagePaths, loadedImages, function(loadedImages) {
    console.log(loadedImages)
});

function loadImages(imagePaths, loadedImages, callback) {
    if (!imagePaths) { return; }

    var count = imagePaths.length;

    _.each(imagePaths, function(data, key, list) {
        var onLoad = function (e) {
            count--;
            if (0 == count) {
                callback(loadedImages);
            }
        }

        $(document.createElement("img"))
            .on('load', function() {  
                loadedImages.push(data);
                onLoad();
            })
            .on('error', function() {  onLoad(); })
            .attr("src", data["src"]);
    });
},

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接