使用JavaScript预加载图像

时间:2010-09-05 12:25:12

标签: javascript

我在下面写的功能是否足以在今天常用的大多数(如果不是全部)浏览器中预加载图像?

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

我有一个图像网址数组,我循环并为每个网址调用preloadImage函数。

16 个答案:

答案 0 :(得分:109)

是。这适用于所有主流浏览器。

答案 1 :(得分:37)

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

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/

答案 2 :(得分: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 Dam)

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

注意:display:none容器中的图像可能无法预加载。 也许可见性:隐藏会更好地工作,但我没有测试过。感谢Marco Del Valle指出这一点

答案 3 :(得分:15)

在我的情况下,为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');
});

答案 4 :(得分: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;
}

Try it here,我还添加了一些评论

答案 5 :(得分:10)

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

OOP:

    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()在旧的垃圾浏览器中有更好的支持。

答案 6 :(得分:10)

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

答案 7 :(得分: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;
}

答案 8 :(得分:4)

这是我的方法:

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

答案 9 :(得分:4)

截至2020年的工作解决方案

此帖子上的大多数答案不再起作用-(至少在Firefox上)

这是我的解决方法:

var cache = document.createElement("CACHE");
cache.style = "position:absolute;z-index:-1000;opacity:0;";
document.body.appendChild(cache);
function preloadImage(url) {
    var img = new Image();
    img.src = url;
    img.style = "position:absolute";
    cache.appendChild(img);
}

用法:

preloadImage("example.com/yourimage.png");

很显然,<cache>不是“已定义”元素,因此,如果需要,可以使用<div>

在CSS中使用它,而不是应用style属性:

cache {
    position: absolute;
    z-index: -1000;
    opacity: 0;
}

cache image {
    position: absolute;
}

如果您对此进行了测试,请发表评论。

注释:

  • 请勿将display: none;应用于缓存-这不会加载 图片。
  • 请勿调整图片元素的大小,因为这也会影响 初次使用时已加载图像的质量。
  • 为图像设置position: absolute是必要的,因为图像元素最终将使其脱离视口-导致它们无法加载并影响性能。

更新

虽然上述解决方案有效,但是我做了一个小小的更新,以使其结构良好:

(现在也可以在一个功能中接受多个图像)

var cache = document.createElement("CACHE");
document.body.appendChild(cache);
function preloadImage() {
    for (var i=0; i<arguments.length; i++) {
        var img = new Image();
        img.src = arguments[i];
        var parent = arguments[i].split("/")[1]; // Set to index of folder name
        if ($(`cache #${parent}`).length == 0) {
            var ele = document.createElement("DIV");
            ele.id = parent;
            cache.appendChild(ele);
        }
        $(`cache #${parent}`)[0].appendChild(img);
        console.log(parent);
    }
}

preloadImage(
    "assets/office/58.png",
    "assets/leftbutton/124.png",
    "assets/leftbutton/125.png",
    "assets/leftbutton/130.png",
    "assets/leftbutton/122.png",
    "assets/leftbutton/124.png"
);

预览:

enter image description here

注释:

  • 尽量不要同时保留太多图像(这可能会导致严重的性能问题)-我通过隐藏图像来解决此问题,我知道在某些事件中该图像将不可见。然后,当然,在需要时再次显示它们。

答案 10 :(得分:3)

您可以将此代码移至index.html以从任何url预加载图像

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

答案 11 :(得分:1)

根据 w3 规范,您现在可以像这样使用 javascript 预加载:

var res = document.createElement("link");
res.rel = "preload";
res.as = "image";
res.href = "https://example.com/image.png";
document.head.appendChild(res);

https://www.w3.org/TR/preload/#introduction

答案 12 :(得分:0)

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

  • Chrome 74
  • Safari 12
  • Firefox 66
  • 第17边缘

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

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

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

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

答案 13 :(得分:0)

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

More about Promises

More about "Async" functions

More about the destructuring assignment

More about ECMAScript 2015

More about ECMAScript 2017

答案 14 :(得分: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);
    });
}

答案 15 :(得分:0)

这是原始答案,但具有更现代的 ES 语法:

let preloadedImages = [];
export function preloadImages(urls) {
    preloadedImages = urls.map(url => {
        let img = new Image();
        img.src = url;
        img.onload = () => console.log(`image url [${url}] has been loaded successfully`);
        
        return img;
    });
}