【问题标题】: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);

【问题讨论】:

    标签: javascript css sass


    【解决方案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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-16
          • 1970-01-01
          • 2014-04-05
          相关资源
          最近更新 更多