【问题标题】:Image preloading techniques (for use as hover state CSS background images) don't seem to work on Chrome图像预加载技术(用作悬停状态 CSS 背景图像)似乎不适用于 Chrome
【发布时间】:2017-08-22 22:10:50
【问题描述】:

我正在使用以下技术来预加载悬停按钮时用作 CSS 背景图像的图像:

#preload_area {
  background-image: 
    url(../images/image1.svg),
    url(../images/image2.svg);
  width: 0px;
  height: 0px;
  display: inline;
}

还尝试通过这种方式仅预加载一张图片:

#preload_area {
  background: url(../images/image1.svg) -9999px -9999px no-repeat;
}

这些都不起作用:硬刷新后,第一次悬停按钮时,我仍然看到闪烁(对应于加载悬停图像)。显然在第一次之后就不再眨眼了。

为什么不能在 Chrome 上运行? (它确实适用于 Firefox)

【问题讨论】:

  • 第一个代码示例没有理由不起作用。第二个应该只有background 而不是background-image - 请参阅developer.mozilla.org/en-US/docs/Web/CSS/…
  • 嗯,它没有。我在 Windows 10 上使用 Chrome 57.0.2987.110(64 位)。感谢您的评论,我已纠正(尽管仍然无法在 Chrome 上运行)。在 Firefox 上运行良好。
  • @drake035 我认为 Chrome 实现了某种 DOM 虚拟化,其中仅呈现可见元素,所以我不确定您是否能够解决此问题,除非您将图像放入预加载时的可见区域,这当然是不希望的。
  • 改用精灵并通过背景位置进行切换...
  • 第一个 sn-p 代码在 Chrome 上适用于我......也许如果你发布你的整个代码我们可以知道问题是什么

标签: css google-chrome preloading


【解决方案1】:

我最近在我的博客http://anggit.com 中将它用于“返回顶部”按钮。 它对你有用吗?

CSS:

<style>
#to_top {
    display: block;
    width: 48px;
    height: 48px;
    overflow: hidden;
}
#to_top img { /* in case the actual image size is over 48px */
    width: 48px;
    height: 48px;
}
#to_top:hover #image-1 { /* hover will remove the 1st image, the 2nd image will appear */
    display: none;
}
</style>

HTML:

<a id="to_top" href="#">
    <img id="image-1" src="image48x48.png" alt="Top" />
    <img id="image-2" src="image48x48-hover.png" alt="Top" />
</a>

【讨论】:

    【解决方案2】:

    如果您不想要加载间隙,您可以使用精灵图像,或者您可以将背景图像设置为 base64 编码图像。在这种情况下,总是在加载 css 文件时加载图像。

    .myImg {
        background-image: url(data:image/svg+xml;base64,PD9...);
    }
    

    在这里您可以将您的 svg 图片转换为 base64:http://b64.io

    【讨论】:

      【解决方案3】:

      恕我直言,这不是预加载。它只是在加载,当您悬停按钮时,您使用了一个技巧来显示正确的图像。

      如果您真的想预加载,或者,据我了解您的需要,“当您尝试悬停按钮时,您希望图像已经存在”,那么您有不同的选择:

      • 预取:

      <link rel="prefetch" href="image1.svg"> <link rel="prefetch" href="image2.svg">

      为此添加的一个好处是“链接预取没有同源限制”。

      • 预加载:

      <link rel="preload" href="image1.svg"> <link rel="preload" href="image2.svg">

      使用“预加载”,必须下载资源,而预取并非总是如此。

      Chrome、Opera、Android 浏览器等支持预加载,但不支持 Firefox 和其他浏览器。更多详情here

      css-tricks.com 上更深入地描述了这些技术

      希望对你有所帮助。

      【讨论】:

        【解决方案4】:

        用 chrome 进行了测试,它接缝图像已加载。眨眼是由于放置我认为的图像。为了更好地理解,请看一下这个测试。

        A test with a very big image

        div#preload_area::before {
         content: " ";
         background: url(http://gfsnt.no/oen/foto/Haegefjell_Jan_2013_Large.jpg) no-repeat;
        }
        
        div#preload_area {
          width: 50%;
          height:100vh;
        }
        
        div#preload_area:hover {
           background: url(http://gfsnt.no/oen/foto/Haegefjell_Jan_2013_Large.jpg);
           background-repeat:no-repeat;
           background-size:100% auto;
        }
        

        【讨论】:

          【解决方案5】:

          现场演示: http://blackmiaool.com/soa/43093224/

          没有人承诺会加载不可见的图像。浏览器有权不预加载您的不可见图像,因此您问题中的 css 方法可能在某些浏览器中不起作用。上面的demo是我自己写的。它实际上是在屏幕上渲染图像以保证图像被加载。

          HTML:

          <!DOCTYPE html>
          <html>
          
          <head>
              <meta charset="utf-8" />
              <title>test</title>
          
          </head>
          
          <body>
              <h1 style="color:white;text-align:center;">Try to hover</h1>
              <div class="show-area"></div>
              <link rel="stylesheet" href="style.css">
          
              <script src="../js/jquery.min.js"></script>
              <script>
                  function preloadImage(src, cb) {
                      //create a image
                      const $dom = $("<img />", {
                          src: src
                      });
          
                      //check whether the image is already loaded
                      if ($dom[0].naturalWidth) {
                          cb && cb();
                          return;
                      }
          
                      //Put the image at the left bottom of the screen, and set its opacity to 0.01 to keep it from people eyes. 
                      //Since it's actually rendered on the screen, the browser must load the image
                      $dom.css({
                          opacity: 0.01,
                          position: 'fixed',
                          bottom: 0,
                          left: 0,
                          height: 1,
                          width: 1,
                          'z-index': 10000,
                          'pointer-events': 'none',
                      });
          
                      $(document.body).append($dom);
          
                      //listen its `load` event to remove it and invoke callback
                      $dom.on("load", function() {
                          $dom.remove();
                          cb && cb();
                      });
                  }
          
                  //try to get the urls in the css file, and preload them
                  $("link").each(function() {
                      const href = $(this).attr("href");
                      $.get(href, function(style) {
                          const urls = [];
                          let match = style.match(/url\([\s\S]+?\)/g);
          
                          match.forEach(function(str) {
                              str = str.replace(/\s/g, "")
                                  .replace(/^url\(/, "")
                                  .replace(/\)$/, "");
                              let url = str.match(/^["']?([\S]+?)["']?$/);
                              if (!url || !url[1]) {
                                  console.warn("Can't find url of " + str);
                              } else {
                                  url = url[1];
                                  preloadImage(url);
                              }
                          });
                      });
                  });
              </script>
          
          </body>
          
          </html>
          

          css:

          body{
              margin: 0;
              background-color: black;
          }
          .show-area {    
              height: 100px;  
              width: 300px;
              margin: auto;
              margin-top: 100px;
              background: url( ./1.jpg) no-repeat center;
              background-size: contain;
          }
          
          .show-area:hover {
              background-image: url("./2.jpg ");
          }
          

          【讨论】:

          • @drake035 我更新了答案,为你添加了一些 cmets。
          • @drake035 我更新了答案以使其支持您问题中的 sn-p。
          【解决方案6】:

          在 CSS 中预加载实际上并不意味着文件在其他所有内容之前加载,它只是意味着它是排队等待从 CSS 文件下载的第一个资源。

          这意味着您的 HTML 已经从服务器中检索到,并且可能已经在 CSS 之前排队或下载了其他资源。在所有 HTML 内容之后加载 CSS 预加载图像的情况并不少见。

          虽然图像在队列中比 CSS 中引用的其他资源更早,但这并不意味着它会在其他资源之前返回。如果文件的大小大于其他正在排队的文件,则下载时间可能比同时下载的其他文件要长。

          查看 Chrome 发生情况的一种方法是转到您的网页并导航到 Chrome Devtools 中的“网络”选项卡,然后刷新页面。它将向您显示每个项目何时加载以及从服务器接收该项目需要多长时间的详细信息。

          根据您要加载的图像和您的用例,还有其他几个选项。

          1) 如果文件很大并且下载时间过长,请弄清楚如何减小文件大小。

          2) 如果您可以控制用户正在从中导航的页面,则可以预取图像以用于前一页面中的缓存。

          3) 除了 CSS 之外,您还可以尝试使用 HTML 预加载。我相信目前只有 Chrome 支持 HTML 预加载,因此它可能非常适合这种情况。将以下内容添加到您的 html 的头部。

          <link rel="preload" as="image" href="image1.svg">
          <link rel="preload" as="image" href="image2.svg">
          

          【讨论】:

          • 谢谢。在 DevTools 中,无论我等待多长时间,只有当我实际悬停链接时,才会在列表末尾加载悬停图像。预加载根本不起作用。
          【解决方案7】:

          为什么它不能在 Chrome 上运行?因为所有浏览器供应商都想要最快的浏览器。他们不会加载不必要的资产。

          您想要一种跨浏览器的预加载方式吗?按照 [CBroe] 的建议,使用精灵。这个解决方案已经存在了很长时间并且坚如磐石。任何其他技巧,使图像不可见,今天可以工作,但明天就会被打破。

          【讨论】:

            猜你喜欢
            • 2013-01-19
            • 2011-11-03
            • 2013-11-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多