JavaScript预加载图像

Fra*_*isc 216 javascript

你能否告诉我,我在下面写的功能是否足以在大多数(如果不是所有)今天常用的浏览器中预装图像?

function preloadImage(url)
{
    var img=new Image();
    img.src=url;
}
Run Code Online (Sandbox Code Playgroud)

我有一个imageURL数组,我循环并调用每个URL的preloadImage函数.

小智 123

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

  • **主持人注意:**请停止将其标记为“不是答案”。实际上,这是对所提问题的直接答案。如果您认为答案是错误的,或者没有足够的证据支持,请*否决*它。 (29认同)

cli*_*tgh 36

试试这个我认为这更好.

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"
)
Run Code Online (Sandbox Code Playgroud)

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

  • 你能解释为什么这更好吗? (10认同)
  • 任何图像都没有`onload`处理程序 (3认同)
  • 如果使用此解决方案,请不要忘记i变量的var语句.否则它将被全局设置,这可能导致真正难以解决的错误(除非你知道你忘记了var语句.`for(var i = 0 .....` (3认同)
  • @BeNice我认为你是误解,加载图像是异步的,因此你必须处理`onload`状态或你只是在内存中实例化图像. (2认同)
  • @Mohammer感谢你,我刚从我提供的链接中复制了代码.我现在将编辑代码以添加`var` (2认同)

Ale*_*der 19

在我的例子中,为onload事件的函数添加一个回调是很有用的:

function preloadImage(url, callback)
{
    var img=new Image();
    img.src=url;
    img.onload = callback;
}
Run Code Online (Sandbox Code Playgroud)

然后将它包装为一个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');
});
Run Code Online (Sandbox Code Playgroud)


mpl*_*jan 18

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; 
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我是说对,这会减慢页面的加载速度,因为在页面启动加载事件之前需要下载这些图像吗? (5认同)
  • 我不相信这些适用于所有浏览器.我知道Chrome上的图片在可见之前不会加载.将需要删除display:none; 而是尝试定位它们,使它们无法被看见.如果需要,可以在所有内容加载后用JS隐藏. (3认同)
  • 使用`display:none`的元素中的背景图像不会预加载. (3认同)

dav*_*ler 16

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)))
Run Code Online (Sandbox Code Playgroud)


小智 12

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

<link rel="preload" href="https://via.placeholder.com/160" as="image">
Run Code Online (Sandbox Code Playgroud)

  • 那么OP问,如何在JS中预加载图像。这不是JS。 (3认同)
  • @KhomNazid 从该标签加载图像**不会**阻止渲染。您可以通过打开网络选项卡、设置限制并查看瀑布图来查看这一点。预加载的图像开始,但页面的其余部分在下载时呈现。 (2认同)

Dav*_*ave 11

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

OOP:

    var preloadImage = function (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }
Run Code Online (Sandbox Code Playgroud)

标准:

    function preloadImage (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }
Run Code Online (Sandbox Code Playgroud)

此外,虽然我喜欢DOM,但是旧的愚蠢的浏览器可能会在使用DOM时出现问题,因此完全避免使用IMHO与freedev的贡献相反.Image()在旧的垃圾浏览器中有更好的支持.

  • 我不认为这是在javascript中加载Image时如何捕获错误 - 有一些像_img.onerror可以(应该?)使用. (7认同)
  • 什么是"可能的问题"? (3认同)

fre*_*dev 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;
}
Run Code Online (Sandbox Code Playgroud)

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


Wik*_*tor 9

HTML Living Standard 现在支持“预加载”

根据W3C HTML 规范,您现在可以使用 JavaScript 进行预加载,如下所示:

var link = document.createElement("link");
link.rel = "preload";
link.as = "image";
link.href = "https://example.com/image.png";
document.head.appendChild(link);
Run Code Online (Sandbox Code Playgroud)

  • 我收到一条“资源已使用链接预加载进行预加载,但在窗口加载事件后几秒钟内未使用”。请确保它具有适当的“as”值并且是有意预加载的。使用此方法时出错:(预加载也不起作用 (2认同)

Ami*_*IRI 8

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();
Run Code Online (Sandbox Code Playgroud)

您也可以等待所有图像加载完毕。

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);
}
Run Code Online (Sandbox Code Playgroud)

或用于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);
}
Run Code Online (Sandbox Code Playgroud)

更多关于 Promise

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

更多关于解构赋值

更多关于 ECMAScript 2015 的信息

更多关于 ECMAScript 2017 的信息


Nan*_*noo 7

截至 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);
}
Run Code Online (Sandbox Code Playgroud)

用法:

preloadImage("example.com/yourimage.png");
Run Code Online (Sandbox Code Playgroud)

显然<cache>不是“已定义”元素,因此您可以根据需要使用 a <div>

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

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

cache image {
    position: absolute;
}
Run Code Online (Sandbox Code Playgroud)

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

笔记:

  • 不要应用于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"
);
Run Code Online (Sandbox Code Playgroud)

预览:

在此处输入图片说明

笔记:

  • 尽量不要同时预加载太多图像(这可能会导致严重的性能问题) - 我通过隐藏图像来解决这个问题,我知道在某些事件中这些图像是不可见的。然后,当然,在我需要的时候再给他们看。


Rob*_*bin 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 您能否链接一些有关此浏览器限制的参考资料? (2认同)
  • -1 因为我仔细测试了这里第一句话中的声明,发现它是错误的,至少在现代浏览器中是这样。请参阅 stackoverflow.com/a/56012084/1709587。是的,浏览器愿意发送的最大并行 HTTP 请求数是有限制的,但它最终会请求您调用 preloadImage 的每个 URL,即使您按照问题中所示的方式进行操作。 (2认同)

nae*_*luh 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);
    }
};
Run Code Online (Sandbox Code Playgroud)