2017-03-24 212 views
5

我正在建立一个循环遍历3个不同背景的页面,每750ms更改一次。为了做到这一点,我添加了一个类与相关的背景图片,改变与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); 

回答

4

我会做这样的事情。 loadImages返回一个Promise,一旦加载所有图像,该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的意见考虑进去,这里是一个版本,一旦它被加载,将只切换图像,但这样做是正确的路程,如果它被加载。它也会跳过加载失败的图像。由于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});

+0

一方面,您可能想考虑将间隔更改为每隔几秒而不是每隔750毫秒。大多数人会因为这种快速闪烁的事情而感到非常烦恼。作为一名注意力不集中的人,我可以告诉你,页面上快速闪烁的内容使人们很难专注于阅读页面上的任何内容;这太让人分心了。即使没有ADD的人也会因分心而受损。 –

+0

很好的答案!一个建议:目前,所有图像都在显示第一个图像之前加载。它可能是一个很好的替代解决方案,只在需要时才加载映像,并简单地延迟交换机,直到Promise解决。这意味着对于第一个循环,您还不能使用间隔,而是需要确保(a)750毫秒(或任何您设置的)已经通过,并且(b)下一个图像被加载。事实上,Promise也可以很好地完成。 –

+0

哦,现在的解决方案的另一个缺点是即使只有一个不能被访问,也不会显示背景图像。 –

0

有趣的想法。

你能潜在地在后台加载它们这么喜欢:

<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 
} 
1

您可以使用javascript加载它们。如:

(new Image()).src = url1; 
(new Image()).src = url2; 
(new Image()).src = url3; 

更改“url1”,“url2”,“url3”与你自己的图像网址。浏览器将加载图像,但它们不会在任何地方可见。

0

你也可以用不同的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); 
}); 

也许你可以添加过渡/动画,使它看起来淡入和淡出。

样品下面codepen:

http://codepen.io/adrianrios/pen/yMEpEv