使用JavaScript预加载图像


263

我在下面编写的函数是否足以在当今大多数(如果不是全部)浏览器中预加载图像?

function preloadImage(url)
{
    var img=new Image();
    img.src=url;
}

我有一个图像URL数组,我遍历并preloadImage为每个URL 调用该函数。


19
请注意,某些(全部?)浏览器会在几秒钟后释放图像(如果您未使用过的话)。为避免这种情况,请保留对img对象的引用,例如在父作用域的数组中。
塔林2014年

3
“释放图像”是什么意思?如果它是由浏览器缓存的,它将留在那里,对吗?
Francisc

56
短一点:(new Image()).src = url;
mrzmyr 2014年

20
请注意,当chrome devtool打开并且在网络面板中启用了“ disable chache”时,这将不起作用
Wayou,2007年

6
甚至更短:new Image().src = url;
Daniel X Moore

Answers:


163

是。这应该适用于所有主要的浏览器。


48
主持人注:请停止将其标记为“不是答案”。实际上,这是对所提问题的直接答案。如果您认为答案是错误的,或没有足够的证据支持,则请否决它。
科迪·格雷

43

试试这个,我认为这样更好。

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

//-- usage --//
preload(
    "http://domain.tld/gallery/image-001.jpg",
    "http://domain.tld/gallery/image-002.jpg",
    "http://domain.tld/gallery/image-003.jpg"
)

来源:http//perishablepress.com/3-ways-preload-images-css-javascript-ajax/


3
没有onload任何图像的处理程序
Benny

12
您能解释一下为什么这样更好吗?
JJJ

2
@BeNice我认为您误会了,加载图像是异步的,因此您必须处理onload状态,或者只是实例化内存中的图像。
本尼

3
如果使用此解决方案,请不要忘记i变量的var语句。否则,将在全局设置它,这可能会导致很难解决的错误(除非您知道您忘记了var语句for (var i = 0.....
。– Mohammer

2
@Mohammer对此表示感谢,我只是从提供的链接中复制了代码。我现在将编辑代码以添加var
clintgh

26

就我而言,将回调添加到onload事件的函数中非常有用:

function preloadImage(url, callback)
{
    var img=new Image();
    img.src=url;
    img.onload = callback;
}

然后包装它,以完成要通过回调预加载的图像的URL数组的情况:https : //jsfiddle.net/4r0Luoy7/

function preloadImages(urls, allImagesLoadedCallback){
    var loadedCounter = 0;
  var toBeLoadedNumber = urls.length;
  urls.forEach(function(url){
    preloadImage(url, function(){
        loadedCounter++;
            console.log('Number of loaded images: ' + loadedCounter);
      if(loadedCounter == toBeLoadedNumber){
        allImagesLoadedCallback();
      }
    });
  });
  function preloadImage(url, anImageLoadedCallback){
      var img = new Image();
      img.onload = anImageLoadedCallback;
      img.src = url;
  }
}

// Let's call it:
preloadImages([
    '//upload.wikimedia.org/wikipedia/commons/d/da/Internet2.jpg',
  '//www.csee.umbc.edu/wp-content/uploads/2011/08/www.jpg'
], function(){
    console.log('All images were loaded');
});

19

CSS2替代:http//www.thecssninja.com/css/even-better-image-preloading-with-css2

body:after {
  content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
  display: none; 
}

CSS3替代:https//perishablepress.com/preload-images-css3/ (H / T Linh大坝)

.preload-images {
  display: none; 
  width: 0;
  height: 0;
  background: url(img01.jpg),
              url(img02.jpg),
              url(img03.jpg);
}

注意:容器中的图像display:none可能不会预加载。也许能见度:隐藏会更好,但我尚未对此进行测试。感谢Marco Del Valle指出这一点


谢谢mplungjan。尽管这对我的情况没有帮助,但很高兴知道。
Francisc

5
我是否正确地说这会减慢页面的加载速度,因为需要在页面启动加载事件之前下载这些图像?
jakubiszon 2015年

3
我不认为这些将在所有浏览器上都适用。我知道在Chrome上的图片只有在可见之前才会加载。将需要删除显示内容:无;而是尝试放置它们,使它们不可见。如果需要,可以在所有内容加载后使用JS隐藏。
Bullyen

3
元素中的背景图片display: none将不会预加载。
Marquizzo

1
这种方法仅对我有效(1)不使用display: none(2)不使用width/height: 0,(3)放置no-repeat和位置,以使图像位于外面(-<width>px -<height>px将带您到那里,因此,如果图像全部为100px高度以下时,你可以使用0 -100px,换句话说url(...) no-repeat 0 -100px
亚历克西斯威尔克

11

我建议您使用try / catch来防止某些可能的问题:

面向对象:

    var preloadImage = function (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

标准:

    function preloadImage (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

另外,虽然我喜欢DOM,但是旧的愚蠢的浏览器在使用DOM时可能会遇到问题,因此请完全避免使用IMHO,这与Freedev的贡献背道而驰。Image()在旧的垃圾桶浏览器中有更好的支持。


7
我不认为这是在javascript中加载图片时捕获错误的方法-可以使用(_should。?)之类的_img.onerror之类的东西。
Greg0ry 2015年

3
什么是“可能的问题”?
Kissaki

诸如对命令的支持之类的问题。查看网站“我可以使用”以查看您需要支持的浏览器是否支持javascript命令。
戴夫

10

这种方法更加精细。在这里,您将所有预加载的图像存储在一个容器中,可能是div。然后,您可以显示图像或将其在DOM中移动到正确的位置。

function preloadImg(containerId, imgUrl, imageId) {
    var i = document.createElement('img'); // or new Image()
    i.id = imageId;
    i.onload = function() {
         var container = document.getElementById(containerId);
         container.appendChild(this);
    };
    i.src = imgUrl;
}

在这里尝试,我还添加了一些评论


7
const preloadImage = src => 
  new Promise(r => {
    const image = new Image()
    image.onload = r
    image.onerror = r
    image.src = src
  })


// Preload an image
await preloadImage('https://picsum.photos/100/100')

// Preload a bunch of images in parallel 
await Promise.all(images.map(x => preloadImage(x.src)))

5

是的,这将起作用,但是浏览器将限制(在4至8之间)实际调用,因此不会缓存/预加载所有所需的图像。

更好的方法是在使用图像之前调用onload,如下所示:

function (imageUrls, index) {  
    var img = new Image();

    img.onload = function () {
        console.log('isCached: ' + isCached(imageUrls[index]));
        *DoSomething..*

    img.src = imageUrls[index]
}

function isCached(imgUrl) {
    var img = new Image();
    img.src = imgUrl;
    return img.complete || (img .width + img .height) > 0;
}

您能否链接一些有关此浏览器限制的参考?
nulll

很难找到参考(至少我从未找到过),但是我使用自己的项目对此进行了测试,同时仔细查看了使用Chrome开发人员工具(f12)中的Chrome的“网络”标签从缓存和服务器调用中加载的内容。我在Firefox和使用移动设备时发现了相同的行为。
罗宾

1
-1是因为我仔细检查了第一句中的要求,并发现它是错误的,至少在现代浏览器中是如此。请参阅stackoverflow.com/a/56012084/1709587。是的,浏览器愿意发送的并行HTTP请求的最大数量是有限制的,但是即使您按照问题中显示的方式进行操作,它最终也确实可以请求您调用preloadImage的每个URL。
Mark Amery

5

这是我的方法:

var preloadImages = function (srcs, imgs, callback) {
    var img;
    var remaining = srcs.length;
    for (var i = 0; i < srcs.length; i++) {
        img = new Image;
        img.onload = function () {
            --remaining;
            if (remaining <= 0) {
                callback();
            }
        };
        img.src = srcs[i];
        imgs.push(img);
    }
};

4

符合ECMAScript 2017的浏览器解决方案

注意:如果您使用像Babel这样的编译器,这也将起作用。

'use strict';

function imageLoaded(src, alt = '') {
    return new Promise(function(resolve) {
        const image = document.createElement('img');

        image.setAttribute('alt', alt);
        image.setAttribute('src', src);

        image.addEventListener('load', function() {
            resolve(image);
        });
    });
}

async function runExample() {
    console.log("Fetching my cat's image...");

    const myCat = await imageLoaded('https://placekitten.com/500');

    console.log("My cat's image is ready! Now is the time to load my dog's image...");

    const myDog = await imageLoaded('https://placedog.net/500');

    console.log('Whoa! This is now the time to enable my galery.');

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

runExample();

您可能还已经等待所有图像加载。

async function runExample() {
    const [myCat, myDog] = [
        await imageLoaded('https://placekitten.com/500'),
        await imageLoaded('https://placedog.net/500')
    ];

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

或用于Promise.all并行加载它们。

async function runExample() {
    const [myCat, myDog] = await Promise.all([
        imageLoaded('https://placekitten.com/500'),
        imageLoaded('https://placedog.net/500')
    ]);

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

有关承诺的更多信息

有关“异步”功能的更多信息

有关销毁任务的更多信息

有关ECMAScript 2015的更多信息

有关ECMAScript 2017的更多信息


2

我可以确认问题中的方法足以触发图像的下载和缓存(除非您通过响应标头禁止浏览器这样做),至少应在以下条件中进行:

  • 镀铬74
  • Safari 12
  • Firefox 66
  • 边缘17

为了测试这一点,我制作了一个小型Web应用程序,其中包含多个端点,每个端点睡眠10秒钟,然后再提供小猫的照片。然后,我添加了两个网页,其中一个包含一个<script>标签,其中使用问题中的preloadImage函数预加载了每只小猫,而另一个包含了使用的页面上的所有小猫<img>标签。

在上面的所有浏览器中,我发现如果我先访问预加载器页面,等待一会儿,然后转到带有<img>标签的页面,则我的小猫会立即呈现。这表明预加载器已在所有测试的浏览器中成功将小猫加载到缓存中。

您可以在https://github.com/ExplodingCabbage/preloadImage-test上查看或试用我用来测试它的应用程序

特别要注意的是,即使循环的图像数量超过了浏览器一次愿意发出的并行请求的数量,该技术也可以在上面的浏览器中使用,这与罗宾的回答相反。图像预加载的速率当然会受到浏览器愿意发送的并行请求数量的限制,但是最终它将请求您调用的每个图像URL preloadImage()


图像是否缓存在内存或磁盘中是否重要?似乎OP方法被缓存在磁盘上,而不是@clintgh方法被缓存在内存中。
克里斯·L

1

您可以将此代码移动到index.html,以从任何网址预加载图像

<link rel="preload" href="https://via.placeholder.com/160" as="image">

0

使用头部的link标签,浏览器将运行最佳。

export function preloadImages (imageSources: string[]): void {
  imageSources
    .forEach(i => {
      const linkEl = document.createElement('link');
      linkEl.setAttribute('rel', 'preload');
      linkEl.setAttribute('href', i);
      linkEl.setAttribute('as', 'image');
      document.head.appendChild(linkEl);
    });
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.