50

我完全意识到这个问题已经被问到并在任何地方得到了回答,无论是在 SO 还是在关闭。但是,每次似乎都有不同的答案,例如thisthisthat

我不在乎它是否使用 jQuery - 重要的是它可以工作,并且是跨浏览器的。]

那么,预加载图像的最佳方法是什么?

4

8 回答 8

66

不幸的是,这取决于你的目的。如果您打算将图像用于样式目的,那么最好的选择是使用精灵。 http://www.alistapart.com/articles/sprites2

但是,如果您打算在 <img> 标签中使用图像,那么您需要预先加载它们

function preload(sources)
{
  var images = [];
  for (i = 0, length = sources.length; i < length; ++i) {
    images[i] = new Image();
    images[i].src = sources[i];
  }
}

(修改后的源代码取自What is the best way to preload multiple images in JavaScript?

usingnew Image()不涉及使用 DOM 方法的费用,但对指定图像的新请求将被添加到队列中。由于图像此时并未实际添加到页面中,因此不涉及重新渲染。但是,我建议将其添加到页面的末尾(如果可能,所有脚本都应该如此)以防止它包含更多关键元素。

编辑:编辑以正确反映评论,指出需要单独Image的对象才能正常工作。谢谢,我很遗憾没有更仔细地检查它。

Edit2:编辑使可重用性更加明显

编辑3(3年后):

由于浏览器处理不可见图像的方式发生了变化(显示:无,或者,如本答案所示,从未附加到文档中),因此首选一种新的预加载方法。

您可以使用 Ajax 请求来强制提前检索图像。使用 jQuery,例如:

jQuery.get(source);

或者在我们之前的例子中,你可以这样做:

function preload(sources)
{
  jQuery.each(sources, function(i,source) { jQuery.get(source); });
}

请注意,这不适用于按原样正常的精灵的情况。这仅适用于照片画廊或带有图像的滑块/轮播等图像未加载的内容,因为它们最初不可见。

另请注意,此方法不适用于 IE(通常不使用 ajax 来检索图像数据)。

于 2009-05-23T15:12:08.647 回答
14

雪碧

正如其他人所提到的,由于各种原因,精灵效果很好,但是,它并没有想象中的那么好。

  • 从好的方面来说,您最终只为您的图像发出了一个 HTTP 请求。虽然是 YMMV。
  • 不利的一面是,您在一个 HTTP 请求中加载所有内容。由于大多数当前浏览器限制为 2 个并发连接,因此图像请求可能会阻止其他请求。因此 YMMV 和类似菜单背景的东西可能暂时不会渲染。
  • 多个图像共享相同的调色板,因此可以节省一些费用,但情况并非总是如此,即使这样也可以忽略不计。
  • 压缩得到了改进,因为图像之间有更多的共享数据。

不过,处理不规则形状很棘手。将所有新图像合并到新图像中是另一种烦恼。

使用 <img> 标签的低插孔方法

如果您正在寻找最明确的解决方案,那么您应该采用我仍然喜欢的低插孔方法。在文档末尾创建指向图像的 <img> 链接,并将 and 设置widthheight1x1 像素,另外将它们放在隐藏的 div 中。如果它们位于页面末尾,它们将在其他内容之后加载。

于 2009-05-23T23:19:32.170 回答
6

截至 2013 年 1 月,此处描述的所有方法都不适用于我,所以这里是替代方法,测试并使用 Chrome 25 和 Firefox 18。使用 jQuery 和这个插件来解决加载事件的怪癖:

function preload(sources, callback) {
    if(sources.length) {
        var preloaderDiv = $('<div style="display: none;"></div>').prependTo(document.body);

        $.each(sources, function(i,source) {
            $("<img/>").attr("src", source).appendTo(preloaderDiv);

            if(i == (sources.length-1)) {
                $(preloaderDiv).imagesLoaded(function() {
                    $(this).remove();
                    if(callback) callback();
                });
            }
        });
    } else {
        if(callback) callback();
    }
}

用法:

preload(['/img/a.png', '/img/b.png', '/img/c.png'], function() { 
    console.log("done"); 
});

请注意,如果缓存被禁用,您将得到不同的结果,当开发者工具打开时,默认情况下在 Chrome 上是这样,所以请记住这一点。

于 2013-01-26T15:10:31.943 回答
3

在我看来,使用一些库引入的 Multipart XMLHttpRequest 将是未来几年的首选解决方案。但是 IE < v8,仍然不支持 data:uri(即使 IE8 支持有限,最多允许 32kb)。这是并行图像预加载的实现 - http://code.google.com/p/core-framework/wiki/ImagePreloading,它捆绑在框架中,但仍然值得一看。

于 2011-04-30T07:23:49.390 回答
1

这是很久以前的,所以我不知道还有多少人对预加载图像感兴趣。

我的解决方案更加简单。

我只是使用CSS。

#hidden_preload {
    height: 1px;
    left: -20000px;
    position: absolute;
    top: -20000px;
    width: 1px;
}
于 2015-04-12T20:45:57.477 回答
0

这是我的简单解决方案,加载后图像淡入。

    function preloadImage(_imgUrl, _container){
        var image = new Image();
            image.src = _imgUrl;
            image.onload = function(){
                $(_container).fadeTo(500, 1);
            };
        }
于 2013-01-29T02:38:47.947 回答
0

对于我的用例,我有一个带有全屏图像的轮播,我想预加载它。但是,由于图像按顺序显示,并且每个加载可能需要几秒钟,因此按顺序加载它们很重要。

为此,我使用了异步库的waterfall()方法(https://github.com/caolan/async#waterfall

        // Preload all images in the carousel in order.
        image_preload_array = [];
        $('div.carousel-image').each(function(){
            var url = $(this).data('image-url');
            image_preload_array.push(function(callback) {
                var $img = $('<img/>')
                $img.load(function() {
                    callback(null);
                })[0].src = url;
            });
        });
        async.waterfall(image_preload_array);

这是通过创建一个函数数组来工作的,每个函数都被传递了callback()它需要执行的参数,以便调用数组中的下一个函数。的第一个参数callback()是一个错误信息,如果提供了非空值,它将退出序列,所以我们每次都传递null。

于 2014-09-23T02:56:54.963 回答
-2

看到这个:

http://www.mattfarina.com/2007/02/01/preloading_images_with_jquery

关于 SO 的相关问题:

jquery隐藏预加载

于 2009-05-23T15:07:28.690 回答