Chrome浏览器似乎无法使用图像预加载技术(用作悬停状态的CSS背景图像)。

8
我正在使用以下技术,在鼠标悬停时预加载应用为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。请参阅 https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties#Background_Properties - helb
好的,它并没有。我正在使用Windows 10上的Chrome 57.0.2987.110(64位)。感谢您的评论,我已经纠正了(但在Chrome上仍然无法工作)。在Firefox上运行良好。 - drake035
1
@drake035 我认为Chrome实现了某种DOM虚拟化,只有可见元素才会被渲染,所以我不确定你是否能够解决这个问题,除非你在预加载时将图像放在可见区域,但这当然是不希望的。 - Konstantin Dinev
2
使用精灵图代替,并通过 background-position 进行切换... - CBroe
第一段代码在Chrome上对我有效...如果您发布整个代码,我们可能会知道问题出在哪里。 - Pato Salazar
7个回答

0

我最近在我的博客中使用了这个“返回顶部”按钮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>

0

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

没有人保证不可见的图片会被加载。浏览器有权利选择不预加载您的不可见图片,因此您问题中的 CSS 方法可能在某些浏览器中无效。上面的演示是我自己编写的。它实际上在屏幕上呈现图像,以确保图像已加载。

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 我更新了答案并为您添加了一些注释。 - blackmiaool
@drake035 我更新了答案,使其支持你问题中的代码片段。 - blackmiaool

0
为什么在Chrome上不起作用?因为所有浏览器供应商都希望拥有最快的浏览器。他们不会加载不必要的资源。
你想要一个跨浏览器的预加载方法吗?像[CBroe]建议的那样使用精灵。这个解决方案已经存在很久,非常可靠。其他任何技巧,比如将图片渲染为不可见,今天可能可以工作,但明天可能会失效。

0

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

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

  • 预取:

<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和其他浏览器不支持。更多细节请参阅此处

这些技术在css-tricks.com上有更深入的描述。

希望这能帮到你。


0

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

这意味着您的HTML已经从服务器检索到,并且可能已经排队或下载了其他资源,然后才是CSS。CSS预加载图像在所有HTML内容加载完成后加载也很常见。

现在,虽然该图像将位于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 中,无论我等待多久,当我实际悬停在链接上时,悬停图像仅在列表末尾加载。预加载根本不起作用。 - drake035

0

使用 Chrome 进行测试,似乎图像已经加载。我认为闪烁是因为放置图像的位置不对。为了更好地理解,请查看这个测试。

一个带有非常大图片的测试

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

0

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

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

在这里,您可以将SVG图像转换为Base64:http://b64.io


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接