预加载背景图片
Posted
技术标签:
【中文标题】预加载背景图片【英文标题】:Preloading background images 【发布时间】:2017-08-17 07:44:36 【问题描述】:我正在构建一个循环通过 3 个不同背景的页面,每 750 毫秒更改一次。为此,我在正文中添加了一个带有相关背景图像的类,并随着 JS 的变化而变化。对于第一个循环,它们会闪烁,因为图像必须加载,所以它不会立即出现。因此有什么方法可以用来预加载图像吗?
CSS:
&.backgrounds
background-position: center bottom;
background-repeat: no-repeat;
background-size: 130%;
&.freddy
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_freddy.jpg);
&.irene
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_irene.jpg);
&.joe
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_joe.jpg);
JS:
setInterval(function()
if ( $('.backgrounds').hasClass('freddy') )
$('.backgrounds').removeClass('freddy').addClass('irene');
else if ( $('.backgrounds').hasClass('irene') )
$('.backgrounds').removeClass('irene').addClass('joe');
else if ( $('.backgrounds').hasClass('joe') )
$('.backgrounds').removeClass('joe').addClass('freddy');
, 750);
【问题讨论】:
【参考方案1】:我会做这样的事情。 loadImages
返回一个 Promise,一旦加载了所有图像,它将解析。附加到它的.then
调用cycleImages
,它启动了间隔。由于无论如何您都需要 JS 中的 URL 来进行预加载,而不是类切换,我直接操作 background-image
,这样您就可以从 CSS 中删除图像 URL 并节省一些冗余字节。这也使得将来扩展图像列表也变得更加容易,您只需向图像数组中添加一个项目,而不是维护复杂的 if 语句。
function loadImages (images)
// each image will be loaded by this function.
// it returns a Promise that will resolve once
// the image has finished loading
let loader = function (src)
return new Promise(function (resolve, reject)
let img = new Image();
img.onload = function ()
// resolve the promise with our url so it is
// returned in the result of Promise.all
resolve(src);
;
img.onerror = function (err)
reject(err);
;
img.src = src;
);
;
// create an image loader for each url
let loaders = [];
images.forEach(function (image)
loaders.push(loader(image));
);
// Promise.all will return a promise that will resolve once all of of our
// image loader promises resolve
return Promise.all(loaders);
// the images we are going to display
let myImages = [
'http://www.gifpng.com/400x200',
'http://www.gifpng.com/400x200/ffffff/000000',
'http://www.gifpng.com/400x200/000000/ffffff'
];
// $(document).ready(fn) is deprecated,
// use the $(fn) form instead
$(function()
// after the images are loaded this will be called with an array of the loaded images
function cycleImages (images)
let index = 0;
setInterval(function()
// since we need an array of the image names to preload them anyway,
// just load them via JS instead of class switching so you can cut them
// out of the CSS and save some space by not being redundant
$('#backgrounds').css('backgroundImage', 'url("' + images[index] + '")');
// increment, roll over to 0 if at length after increment
index = (index + 1) % images.length;
, 750);
// load the images and start cycling through them after they are loaded
loadImages(myImages).then(cycleImages).catch(function (err)
console.error(err);
);
);
#backgrounds
height: 200px;
width: 400px;
border: 1px solid #000;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="backgrounds"></div>
编辑:
考虑到Just a student的cmets,这里有一个版本,它只会在加载后切换图像,但如果加载则立即切换。它还会跳过无法加载的图像。由于cycleImages
不再通过.then
调用,我还对其进行了更改,以便它接受一个目标元素(作为一个jQuery 对象)以及一组图像承诺。这样,如果您愿意,您可以轻松地在具有不同图像集的页面上的多个位置使用它。
function loadImages (images)
// each image will be loaded by this function.
// it returns a Promise that will resolve once
// the image has finished loading
let loader = function (src)
return new Promise(function (resolve, reject)
let img = new Image();
img.onload = function ()
// resolve the promise with our url
resolve(src);
;
img.onerror = function (err)
reject(err);
;
img.src = src;
);
;
// return an array of image-loading promises
return images.map(function (image)
return loader(image);
);
// the images we are going to display
let myImages = [
'http://www.gifpng.com/400x200',
'http://www.invalid-domain-name.foo/this-url-certainly-does-not-exist.jpg',
'http://www.gifpng.com/400x200/ffffff/000000',
'http://www.gifpng.com/400x200/000000/ffffff'
];
// $(document).ready(fn) is deprecated,
// use the $(fn) form instead
$(function()
// this receives an array of the promises for each image
function cycleImages ($target, images)
let index = 0,
interval = 750; // how many ms to wait before attempting to switch images
function nextImage ()
// p is the promise for the current image
let p = images[index],
next = function (wait)
// increment our counter and wait to display the next one
index = (index + 1) % images.length;
setTimeout(nextImage, wait);
;
// wait for this image to load or fail to load
p.then(function (src)
// it loaded, display it
$target.css('backgroundImage', 'url("' + src + '")');
next(interval);
).catch(function (err)
// this one failed to load, skip it
next(0);
);
// start cycling
nextImage();
// load the images and start cycling through them as they are loaded
cycleImages($('#backgrounds'), loadImages(myImages));
);
#backgrounds
height: 200px;
width: 400px;
border: 1px solid #000;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="backgrounds"></div>
您也可以将其作为参数传入,而不是对图像更改之间的间隔进行硬编码。不过到那时,我会重构它以使用配置对象来传递除图像承诺数组之外的所有内容:cycleImages(myImages, target: $('#backgrounds'), interval: 1000);
【讨论】:
附带说明,您可能需要考虑将间隔更改为每几秒而不是每 750 毫秒。大多数人会对如此快速地闪烁的东西感到非常恼火。作为一个有注意力缺陷障碍的人,我可以告诉你,页面上快速闪烁的东西让你很难集中精力阅读页面上的任何内容。这太让人分心了。即使是没有 ADD 的人也可能会因分心而受损。 很好的答案!一个建议:目前,在显示第一个图像之前加载所有图像。仅在需要时加载图像并简单地延迟切换直到 Promise 解决,这可能是一个不错的替代解决方案。这意味着对于第一个循环,您还不能使用间隔,而是需要确保 (a) 750 毫秒(或您设置的任何时间)都已经过去,并且 (b) 加载了下一个图像。事实上,Promises 也可以很好地做到这一点。 哦,当前解决方案的另一个缺点是,即使只有一个无法访问,也不会显示背景图像。 @Justastudent 我已根据您的建议添加了替代解决方案。感谢您的意见,它帮助我思考如何改进它。我喜欢它让我想出的东西:-)【参考方案2】:您可以在 javascript 中加载它们。喜欢:
(new Image()).src = url1;
(new Image()).src = url2;
(new Image()).src = url3;
用您自己的图片网址更改“url1”、“url2”、“url3”。浏览器将加载图像,但它们不会在任何地方可见。
【讨论】:
【参考方案3】:有趣的想法。
您能否在后台加载它们,例如:
<div class="hidden-images">
<img src="img1.jpg" />
<img src="img2.jpg" />
<img src="img3.jpg" />
</div>
然后在 CSS 中
.hidden-images
position: relative;
z-index: 1;
那么无论你的主容器 div 是什么类型的,在其上添加以下内容?
.main-container
position: relative;
z-index: 2; // Or higher, whatever is needed
【讨论】:
【参考方案4】:你也可以像这样使用不同的 html 和 css 结构来做到这一点。
html
<div class="backgrounds">
<div class="background freddy"></div>
</div>
css
.backgrounds
background-color: transparent;
background-position: center bottom;
background-repeat: no-repeat;
background-size: 130%;
height: 250px;
width; 100%;
.backgrounds .freddy
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/hero.jpg');
.backgrounds .irene
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/projects-blur.png');
.backgrounds .joe
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/contacts-blur.png');
JS
$(document).ready(function()
setInterval(function()
if ( $('.background').hasClass('freddy') )
$('.background').removeClass('freddy').addClass('irene');
else if ( $('.background').hasClass('irene') )
$('.background').removeClass('irene').addClass('joe');
else if ( $('.background').hasClass('joe') )
$('.background').removeClass('joe').addClass('freddy');
, 750);
);
也许您可以添加过渡/动画以使其看起来淡入淡出。
下面的示例代码笔:
http://codepen.io/adrianrios/pen/yMEpEv
【讨论】:
以上是关于预加载背景图片的主要内容,如果未能解决你的问题,请参考以下文章