使用JavaScript/jQuery预加载图片的最佳方法是什么?

50

我完全知道这个问题已经在各处被问答过了,无论是在SO上还是其他地方。但每次似乎都有不同的答案,例如 这个这个那个

我不在意是否使用jQuery - 重要的是它能够工作,并且是跨浏览器的。

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


1
预加载图片已经过时了,现在流行的是Sprites。http://www.alistapart.com/articles/sprites - Chris
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];
  }
}

以下翻译内容改编自如何在JavaScript中预加载多张图片的最佳方法?

使用new Image()不会涉及使用DOM方法的开销,但是指定图像的新请求将被添加到队列中。此时,由于该图像实际上未添加到页面中,因此没有涉及重新渲染。然而,我建议将其添加到页面末尾(当可能时,所有脚本都应该放在末尾),以防止它阻止更关键的元素。

编辑:根据评论的正确指出,需要使用单独的Image对象才能正常工作。感谢您的指正,并对我没有仔细检查表示歉意。

编辑2:为了更明显地表明可重复使用性,进行了编辑。

编辑3(三年后):

由于浏览器处理非可见图片的方式发生了变化(display: none或者从未添加到文档中,如本答案所示),现在推荐使用一种新的预加载方法。例如,您可以使用Ajax请求来强制提前检索图像。使用jQuery:

jQuery.get(source);

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

function preload(sources)
{
  jQuery.each(sources, function(i,source) { jQuery.get(source); });
}
请注意,这不适用于精灵图这种情况,因为它们已经很好了。这只适用于像照片库或带有图片的滑块/轮播等内容的情况,其中图片由于最初不可见而未加载。
还要注意,此方法不适用于IE(ajax通常不用于检索图像数据)。

4
+1写了我即将提交的所有内容。比我快25秒。 :-) - Jesse Dearing
1
谢谢你的点赞,很高兴在发布之前没有试图让语法完美 :) - Jonathan Fingland
6
由于跨域资源共享(CORS)的限制,您不能使用jQuery.get获取另一台服务器上的图像。 - Haralan Dobrev
1
这些图片在DOM中吗?它们会导致内存使用,还是只是静静地坐在缓存中等待被提取出来? - Matthew Dolman
这种预加载方法是否可以执行多部分请求?我有大约100张图片,不想做100个请求来预加载所有图片。 - mmarques
显示剩余7条评论

14

精灵图

正如其他人所提到的,精灵图在许多方面都非常有效,但它并没有被描述得那么好。

  • 优点是,您仅需为图像发出一次HTTP请求。但可能会有所不同。
  • 缺点是您需要在一个HTTP请求中加载所有内容。由于大多数当前浏览器仅限于2个并发连接,因此图像请求可能会阻塞其他请求。因此,某些内容,例如菜单背景,可能要等待一段时间才能呈现。
  • 多个图像共享相同的调色板,因此可以节省一些空间,但并非总是如此,即使如此,也几乎可以忽略不计。
  • 由于图像之间存在更多共享数据,因此压缩效果得到了改善。

然而处理不规则形状的图像比较棘手。将所有新图像合并成一个新图像还是有点麻烦。

使用<img>标签的低侵入性方法

如果您正在寻找最可靠的解决方案,则应选择我仍然更喜欢的低侵入性方法。在文档末尾创建指向图片的<img>链接,并将其widthheight设置为1x1像素,并将它们放在隐藏的div中。如果它们在页面底部,它们将在其他内容之后加载。


我以前从未想过“低音爵士方法”……好主意,而且如此简单! - Dave Everitt
这并不是很“美观”,但使用低阶方法是跨浏览器和设备使其正常工作的最一致的方式。需要注意的一点是,在火狐浏览器上,对于包装器div,使用visibility: hidden比display: none效果更好,只需根据需要处理间距即可。 - Andrew Bartel

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开发者工具打开时,默认情况下会禁用缓存,请牢记这一点。

对于那些害怕添加新插件的人 - 我很惊喜地发现它可以通过npm轻松获得。我花了5分钟把上面的解决方案放到了位,它非常简单。其他解决方案对我也不起作用(即使在chrome中)。 - aaaaaa

3

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


1
这是很久以前的内容,所以我不知道还有多少人对预加载图像感兴趣。
我的解决方案甚至更加简单。
我只使用了CSS。
#hidden_preload {
    height: 1px;
    left: -20000px;
    position: absolute;
    top: -20000px;
    width: 1px;
}

0

以下是我的简单解决方案,图像加载后淡入效果。

    function preloadImage(_imgUrl, _container){
        var image = new Image();
            image.src = _imgUrl;
            image.onload = function(){
                $(_container).fadeTo(500, 1);
            };
        }

0

针对我的使用情况,我有一个带有全屏图像的轮播图,我想要预加载它们。然而,由于这些图像按顺序显示,并且每个图像可能需要几秒钟才能加载完成,因此按顺序、连续地加载它们非常重要。

为此,我使用了async库的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。



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