用JavaScript预加载图像

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

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

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

262177 次浏览

是的。这应该可以在所有主流浏览器上运行。

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指出这一点

这种方法稍微复杂一些。在这里,你将所有预加载的图像存储在一个容器中,可能是一个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/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时遇到问题,所以恕我直言,与freedev的贡献相反,完全避免使用DOM。Image()在旧的垃圾浏览器中有更好的支持。

试试这个,我觉得这个更好。

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/

是的,这将工作,然而浏览器将限制(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;
}

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

function preloadImage(url, callback)
{
var img=new Image();
img.src=url;
img.onload = callback;
}
然后包装它的情况下,一个url数组的图像将被回调预加载在all是完成的: https://jsfiddle.net/4r0Luoy7/ < / p >
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');
});

以下是我的方法:

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

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

  • 铬74
  • Safari 12
  • 火狐66
  • 边缘17

为了测试这一点,我做了一个小的网络应用程序,它有几个端点,每个端点都休眠10秒钟,然后提供一张小猫的照片。然后我添加了两个网页,其中一个包含<script>标签,其中每个小猫都是使用问题中的preloadImage函数预加载的,而其中的其他包含页面上使用<img>标签的所有小猫。

在上面所有的浏览器中,我发现如果我先访问预加载器页面,等待一段时间,然后进入带有<img>标记的页面,我的小猫立即呈现。这表明预加载器在所有测试的浏览器中成功地将小猫加载到缓存中。

你可以在https://github.com/ExplodingCabbage/preloadImage-test中看到或尝试我用来测试这个的应用程序。

特别要注意的是,这种技术在上面的浏览器中仍然有效,即使循环的图像数量超过了浏览器一次愿意进行的并行请求的数量,这与罗宾的回答所建议的相反。你的图像预加载的速率当然会受到浏览器愿意发送多少并行请求的限制,但它最终会请求你调用preloadImage()的每个图像URL。

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

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

<link rel="preload" href="https://via.placeholder.com/160" as="image">
const preloadImage = src =>
new Promise((resolve, reject) => {
const image = new Image()
image.onload = resolve
image.onerror = reject
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)))

浏览器最好使用头部的链接标签。

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

截至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

注:

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

这是最初的答案,但使用了更现代的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;
});
}

HTML生活标准现在支持“预加载”

根据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);

对于感兴趣的人,这里有一些OP提供的代码的替代方案。

preloadImage()

函数现在返回

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

v1:通过将图像作为参数传递给preloadImages()来预加载

返回函数返回的Image类型对象数组。有助于检查预紧状态。

jsFiddle

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


function preloadImages() {
const images = []
for (var i = 0; i < arguments.length; i++) {
images[i] = preloadImage(arguments[i])
}
return images
}


//-- usage --//
const images = preloadImages(
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
)

v2:通过将图像作为数组传递给preloadImages()来预加载

类型不安全Image类型对象覆盖提供的数组。返回函数返回的Image类型对象数组。

.

.

jsFiddle

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


function preloadImages(images) {
for (var i = 0; i < images.length; i++) {
images[i] = preloadImage(images[i])
}
return images
}


//-- usage --//
let arr = [
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
]


const images = preloadImages(arr)
console.dir(images)

v3:通过传递要么数组和/或参数(s)到preloadImages()来预加载

类型安全。返回函数返回的Image类型对象的数组。有助于检查预紧状态。

jsFiddle

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


function preloadImages() {
const images = []
let c = 0
for (var i = 0; i < arguments.length; i++) {
if (Array.isArray(arguments[i])) {
for(var arr = 0; arr < arguments[i].length; arr++) {
if(typeof arguments[i][arr] == 'string') {
images[c] = preloadImage(arguments[i][arr])
c++
}
}
}
else if(typeof arguments[i] == 'string') {
images[c] = preloadImage(arguments[i])
c++
}
}
return images
}


//-- usage --//
var arr = [
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg"
]


const images = preloadImages(
arr,
"http://domain.tld/gallery/image-003.jpg",
"http://domain.tld/gallery/image-004.jpg",
[
"http://domain.tld/gallery/image-005.jpg",
"http://domain.tld/gallery/image-006.jpg"
]
)


console.dir(images)

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

这就是我所做的,用承诺:


const listOfimages = [
{
title: "something",
img: "https://www.somewhere.com/assets/images/someimage.jpeg"
},
{
title: "something else",
img: "https://www.somewhere.com/assets/images/someotherimage.jpeg"
}
];


const preload = async () => {
await Promise.all(
listOfimages.map(
(a) =>
new Promise((res) => {
const preloadImage = new Image();
preloadImage.onload = res;
preloadImage.src = a.img;
})
)
);
}