预加载背景图片 [英] Preloading background images

查看:100
本文介绍了预加载背景图片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个循环遍历3个不同背景的页面,每750毫秒更换一次。要做到这一点,我将使用相关的背景图像向主体添加一个类,并使用JS进行更改。对于第一个循环,它们会闪烁,因为图像必须加载,所以它不会立即出现。因此,有什么方法可以用来预加载图像吗?

I'm building a page that loops through 3 different backgrounds, changing every 750ms. To do this, I'm adding a class to the body with the relevent background image, changing with JS. For the first loop through, they flash because the image has to load, so it's not there instantly. Therefore is there any methods I can use to preload the images?

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


推荐答案

我会做这样的事情。 loadImages 返回一个Promise,它将在加载所有图像后解析。附加到它的 .then 调用 cycleImages ,它会启动间隔。既然你需要JS中的URL来进行预加载,而不是类切换我直接操作 background-image ,这样你就可以删除来自CSS的图像URL并保存一些冗余字节。这也使得将来更容易扩展图像列表,你只需要在图像数组中添加一个项目,而不是维护一个复杂的if语句。

I would do something like this. loadImages returns a Promise that will resolve once all of the images are loaded. The .then attached to it calls cycleImages, which starts up the interval. Since you will need the URLs in the JS anyway to do the pre-loading, instead of class switching I'm directly manipulating the background-image, that way you can remove the image URLs from the CSS and save a few redundant bytes. This also makes it easier to expand the list of images in the future too, you only need to add an item to the array of images instead of maintaining a complicated if statement.

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>

修改:
考虑到仅仅是学生的评论,这里只有一个版本,只会切换图像已加载,但如果已加载,请立即执行。它还会跳过无法加载的图像。由于不再通过 .then 调用 cycleImages ,我也更改了它以便接受目标元素(作为jQuery)对象)以及一系列图像承诺。这样,如果您愿意,可以在具有不同图像集的页面上的多个位置轻松使用它。

Taking Just a student's comments into account, here is a version that will only switch the image once it is loaded, but do it right away if it is loaded. It also skips images that failed to load. Since cycleImages is no longer called via .then, I also changed it so it accepts a target element (as a jQuery object) along with an array of images promises. That way you can easily use this on multiple places on a page with different images sets if you wanted to.

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

Instead of hard-coding the interval between images changes you could also pass that in as a parameter. At that point though, I would refactor it to use a config object to pass everything but the image promise array: cycleImages(myImages, {target: $('#backgrounds'), interval: 1000});

这篇关于预加载背景图片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆