预加载背景图片

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

【讨论】:

以上是关于预加载背景图片的主要内容,如果未能解决你的问题,请参考以下文章

图片预加载与懒加载

图片懒加载和预加载

nuxt 如何预加载大图片

预加载背景图片

jquery实现图片预加载

关于图片预加载1